Skip to main content
DevOps Insightsguides & answers

Docker · commonly confused

Image vs container

Short answer

An image is a read-only template. A container is a running instance of one, with a writable layer on top that disappears when the container is removed.

First published

On this page · 10 sections

In simple terms

The distinction sounds pedantic until data goes missing, at which point it becomes the whole story.

An image is built once and never changes. A container is created from an image and gets a thin writable layer of its own, stacked on top of the image's read-only layers. Anything the container writes goes into that layer, and that layer belongs to the container, not the image.

So two containers from the same image start identical and immediately diverge. And when a container is removed, its writable layer is removed with it. Nothing about the image changed, which is why rebuilding or re-running gets you back to the original state — and why the file you carefully wrote is gone.

Words used on this page

image
A read-only template: a stack of filesystem layers plus configuration such as the default command. Building or pulling it does not run anything.
container
An instance created from an image, with its own writable layer on top of the image's layers. It exists, running or stopped, until you remove it.
layer
One set of filesystem differences, made by a Dockerfile instruction that changes files. Image layers are read-only and shared by every image and container that uses them.
writable layer
Also called the container layer: the thin layer Docker adds for each container. Every file the container creates, changes or deletes is recorded there, and it is deleted with the container.
copy-on-write
Files are read from the image's layers until a container changes one. Then the file is copied up into the writable layer first; with overlay filesystems the whole file is copied, even for a small change.
storage driver and snapshotter
The part of Docker that stacks the layers on disk: a classic storage driver such as overlay2, or a containerd snapshotter (overlayfs by default) in the containerd image store.
volume
Storage that Docker manages outside any container's layers. It keeps its data when the containers using it are removed.
virtual size
In docker ps --size, the read-only image data a container uses plus its own writable layer. Containers from one image share that image data, so adding their virtual sizes overstates disk use.

What actually happens

An image is a stack of filesystem layers plus a configuration object, identified by the digest of its content. Layers are shared between images, which is why pulling a second image based on the same base is fast.

Creating a container adds a writable layer on a copy-on-write filesystem: the classic overlay2 storage driver, or on fresh installs of Engine 29.0 and later the containerd image store, whose default snapshotter is overlayfs. Reads fall through to the layers below; writes go into the top layer. Modifying an existing file triggers copy-up: the whole file is copied into the writable layer first, which is why writing to a large file in a container can be surprisingly slow the first time.

The consequence for secrets is worth stating separately, because it catches people out. A file added in one image layer stays in that layer even if a later layer deletes it. Deleting a copied credential does not remove it — anyone with the image can recover it from the earlier layer.

The divergence, demonstrated

docker run --name a -d myapp
docker run --name b -d myapp     # same image, separate writable layers

docker exec a sh -c 'echo hello > /tmp/note'
docker exec b cat /tmp/note      # No such file — b never saw it

docker rm -f a
docker run --name a -d myapp
docker exec a cat /tmp/note      # Still gone. The layer went with the container.

For anything that must outlive the container, mount a volume. That is not an optimisation: a writable layer is deleted with its container, so data that must survive needs a volume or a bind mount.

Try it yourself

Before you start

  • Docker running, and permission to use it: Docker Desktop started, or on Linux membership of the docker group or sudo. Anyone who controls the Docker daemon can change the host's files without restriction, so only trusted users should have it.
  • A small image that includes sh, such as alpine, so you can write files inside a container with docker exec.
  • Containers you are free to delete. Removing a container deletes everything in its writable layer, so do not experiment on one that holds data you need.
  • Which storage backend your Engine uses: docker info -f '{{ .DriverStatus }}' prints [[driver-type io.containerd.snapshotter.v1]] for the containerd image store, the default on fresh installs of Engine 29.0 and later. Docker's layer examples were written for the classic overlay2 driver, so sizes and paths can differ on yours.

Five containers, one image, three small writes

Quoted from the copy-on-write walkthrough in Docker's Storage drivers page, not reproduced: Docker is not installed on the machine this page was checked on, and the page does not say which Engine version it used. Earlier steps built acme/my-final-image:1.0, a small Alpine-based image, and started five containers from it, my_container_1 to my_container_5, each running bash. The page shows no output for the loop, and the loop's {1..3} needs bash or zsh. The page describes classic storage drivers such as overlay2; Engine 29.0 and later use the containerd image store on fresh installs, where Docker says these examples won't reflect how your system stores images.

Command

for i in {1..3}; do docker exec my_container_$i sh -c 'printf hello > /out.txt'; done
docker ps --size --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Size}}"

Output · as shown in Docker docs: Storage drivers, copy-on-write walkthrough

$ for i in {1..3}; do docker exec my_container_$i sh -c 'printf hello > /out.txt'; done

$ docker ps --size --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Size}}"

CONTAINER ID   IMAGE                     NAMES            SIZE
cddae31c314f   acme/my-final-image:1.0   my_container_5   0B (virtual 7.75MB)
939b3bf9e7ec   acme/my-final-image:1.0   my_container_4   0B (virtual 7.75MB)
3ed3c1a10430   acme/my-final-image:1.0   my_container_3   5B (virtual 7.75MB)
a5ff32e2b551   acme/my-final-image:1.0   my_container_2   5B (virtual 7.75MB)
40ebdd763416   acme/my-final-image:1.0   my_container_1   5B (virtual 7.75MB)

