A container's filesystem is the image's read-only layers plus one thin that belongs to that container alone.
text
container "app" container "job"
┌──────────────────────┐ ┌──────────────────────┐
│ writable layer (app) │ │ writable layer (job) │ read-write, private
└──────────┬───────────┘ └──────────┬───────────┘
┌──────────┴──────────────────────────┴───────────┐
│ image layers: alpine:3.20 │ read-only, shared
└──────────────────────────────────────────────────┘When a process inside the container writes, the change goes into the writable layer:
| Action in the container | What the writable layer records |
|---|---|
| Create a file | The new file |
| Edit a file from the image | A full copy of the file, then the edit (copy-on-write) |
| Delete a file from the image | A marker that hides the image's file |
The image never changes. Start ten containers from it and each one begins from the same files.
See the changes with docker diff
docker diff lists what a container changed compared to its image:
sh
docker run -d --name worker alpine:3.20 sleep infinity
docker exec worker mkdir -p /var/cache/worker
docker exec worker sh -c 'echo job-42 > /var/cache/worker/last-job'
docker exec worker rm /etc/alpine-release
docker diff workertext
C /etc
D /etc/alpine-release
C /var
C /var/cache
A /var/cache/worker
A /var/cache/worker/last-job| Letter | Meaning |
|---|---|
A | Added |
C | Changed (a parent directory of a change is also C) |
D | Deleted |
How long the layer lives
text
docker run ──► running ──docker stop──► exited ──docker start──► running
│ │
└────── same writable layer, same files ──────┘
│
docker rm ──► layer deleted| Command | Writable layer |
|---|---|
docker stop, docker start, docker restart | Kept |
docker rm (or docker run --rm on exit) | Deleted |
docker run again from the same image | A new, empty layer |
That last row is the problem. Upgrading an image, changing a port or fixing an environment variable all mean removing the container and creating a new one. Anything the application wrote to its writable layer goes with it.
Other limits
- The layer sits under Docker's storage directory on the host, not in a path you choose.
- Copy-on-write makes write-heavy workloads such as databases slower than writing to a volume.
- Other containers cannot read it.