skip to content

What does `docker commit` capture from a container, and why is the resulting image a poor deliverable?

level: middleimportance: should knowfreq 48%

answer

  1. Ask what records how the state came about
  2. The writable layer plus the container's config
  3. Runtime `-e` values land in the image config
  4. Pausing during capture is the default
  5. Forensic snapshot, never a shipped artefact

basics

~20 s

docker commit freezes a container's writable layer as a new layer on top of its image and copies the container's runtime config into the new image. Nothing records how that state was produced, so the image cannot be rebuilt, reviewed or trusted.

solid answer

~50 s

`docker commit CONTAINER [repo:tag]` takes everything the container has written since it started — its writable layer — and turns it into one new layer stacked on the image the container came from. It also copies the **container's config** into the new image: `Env`, `Cmd`, `Entrypoint`, exposed ports and so on, including values you injected with `docker run -e`. By default it **pauses** the container while it does this (`--pause=false` turns that off, at the risk of a torn filesystem), and `--change` / `-c` lets you set config instructions such as `CMD`, `ENTRYPOINT`, `ENV`, `USER`, `WORKDIR`, `EXPOSE` or `LABEL` on the result. What it does not produce is any record of *how* the state came about: no Dockerfile, no build log, one opaque history entry. The image cannot be rebuilt from source, cannot be code-reviewed, and quietly bakes in whatever secrets and debris the session left behind. Volume contents are not included.

code

bash · 5 lines
bash
docker commit -m 'gateway wedged at 14:07, before kill' -a 'oncall' \
  ingest-gw forensics/ingest-gw:2026-09-03
docker rm -f ingest-gw

docker run --rm -it --entrypoint /bin/sh forensics/ingest-gw:2026-09-03

go deeper

for a junior

Know that docker commit turns a container's current state into an image, and that the normal way to build images is a Dockerfile. Be able to say why the committed one cannot be rebuilt.

for a middle

Explain exactly what is captured — the writable layer plus the container config — what --pause and --change do, and why volume data and the shell session's commands are absent from the result.

for a senior

Show the incident use: snapshot a misbehaving container for forensics, then fold the finding back into the Dockerfile. Be able to reverse-engineer an inherited committed image into something rebuildable.

for a principal

Own why commits appear at all. Recurring committed images mean the build or release path is too slow or too restricted; fix that, and make provenance a property the registry and the deployment path actually check.

### The mechanics A running container is its image's read-only layers plus one writable layer holding everything the process has changed. `docker commit` takes that writable layer, freezes it as a new read-only layer, stacks it on top of the same base layers, and registers the result as a new image — optionally under a name you give: `docker commit ingest-gw example/ingest-gw:hotfix`. Two things travel with the filesystem: 1. **The container's configuration.** The new image's config is derived from the container's, so `Entrypoint`, `Cmd`, `Env`, `WorkingDir`, `User` and `ExposedPorts` come across as the container had them — *including* environment variables supplied at `docker run -e`. A database password passed that way is now inside the image config, readable to anyone who can `docker image inspect` or pull it. 2. **Nothing else.** Data under a mounted volume or bind mount is not part of the container's writable layer, so it is not committed. A committed database container contains the binaries and the config but not the data directory if that directory was a volume — a surprise people hit exactly once. Useful flags: `-m` a commit message and `-a` an author, both stored in the image history; `-c` / `--change` applies Dockerfile-style config instructions to the result (`CMD`, `ENTRYPOINT`, `ENV`, `EXPOSE`, `LABEL`, `USER`, `VOLUME`, `WORKDIR`); and `-p` / `--pause`, which is **on by default** — the daemon pauses the container's processes so the filesystem is not being mutated mid-capture. `--pause=false` avoids the stall on a latency-sensitive service but can capture a half-written file, so it is a deliberate trade, not a default to copy. ### Why the result is not a deliverable Imagine a telemetry ingest gateway — a Python FastAPI service on `python:slim`. Its image is failing to start because a native wheel is missing. Someone `docker exec`s in, runs a package install, watches the service come up, and commits the fixed container as `ingest-gw:1.9.3`. It works. It is still a bad artefact, for four reasons. **It cannot be rebuilt.** There is no Dockerfile describing what was installed. `docker history` on the result shows the base image's build steps and then a single opaque entry from the commit; the commands run in the shell session are not recorded anywhere. Six weeks later, when a CVE forces a base-image bump, nobody can reproduce the fix — the Dockerfile in the repo still builds the broken image, so the source of truth and the running artefact have diverged. **It cannot be reviewed.** The change never went through a pull request. Nobody can diff it, comment on it, or find out who decided to install that package. Compare this to a one-line Dockerfile edit, where the build is deterministic and the reviewer sees exactly what changed — and where a rebuild that reuses most cached layers (a 71% cache-hit rate on that gateway's build is typical) costs less time than the debugging session did. **It carries debris and secrets.** The writable layer contains everything else the interactive session touched: shell history, a package manager cache, a token echoed into a file, a core dump, editor swap files. All of that becomes a permanent, immutable layer. Together with the `-e` values copied into the config, that is the standard way credentials leak into images. **It breaks the immutability contract.** The value of an image is that a tag names a build you can point at a commit in a repository. A committed image names a person's afternoon. ### When commit is actually right Commit is a **forensic** tool, and there it is genuinely the right answer. A container is misbehaving in production and you have to kill it, but you want its state preserved: `docker commit --pause=true bad-gw forensics/bad-gw:2026-09-03` gives you an image you can `docker run` later with a shell as the entrypoint, or ship to whoever debugs it, without keeping the incident container occupying its port. It is also acceptable for a genuinely throwaway snapshot — capturing a mid-experiment state you intend to discard. The discipline is the same in both cases: whatever you learn from the committed image ends up back in the Dockerfile, and the committed image is never what ships. If you find committed images in a production registry, treat it as a process defect — usually a signal that rebuilding is too slow or too painful, which is the thing to fix.

  • What does `docker commit --pause=false` change, and when would you accept it?
    By default the daemon pauses the container's processes while it captures the writable layer, so the filesystem is not mutated mid-capture. `--pause=false` skips that stall for a latency-sensitive service, at the risk of committing a half-written file or an inconsistent data directory. Accept it only when a brief pause is worse than a possibly torn snapshot.
  • Does `docker commit` capture the data in a volume mounted into the container?
    No. Volume and bind-mount contents are not part of the container's writable layer, so the committed image has the mount point but not the data. Committing a database container this way gives you binaries and configuration with an empty data directory.
  • You inherit a production image that was produced by `docker commit`. How do you get back to something rebuildable?
    Run `docker history` and `docker image inspect` to identify the base image and the config, then `docker diff` a container from the base against the committed one, or unpack both filesystems, to enumerate what actually changed. Write those changes into a Dockerfile, rebuild, and compare behaviour before retiring the committed tag.

saying these in an interview costs you the question

  • Presents commit as a normal way to build production images
  • Thinks the shell commands run in the container are recorded
  • Assumes volume data is included in the commit
  • Unaware that runtime -e values land in the image config
  • Believes `docker commit` never disturbs the running container
  • Says the committed image can be reproduced from its history

context