Two containers start from the same application image.
Both initially read /etc/api/config.yaml and receive the same bytes. Container A then changes the file. Container B continues to see the original version, and starting a third container from the image also produces the original version.
The runtime did not copy the complete image for every container. It constructed a layered file-system view:
Reads can use common image contents. A container's changes go into its own writable layer, leaving the shared image unchanged.
A container root file system is a constructed mount view: immutable image contents form the base, and runtime-specific writable storage and mounts supply mutable state.
On Linux, OverlayFS is a common way to build this view. It is not the only implementation, but its model makes image sharing, first-write behavior, deletion, and container lifecycle concrete.
A virtual machine commonly sees a block device, partitions it, and mounts a file system through its guest kernel.
A container shares the host kernel. The runtime can therefore construct a file-system tree directly from host-managed directories, mounts, and file-system implementations.
The container process sees an ordinary root:
The container's root contains the usual top-level directories: bin, dev, etc, proc, run, tmp, usr, and var.
This tree is a logical view. Its paths can come from several sources:
tmpfs mountsprocfs and sysfsPath lookup crosses those mounts without requiring the application to know which source supplies each component.
The root therefore should not be modeled as one directory copied from an image or one miniature disk. It is a mount tree assembled for the container's process environment.
A container image packages a user-space file-system template and metadata describing how to run it.
In the OCI image model, the main pieces include:
A manifest that identifies the image configuration and an ordered list of filesystem layers.
A configuration object containing metadata such as the default command, environment variables, working directory, root-file-system diff IDs, and build history.
Layer blobs containing filesystem changes. A layer commonly uses a tar-based archive representation, often compressed for distribution.
A layer is a difference, or diff, relative to the state below it. It can add files, replace paths, or record deletions.
Consider an image built conceptually as:
The runtime applies the layers in order to produce one root-file-system view. If multiple layers contain /etc/api/config.yaml, the highest applicable layer supplies the visible version.
Layers are normally identified by cryptographic digests. Identical layer blobs can be downloaded once and reused by several images and containers. The digest also lets a client verify that received bytes match the referenced content.
An image layer archive is a distribution format, not necessarily a directory mounted directly at runtime. A runtime's snapshotter or storage driver unpacks or represents the diffs in a form suited to the host, then constructs the live root view.
Once an image is identified by its content digest, changing one of its layer blobs would produce different content and therefore a different digest.
Runtimes treat stored image layers as immutable. Several containers can safely refer to the same layer because no container is allowed to modify it in place.
This sharing reduces storage and startup work. Starting another container does not require copying every executable and library from the image.
Immutability applies to the image layers, not to every pathname the container sees. The runtime adds writable storage above the image and can mount mutable volumes over selected paths.
Linux OverlayFS combines directory trees into one merged view.
Its central terms are:
A conceptual mount command looks like:
The exact storage paths belong to the runtime and should not be edited manually. Container platforms can also use snapshotters backed by other mechanisms, including Btrfs, ZFS, device-mapper snapshots, or remote snapshot services.
The invariant is more important than one implementation:
When a process opens a pathname in the merged view, OverlayFS searches the layers by precedence.
For a regular file, the upper directory wins if it contains a visible entry for that path. Otherwise the search continues through the lower layers from highest priority to lowest.
Suppose:
The merged view returns the file from the app layer.
Directories require merging. If both an upper and lower layer contain /etc/api, the merged directory can show entries from both, except where upper entries replace or hide lower ones.
The application receives normal file-descriptor and pathname behavior through the Linux VFS interface. It does not issue a special “read from image layer 3” operation.
The lower layers cannot be modified. If a process writes a file that currently exists only in a lower layer, OverlayFS first copies up the file into the upper directory.
After the copy, the write changes the upper version. Later path lookups find that version before the lower one.
The lower file remains unchanged and can still serve other containers.
This is a file-system application of copy-on-write: sharing continues until a write requires one container's view to diverge. The detailed interception mechanism differs from virtual-memory copy-on-write, but the semantic pattern is the same.
A small write may require copying the complete file into the upper layer before modification. It does not copy the complete image layer, but modifying one byte of a large lower file can still create substantial first-write work.
Creating a new file is simpler. Since no lower version exists, OverlayFS creates the entry directly in the upper directory.
Deleting an upper-only file can remove that upper entry normally.
Deleting a file that exists in an immutable lower layer is different. OverlayFS cannot erase the lower content because other containers and images may still need it.
Instead, the upper layer records a whiteout that hides the lower path from the merged view:
Directories can also be marked opaque, telling the merged view not to expose entries from matching lower directories.
Whiteouts are overlay metadata, not zero-byte replacement files visible to the container application. Their on-disk representation depends on the storage implementation.
OCI layer archives encode deletions using reserved whiteout names such as .wh.<name> and use a special opaque-directory marker. A runtime can translate that archive representation into the form expected by its local snapshotter or OverlayFS backing directories.
This distinction matters when inspecting raw layers: the format used to distribute a deletion and the representation used by the mounted overlay can differ even though they produce the same merged result.
Loading simulation...
Higher layers can replace lower entries at the same pathname.
Suppose an image has:
The container sees version 2. Version 1 still occupies bytes in the base layer; it is merely hidden by the higher entry.
This leads to an important image-building rule:
Removing a file in a later image layer does not erase its bytes from an earlier layer.
If a build step copies a credential into one layer and a later step deletes it, the final merged root does not show the credential, but anyone who can inspect the earlier layer may still recover it.
Sensitive build inputs should not be committed into a layer. Build-secret mechanisms, careful multi-stage builds, and a narrow build context avoid placing the secret in image history in the first place.
Layering changes visibility, not the historical contents of immutable lower blobs.
Each container normally receives private writable state above its image.
The layer records changes such as:
Stopping and starting the same container commonly preserves this writable layer. Removing and recreating the container from its image normally creates a fresh writable layer.
The exact lifecycle depends on the runtime and orchestration platform, so application correctness should not rely on an instance layer surviving replacement.
The writable layer is often called ephemeral storage, but ephemeral does not mean memory-only. Its backing commonly resides on a real host file system and can consume disk blocks and inodes. It is ephemeral because its lifecycle is tied to the container instance.
Writing large logs, caches, downloads, or database files into this layer can fill the runtime's storage even when application metrics show adequate memory.
Data that must outlive a container instance should use storage with an explicit independent lifecycle.
Container platforms commonly provide three mount choices.
A bind mount exposes an existing host path at a path inside the container.
the host path /srv/api/config mounted at /etc/api inside the container
Reads and writes go to the host path rather than the container's writable overlay layer.
Bind mounts are direct and easy to understand, but they couple the workload to host directory layout, ownership, permissions, and security policy. Mounting a sensitive host directory writable gives the container authority over that directory's files.
A volume is storage managed separately from the container instance. The runtime or an external storage plugin determines its host location and implementation.
Removing and recreating the container need not remove the volume. Another container can mount it deliberately, and operators can manage its backup or lifecycle independently.
The word volume does not guarantee durability, replication, backup, or high performance. Those properties depend on the volume implementation and operational policy.
tmpfs mountsA tmpfs mount stores file contents in memory-backed kernel storage rather than in the container's disk-backed writable layer.
It is useful for temporary files, runtime sockets, and sensitive short-lived data that should not remain in the container layer. Its memory is still a finite host resource, can be charged to the container's memory cgroup, and may interact with swap according to host policy.
tmpfs contents disappear when the mount is destroyed. It is not persistent storage.
Mounting storage at a container path covers the image content already visible there.
Suppose the image contains:
If a volume is mounted at /var/lib/api, pathname lookup crosses into the volume at that directory. The image's default.db is hidden for as long as the mount is present.
The file has not been deleted from the image layer. A container without that mount can still see it.
This behavior can surprise an application that expects image-provided initialization files beneath a mounted directory. Some runtimes offer volume-initialization behavior, but those semantics are platform-specific and should not be assumed universally.
A reliable design separates immutable application assets from mutable data mount points so mounting persistent storage does not unintentionally hide required image files.
A runtime gives the container process a mount namespace and constructs the desired tree inside it.
A simplified setup flow is:
/proc, /sys, /dev, and other kernel interfaces.The program starts last, by which point its entire view of the filesystem is already fixed.
Linux provides operations such as pivot_root() and chroot() for changing a process's root context. Container runtimes commonly use pivot_root() when possible so the old root can be detached from the container's mount view.
Changing the root alone is not a complete isolation boundary. The mount namespace controls which mounts remain reachable, while permissions and other security mechanisms control what operations the process can perform.
The resulting / is therefore a namespace-specific root, not a claim that the container owns the host's real root file system.
Several familiar paths are usually supplied or modified at runtime.
/proc is commonly a procfs mount reflecting the container's PID view.
/sys exposes a controlled view of kernel and device state and is often mounted with restrictive permissions.
/dev is assembled from selected device nodes and virtual file systems rather than copied blindly from the image.
/dev/shm is commonly a tmpfs mount for POSIX shared memory.
Files such as /etc/hosts, /etc/hostname, and /etc/resolv.conf may be mounted or generated by the runtime to reflect container networking and identity.
The path tree is therefore a composition:
Inspecting a file's pathname alone does not identify which backing source provides it.
Layering does not bypass the Linux permission model.
Files in image layers carry numeric ownership and mode bits. The kernel interprets those values using the credentials and user-namespace mappings of the container process.
A process that appears as UID 0 inside a user namespace may map to an unprivileged host UID. Whether it can write a bind-mounted host file depends on the mapped identity, file ownership, access-control rules, mount flags, and other security policy.
A volume can also contain ownership values that do not match the user configured in a newly deployed container. Changing the image's application UID without migrating the volume can therefore produce permission failures.
Read-only mounts add another enforcement layer. Even when file mode bits would permit a write, a read-only mount rejects it.
The root file system itself can be mounted read-only. Applications that need temporary writable paths can receive specific tmpfs or volume mounts rather than write access across the entire root.
OverlayFS still uses the host kernel's normal file and memory mechanisms.
When several containers read the same lower-layer executable or library, the host can reuse cached file contents rather than keeping an unrelated cached copy for every container. This is one source of container density.
Once a lower file is copied up and changed, the upper version is a different file-system object with its own contents. The old lower version remains available to containers that have not overridden it.
Page-cache behavior, write-back, and durability still depend on the underlying file systems and storage devices. An overlay mount does not turn a successful write into an immediate durability guarantee.
Memory-backed tmpfs follows a different storage path from a disk-backed upper layer but still consumes accounted memory. “Inside the container file system” is not enough information to decide whether a byte occupies page cache, anonymous-like shmem, or persistent storage.
The writable layer consumes capacity on its backing file system. It can fail because the backing store has no free blocks or no free inodes.
A memory cgroup limit does not bound disk bytes in the writable layer. An I/O controller can shape block-device throughput without setting a maximum file-system size.
Platforms can apply storage quotas or size limits, but the mechanism and accounting depend on the storage driver, backing file system, and orchestrator.
The relevant questions are distinct:
Calling all of these “container disk” hides the policy that actually controls each one.
Overlay storage is efficient when containers mostly read common image data and write a modest amount of instance-specific state.
Several patterns add work:
First writes to lower files can trigger copy-up. A tiny change to a large file may require copying the file before applying the write.
Metadata-heavy workloads can pay for lookups and updates across the merged namespace.
Large mutable data sets quickly lose the sharing advantage because their active files live in the upper layer.
Deep or numerous layers can increase lookup and management work, although runtimes and the kernel optimize common cases.
This does not make OverlayFS unsuitable for all writes. It means that a container's writable layer is optimized for instance-local changes, not automatically for every database or high-throughput storage workload.
Persistent databases and other I/O-sensitive services commonly place mutable data on a dedicated volume whose performance, capacity, backup, and durability properties can be managed explicitly.
Docker can report its configured storage driver:
On many native Linux installations, the result is:
Other environments can report a different driver. Docker Desktop commonly runs the storage driver inside its supporting Linux VM rather than directly on the macOS or Windows file system.
Start a demonstration container:
Read and then replace an image-provided file:
Inspect the container's file-system differences:
The output should report /etc/alpine-release as changed, possibly along with its parent directory or runtime-created paths. Docker marks added paths with A, changed paths with C, and deleted paths with D.
Start a fresh container from the same image:
It prints the original image value. The first container's write changed only its writable layer.
Inspect the first container's mounts:
Remove the demonstration container if it is still running:
Docker's internal storage directories are implementation details. Inspect them read-only when diagnosing, and never modify layer or snapshotter files behind a running runtime.
Consider an API image containing:
The executable and default configuration are immutable application assets. Every replica can read them from shared image layers.
At deployment, the platform mounts a configuration source read-only at /etc/api/runtime.yaml, a persistent volume at /var/lib/api, and a tmpfs at /run/api.
The application writes database state to the volume and short-lived sockets to tmpfs. Logs go to standard output rather than growing without bound in the container layer.
If the application edits /etc/api/default.yaml, OverlayFS copies the file into that instance's upper layer. Other replicas still see the image version.
When the replica is replaced, its upper layer disappears. The data volume remains because its lifecycle is separate, while /run/api disappears because it was memory-backed temporary storage.
This design makes the intended lifetimes explicit:
Correct storage design begins by deciding which lifetime each path requires.
A container root file system is assembled from immutable image layers, private writable state, and additional mounts. It is a namespace-specific file-system tree, not a private virtual disk.
OverlayFS commonly combines ordered lower directories with one upper directory and exposes a merged view. Reads can use shared lower files, while the first write to a lower file copies it into the upper layer. Whiteouts and opaque directories hide lower entries without changing immutable image data.
The writable layer belongs to the container instance and should not hold data that must survive replacement. Bind mounts, managed volumes, and tmpfs mounts provide different host coupling, persistence, and storage behavior.
The image, upper layer, runtime mounts, kernel pseudo-file systems, and persistent storage can all supply paths beneath one visible /. The essential question for every writable path is:
Which backing source owns these bytes, and what lifecycle, capacity, performance, and durability guarantees does it provide?
5 quizzes