What Is an Image, Really?
Think of a Docker image like a stack of transparent overhead projector sheets. Each sheet adds something to the picture. The bottom sheet might be a bare Ubuntu filesystem. The next sheet adds the Python runtime. The next adds your application code. When you look down through all the sheets at once, you see one complete, unified picture.
That's exactly how Docker images work. An image is not a single monolithic file — it is an ordered stack of distinct, immutable (read-only) filesystem layers. Every instruction in your Dockerfile that modifies the filesystem (RUN, COPY, ADD) creates a new layer on top of the previous one.
OverlayFS: The Mechanism Behind the Magic
Docker presents these separate layers as one unified filesystem to your application using a Union Filesystem. The modern implementation used by Docker is called OverlayFS, and it is built directly into the Linux kernel.
OverlayFS works by designating two directories:
- lowerdir: The read-only image layers, stacked on top of each other.
- upperdir: A single writable directory — the container layer.
- merged: The unified view that the container process actually sees.
Inspecting a running container's graph driver metadata on a Linux host surfaces this directly, referencing LowerDir, UpperDir, MergedDir, and WorkDir — these are the actual directories on your host filesystem that OverlayFS is combining.
Copy-on-Write (CoW): Efficient Mutation
Because image layers are read-only, what happens when your application tries to modify a file that lives in a lower layer — say, /etc/hosts or a config file?
Docker uses a Copy-on-Write (CoW) strategy:
- The container process requests a write to
/etc/hosts. - OverlayFS detects that
/etc/hostsexists in a lower (read-only) layer. - OverlayFS copies the file up to the writable
upperdir(the container layer). - The write is applied to that copy.
- From the container's perspective, the file was modified normally.
The original file in the image layer is never touched. This has two critical implications:
- Containers start instantly because Docker only creates a thin writable layer — it does not copy the entire image.
- Image layers are shared across containers. If you run 10 containers from the same image, they all share the same read-only layers on disk. Only the thin writable layer per container is unique.
A common interview question is: "If containers share image layers, what happens to data written inside a container when it stops?" The answer is: it is lost. The writable container layer is ephemeral — it is deleted when the container is removed. This is why stateful data must be stored in Docker volumes or bind mounts, which exist outside the container layer entirely. Follow-up: "What's the difference between a volume and a bind mount?" Volumes are managed by Docker (stored in /var/lib/docker/volumes/); bind mounts map a specific host path into the container.
Key Point: Every RUN instruction in a Dockerfile creates a new layer. This means RUN apt-get update and RUN apt-get install -y curl as two separate instructions create two layers. The first layer (containing the package index) is permanently stored in the image even if the second layer deletes it. This is why you should chain related commands: RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*. The cleanup happens in the same layer, so the package cache never gets committed to the image.