How to read it

  • All five rows show virtual 7.75MB: the read-only image data each container uses. It is the same image data, stored once, so five containers do not cost five copies.
  • SIZE is each container's own writable layer. The three containers that wrote /out.txt show 5B, the five bytes of hello; my_container_4 and my_container_5 still show 0B.
  • The write went into three separate writable layers, not into the image. Docker's page notes that this data is unique to each container and not shared, so the other two containers have no /out.txt.
  • Remove my_container_1 and its writable layer goes with it, file included. The image, and the other containers' layers, stay as they were. A new container from the same image starts at 0B again.
  • The disk use of a container is more than SIZE: Docker's page lists log files, volumes and bind mounts, and the container's configuration files as extra space that this column does not count.

The mistake this causes

Treating the container's filesystem as somewhere data can live.

Why people do it It behaves exactly like a normal filesystem right up until the container is replaced, and containers get replaced constantly — on every deploy and every reschedule, and under Kubernetes on every container restart too. docker restart keeps the same container and its writable layer.

What you see Uploaded files, SQLite databases or generated content vanish after a deploy. Nothing errors and nothing is logged, because from the container's point of view nothing went wrong: a brand-new container simply has a brand-new empty writable layer.

If it goes wrong

Files written inside the container are gone after docker rm and docker run, or after docker compose down and up
The new container started with a new, empty writable layer; docker compose down removes the service containers too. Put data that must survive on a volume (-v app-data:/path). To rescue files from a container that still exists, even a stopped one, copy them out first with docker cp <container>:<path> ..
docker ps --size shows a container's SIZE growing, or the host disk fills while images stay the same
Something writes into the writable layer: caches, temporary files, an embedded database. docker container diff <container> lists added (A), changed (C) and deleted (D) paths. Move that path to a volume, which also avoids the copy-on-write overhead.
Deleting a large file in a later Dockerfile step did not make the image smaller
Each layer holds only differences, so the file still exists in the layer that added it and counts toward the image size. Create and delete it in the same RUN instruction, or use a multi-stage build that copies only what the final image needs.
The first write to a large existing file in a container is slow
That is copy-up: the storage driver copies the whole file into the writable layer before the first change, and only the first time. For files a container writes heavily, such as database files, use a volume instead.

How it shows up in production

The same misunderstanding runs in the other direction with secrets, and there it is permanent rather than merely inconvenient.

A .env file gets copied into an image during a build and deleted in a later step. The build output looks clean and the running container has no such file. But the layer created by the copy still contains it, the image was pushed to a registry, and anybody who can pull it can extract the secret with docker history and a layer dump. Rebuilding does not help, because the published image already exists. Only revoking the credential does.

How to tell which one you are hitting

Did the data survive a restart but not a replacement?
docker restart keeps the same container and its writable layer. docker rm then docker run creates a new one. If data survives the first and not the second, it is in the writable layer and needs a volume.
Is a deleted file really gone from the image?
Run docker history --no-trunc <image> and look for the step that added it. If a layer added it, it is still there regardless of what a later layer did.

Security considerations

  • A file that went into any layer stays in the image even when a later instruction deletes it. A key or .env copied in and then removed can be extracted by anyone who can pull the image, so pass build-time secrets with a secret mount (RUN --mount=type=secret) rather than COPY, ARG or ENV, and revoke any credential that has already shipped.
  • docker commit turns a container's writable layer into a new image layer, including whatever was written at run time: credentials, shell history, logs. Check docker container diff before you commit, and prefer building images from a Dockerfile.
  • A stopped container keeps its writable layer until it is removed, and docker cp can read files from a stopped container. Remove containers that handled secrets, and remember that anyone who can use the Docker daemon can read every container's files.
  • docker run --read-only mounts the container's root filesystem read-only, so a compromised process cannot change the application's own files. Add volumes for the few paths that really need to be writable.

In an interview

How this gets asked in an interview

Usually phrased as “What is the difference between an image and a container?”

What a strong answer contains Template versus running instance, then go straight to the writable layer and what happens to it. Mentioning copy-up, layer sharing, or the fact that deleting a file does not remove it from an earlier layer takes the answer beyond the definition.

The follow-up “Where would you put data that has to persist?” leads to volumes, and then to how that changes again under Kubernetes with PersistentVolumeClaims.

Sources

The behaviour this page describes, as documented by the projects themselves.

Terms used here (2)

Short definitions for the vocabulary on this page, in case any of it is new. Each links to the full entry.

Container
A running instance of an image: an isolated process with its own view of the filesystem, network and other system resources, plus a thin writable layer on top of the image's read-only layers that is discarded when the container is deleted. Containers share the host OS kernel, making them far more efficient than traditional virtual machines.
Image
A read-only template used to create containers. A container image packages application code, runtime, libraries, and dependencies into a single artifact that can be versioned, shared via registries, and run identically on any compatible host.

All 59 terms in the glossary

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it