Data & Volumes
Measured on Docker Desktop 4.81.0 / Engine 29.6.1 (macOS, linux/arm64), 2026-09-30.
Why container data disappears
Section titled “Why container data disappears”A container has a writable layer on top of its read-only image layers. Every file you create, modify, or delete inside a running container lives only in that writable layer. The moment the container is removed with docker rm, that layer is gone — permanently.
This is by design. Containers are meant to be stateless, disposable, and reproducible. You should be able to stop a container, throw it away, and start a fresh one from the same image at any time. If your data lived inside the container, that model breaks down.
flowchart TB W["Writable layer (this container only): runtime writes, gone on docker rm"] I2["Image layer (read-only): COPY app files"] I1["Image layer (read-only): node:24-alpine base"] W --> I2 --> I1
Stop is not remove
Section titled “Stop is not remove”The data is lost on docker rm, not on docker stop. A stopped container keeps its writable layer on disk; docker diff lists exactly what that layer holds:
$ docker run --name eph alpine:3.24 sh -c "mkdir -p /data && echo 'hello world' > /data/message.txt && cat /data/message.txt"hello world$ docker diff ephA /dataA /data/message.txt$ docker run -d --name keep alpine:3.24 sh -c 'mkdir -p /data; echo persisted > /data/f; sleep 300'$ docker stop -t 1 keep && docker start keep && docker exec keep cat /data/fkeepkeeppersisted$ docker rm eph$ docker run --rm alpine:3.24 sh -c "cat /data/message.txt 2>/dev/null || echo 'File is gone!'"File is gone!The file survived the stop/start of the same container and vanished for a new container from the same image. That is why “the container restarted and my data is there” proves nothing about persistence: recreate the container (docker rm + docker run, docker compose up after a config change, an image upgrade) and it is gone.
The three persistence options
Section titled “The three persistence options”Docker gives you three mechanisms to break out of the ephemeral writable layer. Each row of the last column was produced on this Mac:
| Option | Where data lives | Managed by | Proof (Engine 29.6.1) |
|---|---|---|---|
| Named volumes | Docker-managed directory (inside the VM on Docker Desktop) | Docker | survives docker rm of the container - Named Volumes |
| Bind mounts | Any path on the host filesystem | You | file written in the container appears in the host directory - Bind Mounts |
| tmpfs mounts | Memory only (never written to the container’s disk) | Kernel | --tmpfs /scratch:size=8m refuses the 9th MB; emptied on restart (below) |
tmpfs: fast, capped, and gone at restart
Section titled “tmpfs: fast, capped, and gone at restart”$ docker run --rm --tmpfs /scratch:size=8m alpine:3.24 sh -c 'mount | grep scratch; dd if=/dev/zero of=/scratch/big bs=1M count=20; df -h /scratch | tail -1'tmpfs on /scratch type tmpfs (rw,nosuid,nodev,noexec,relatime,size=8192k)dd: error writing '/scratch/big': No space left on devicetmpfs 8.0M 8.0M 0 100% /scratchThe 20 MB write stopped at exactly 8 MiB. Note noexec in the mount options: a plain --tmpfs cannot run programs (see the bug below). After docker stop + docker start the same container found /scratch empty while a file written to its normal filesystem was still there. docker inspect shows a tmpfs under .HostConfig.Tmpfs ({"/scratch":""}), not in .Mounts ([]). The long form is --mount type=tmpfs,destination=/scratch,tmpfs-size=8m. tmpfs is for scratch data and secrets you refuse to write to disk; memory it uses counts toward the container’s memory limit.
Demonstrating ephemerality
Section titled “Demonstrating ephemerality”Before learning how to persist data, it is worth seeing the problem first-hand. The snippet below creates a file inside a container, removes the container, and runs a fresh one — the file is gone.
# Run a container and write a file
docker run --name ephemeral-demo alpine:3.24 sh -c "mkdir -p /data && echo 'hello world' > /data/message.txt && cat /data/message.txt"
# Remove the container
docker rm ephemeral-demo
# Start a brand-new container from the same image
docker run --rm alpine:3.24 sh -c "cat /data/message.txt 2>/dev/null || echo 'File is gone!'"Reading AI-generated code
Section titled “Reading AI-generated code”An AI-written worker script that “keeps the build cache in RAM for speed” and then runs a tool it downloads there:
🐛 Spot the bug
#!/bin/shset -eu# AI: tmpfs = fast scratch space, capped at 8 MBdocker run --rm --tmpfs /work:size=8m alpine:3.24 sh -c 'printf "#!/bin/sh\necho worker ran\n" > /work/tool.shchmod +x /work/tool.sh/work/tool.sh'Show the answer
The script fails although the file was written and made executable:
$ sh scripts/run-worker.shsh: /work/tool.sh: Permission denied # exit code 126--tmpfs mounts default to noexec,nosuid,nodev (visible in the mount line above), so nothing on that mount can be executed. Add the exec option when a tmpfs really must hold programs:
#!/bin/shset -eudocker run --rm --tmpfs /work:exec,size=8m alpine:3.24 sh -c ' printf "#!/bin/sh\necho worker ran\n" > /work/tool.sh chmod +x /work/tool.sh /work/tool.sh'With :exec the run printed worker ran. Prefer keeping executables in the image and using tmpfs only for data.
Mental Model
Section titled “Mental Model”A container’s filesystem is an image plus one private, disposable writable layer. Anything you want to outlive the container must be written to something mounted into it: a volume (Docker owns the storage), a bind mount (you own the path), or a tmpfs (RAM, on purpose short-lived). The container’s lifetime and the data’s lifetime are separate decisions.
Trade-offs
Section titled “Trade-offs”| Option | Benefit | Cost |
|---|---|---|
| Writable layer only | Zero setup | Lost on docker rm; slower than a volume for heavy writes; grows the container |
| Named volume | Survives rm, portable across hosts by name, Docker manages it | Opaque location (inside the VM on Desktop) - needs docker run -v or cp to look inside |
| Bind mount | You see and edit the files with host tools | Host paths, permissions, and OS differences leak into the container |
| tmpfs | No disk I/O, capped with size=, gone at restart | Not durable; counts against memory; noexec unless :exec |
Common Misconceptions
Section titled “Common Misconceptions”- “
docker stopdeletes my data.” It does not: the writable layer survives stop/start; onlydocker rm(or recreating the container) drops it. - “The container restarted and my file is still there, so it is persistent.” It is only persistent if it lives on a volume or bind mount; recreation loses it.
- “tmpfs is just a fast volume.” It is memory, it is emptied when the container stops, and it is
noexecby default. - “A named volume is a folder on my Mac.” On Docker Desktop it lives inside the VM.
What this module covers
Section titled “What this module covers”| Lesson | Topic |
|---|---|
| Named Volumes | docker volume create/ls/inspect/rm, mounting volumes, copy-up, prune semantics |
| Bind Mounts | Mapping host paths into containers, -v vs --mount, live-editing source code |
| Volumes vs Bind Mounts | Comparison table, portability, measured write speed, read-only mounts |
| Backup & Restore | Archiving volumes with a throwaway container, restoring, migrating |