What happens to a container's writable-layer data when the container is removed?

5 minintermediatewritable-layerdata-persistencecontainer-removal

Quick Answer

Any data written to a container's own writable layer — files not stored in a mounted volume or bind mount — is permanently deleted the moment that container is removed with docker rm. The writable layer belongs exclusively to that one container and has no existence beyond it. That's why data meant to outlive a container's removal must live in a separate, explicitly mounted volume, not in the container's own filesystem.

Detailed Answer

The demonstration

docker run -d --name my-db postgres:16    # no volume mounted -- data lives ONLY in the writable layer
docker exec my-db psql -U postgres -c "CREATE TABLE important_data (...);"
# ... insert critical data ...

docker rm -f my-db
docker run -d --name my-db postgres:16     # a FRESH container, from the same image
docker exec my-db psql -U postgres -c "SELECT * FROM important_data;"
# ERROR: relation "important_data" does not exist

The second container is entirely new. It starts from the image's original, unmodified layers, with a fresh, empty writable layer. Every change made to the first container — the new table and its data — lived only in that container's now-deleted writable layer. That data is gone permanently, with no relationship to the second container, even though both started from the same image.

Why this is expected, correct behavior — not a bug

An image is an immutable, read-only template. Each container gets its own independent writable layer on top of it, via copy-on-write. That's what lets many containers start from the same image at once, each with fully independent state. But it also means a container's writable layer is tied to that one container's lifetime — not to the image, and not shared with any other container.

The fix: mount a volume for anything that needs to survive

docker volume create db-data
docker run -d --name my-db -v db-data:/var/lib/postgresql/data postgres:16

Now the database's actual data files live in the named volume db-data, not in the container's writable layer. Removing this container and starting a fresh one picks up exactly where the previous one left off, as long as it mounts the same volume:

docker rm -f my-db
docker run -d --name my-db -v db-data:/var/lib/postgresql/data postgres:16
docker exec my-db psql -U postgres -c "SELECT * FROM important_data;"
# the data is still there -- it was never IN the removed container's writable layer at all

The mental model this reinforces

Think of the writable layer as disposable, scratch space specific to one container instance. Volumes are the only place genuinely persistent data should live. Treat any file written outside a mounted volume path as something you're fine losing the instant that container is removed. Logs (which should go to stdout/stderr and be captured by Docker's logging driver), temporary caches, and other genuinely ephemeral data are fine in the writable layer. Real application data, database files, and uploaded content are not.

A common real-world mistake this explains

A common incident: a database or application was run without a mounted volume during initial setup, maybe for a "quick test" that quietly became the actual production deployment. Months of accumulated data are then permanently lost the first time that container is removed or replaced — a routine update, a host migration, or simple operator error. This happens because nothing was ever persisted outside that one container's writable layer. Checking that every stateful container mounts a proper volume for its data, instead of just assuming it does, is a basic production-readiness check.

Related Resources