Comparisons

Docker image vs container: what each one is and how they relate

Docker image vs container: an image is a read-only layered template, a container an instance of it with a writable layer. Table, lifecycle, commands.

Published ~8 min read
On this page

Short answer: a Docker image is a read-only template: a stack of filesystem layers plus settings such as the command to run. A container is an instance of that image: the same layers plus a thin writable layer, its own settings and, while it runs, a process. One image can start many containers, as one program file starts many processes.

any system with Docker · one image, two containers
docker pull nginx:1.27                       # the image: downloaded once
docker run -d --name site-a nginx:1.27       # container 1
docker run -d --name site-b nginx:1.27       # container 2, same image, separate state
docker images                                # lists images
docker ps -a                                 # lists containers

The commands and storage details below follow Docker’s documentation on docs.docker.com. Docker was not run for this article, so the output blocks show the documented format, not a recorded session.

Docker image vs container at a glance

ImageContainer
What it isA read-only templateAn instance of an image, running or stopped
Writable?No: its layers never change once builtYes: a thin writable layer that belongs to this container only
Made withdocker build from a Dockerfile, or docker pulldocker run, or docker create, from an image
ContainsFilesystem layers and settings: CMD, ENTRYPOINT, ENV, EXPOSE, the userThe image's layers, its own writable layer, runtime settings (name, ports, variables, mounts, limits) and, while running, processes
StateNone: the same image is identical on every machinecreated, running, paused, exited and a few more
Shared?Its layers are shared by every container and every image built on itIts writable layer and settings are its own
Where it livesIn a registry such as Docker Hub, and in the local image storeOnly on the host that created it
Size on diskThe size of its layers, counted onceOnly what it changed, shown by docker ps -s
Removed withdocker rmi, refused while a container uses itdocker rm; changes outside volumes are gone
Listed withdocker imagesdocker ps -a
AnalogyA class, a program file, a recipeAn object, a running process, the dish

What a Docker image is

An image is a set of read-only filesystem layers and a small configuration file that says how to run it. Each Dockerfile instruction that changes files (RUN, COPY, ADD) adds a layer holding just those changes; instructions such as ENV, EXPOSE and CMD only change the configuration. Take this Dockerfile:

Dockerfile
FROM debian:12-slim
RUN apt-get update && apt-get install -y --no-install-recommends curl \
    && rm -rf /var/lib/apt/lists/*
COPY app.sh /usr/local/bin/app.sh
CMD ["app.sh"]

docker build -t myapp:1.0 . turns it into an image, and docker history lists its layers, newest first:

Example output · docker history myapp:1.0 (shortened)
IMAGE          CREATED         CREATED BY                                      SIZE
3c1f0d2b9e7a   2 minutes ago   CMD ["app.sh"]                                  0B
<missing>      2 minutes ago   COPY app.sh /usr/local/bin/app.sh # buildkit    1.2kB
<missing>      2 minutes ago   RUN /bin/sh -c apt-get update && apt-get ins…   9.8MB
<missing>      3 weeks ago     ... the layers of debian:12-slim ...

Layers are stored by content hash, so ten images built on debian:12-slim keep its layers on disk once, not ten times. A tag such as myapp:1.0 is a movable name pointing at an image; the digest (myapp@sha256:...) names exactly one image and never changes. Images follow the OCI image specification, which is why the same image runs under Docker, Podman, containerd and Kubernetes alike.

What a Docker container is

A container adds three things to an image: a writable layer, its own runtime settings, and, once started, a process isolated with the kernel’s namespaces and limited with cgroups. The image’s layers stay read-only underneath. When the container changes a file that came from the image, the storage driver first copies that file up into the writable layer and the container works on the copy (copy-on-write); a deleted file is only marked as deleted there. The image never changes, and other containers never see this one’s changes.

any system with Docker · what a container changed
docker exec site-a sh -c 'echo "# extra" > /etc/nginx/conf.d/extra.conf'
docker diff site-a
Example output (shortened)
C /etc
C /etc/nginx
C /etc/nginx/conf.d
A /etc/nginx/conf.d/extra.conf
C /var/cache/nginx
A /var/cache/nginx/client_temp

A is added, C changed and D deleted. That writable layer lives exactly as long as the container: stopping keeps it, docker rm deletes it. Data that must outlive the container, such as a database’s files or uploads, belongs in a volume or a bind mount, which sit outside the container’s layers.

Example output · docker ps -s (some columns left out)
CONTAINER ID   IMAGE        STATUS         NAMES    SIZE
4f1c2e9a7b3d   nginx:1.27   Up 3 minutes   site-a   1.09kB (virtual 192MB)
9d2e8b1a6c5f   nginx:1.27   Up 3 minutes   site-b   1.09kB (virtual 192MB)

SIZE is the container’s own writable layer; virtual adds the image it shares with every other container of that image. Two containers of a large image cost that image once, plus what each one writes.

The lifecycle of each

StageImageContainer
Comes into beingdocker build, or docker pull from a registrydocker create, or docker run (create and start)
Nameddocker tag myapp:1.0 registry.example.com/myapp:1.0--name at creation, docker rename later
In useRead by containers; docker push shares itrunning, paused (docker pause), restarting
StoppedImages do not run, so they do not stopexited after docker stop or when its process ends; docker start runs it again
Turned into the otherdocker run makes a container from itdocker commit makes an image from it (a Dockerfile is the better habit)
Removeddocker rmi, once no container uses itdocker rm, after stopping

The order of removal follows from that: an image cannot go while a container, even a stopped one, still uses it, so containers go first. Cleaning up both, and the docker system prune family, is covered in the Docker commands cheat sheet.

Commands for images and for containers

TaskImageContainer
Listdocker imagesdocker ps -a
Createdocker build -t myapp:1.0 .docker run -d --name web myapp:1.0
Inspect the settingsdocker image inspect myapp:1.0docker inspect web
See what is insidedocker history myapp:1.0docker diff web, docker logs web
Get a shelldocker run --rm -it myapp:1.0 shdocker exec -it web sh
Save to a filedocker save -o myapp.tar myapp:1.0docker export -o web.tar web
Removedocker rmi myapp:1.0docker rm web
Remove all unuseddocker image prune -adocker container prune

docker save and docker export look alike and are not: docker save writes an image with all its layers, tags and history, for docker load on another machine. docker export writes a container’s filesystem flattened into one tarball, without layers, history or settings such as CMD.

Container image vs base image

Every image you can run is a container image. A base image is the image another one starts from, the one named in its FROM line: in the Dockerfile above, debian:12-slim is the base image and myapp:1.0 the image built on it. Older Docker documentation was stricter: it called the image in FROM the parent image and kept “base image” for one built FROM scratch, the empty image. Current usage, Docker’s own included, calls the image in FROM the base image.

  • Distribution images (debian, ubuntu, alpine) give you a package manager and a shell. -slim variants leave out documentation and extras. Alpine is the smallest of them but uses musl instead of glibc, which some prebuilt binaries and Python wheels do not support.
  • Language images (python, node, golang) put a runtime on top of a distribution image, in full and slim variants.
  • Minimal images (scratch and distroless images) hold almost nothing but your program: small and with little to attack, but no shell to debug in.

The base image is part of every image built on it, so its security fixes only reach you when you rebuild: docker build --pull -t myapp:1.1 . fetches the latest version of the base tag first. Pin the base to a version tag (debian:12-slim, not debian:latest) so a rebuild does not move you to a new major release by surprise.

Where Docker stores images and containers

Linux
docker info -f '{{.DockerRootDir}}'    # usually /var/lib/docker
docker info -f '{{.Driver}}'           # the storage driver, usually overlay2
docker system df                       # space used by images, containers, volumes, build cache

On Linux with the default overlay2 driver, everything sits under /var/lib/docker: image layers and the containers’ writable layers in overlay2/, each container’s settings and its JSON log in containers/<id>/, and named volumes in volumes/. Docker installs that use the containerd image store keep image layers and snapshots in containerd’s own directory instead, by default /var/lib/containerd; there the storage driver that docker info reports is a containerd snapshotter such as overlayfs rather than overlay2, so its output shows which applies. Docker Desktop on Windows and macOS keeps all of it inside its Linux virtual machine.

Containers vs virtual machines

A container is an isolated process on the host’s kernel; a virtual machine boots a kernel of its own on virtual hardware. That makes containers start in moments and cost little more than the program itself, while a VM gives the harder isolation boundary and can run any operating system. They are usually combined, not chosen between: Docker runs inside a KVM virtual machine such as a Linux VPS exactly as on bare metal. The full comparison is in what virtualisation is, and how Docker’s one-process containers differ from system containers that behave like small VMs is in LXC vs Docker.

Three misunderstandings that cost people data

  • “I changed the container, so the image has it.” It does not. The change lives in that container’s writable layer and is gone with docker rm or when you recreate the container from a newer image. Put the change in the Dockerfile, or the data in a volume.
  • “Pulling a new image updates my container.” It does not. A container stays on the image it was created from; to update, pull, then remove and recreate the container (docker compose up -d does both for Compose services).
  • “Stopping a container deletes it.” It does not, unless it was started with --rm. Stopped containers keep their writable layer, their name and their image until docker rm, which is why docker ps -a fills up over time.

Frequently asked

What is the difference between a Docker image and a container?

An image is a read-only template made of filesystem layers and settings such as the command to run. A container is an instance of an image: the image's layers plus its own writable layer, runtime settings and, while it runs, a process. One image can run as many containers at once.

Can a container exist without an image?

No. Every container is created from an image and needs its layers for as long as it exists, which is why docker rmi refuses to delete an image that any container, even a stopped one, still uses. Remove the containers first, then the image.

What is a base image?

The image another image starts from, named in the FROM line of its Dockerfile, such as debian:12-slim. Its layers become the bottom layers of your image, so its security fixes reach you only when you rebuild, for example with docker build --pull. An image built FROM scratch starts from nothing.

Where does Docker store images and containers?

On Linux under /var/lib/docker by default: image layers and the containers' writable layers in overlay2/, container settings and logs in containers/, named volumes in volumes/. docker info -f '{{.DockerRootDir}}' prints the location. Installs that use the containerd image store keep image layers in containerd's own directory, by default /var/lib/containerd.

Related articles

All comparisons