Skip to main content

How to create a Docker image from a running container?

docker commit lets you turn a running container into an image — a snapshot of whatever state the container is in. Tempting for shortcuts, dangerous for production. Knowing when it is the right tool is mostly about knowing when it is the wrong one.

Theory

TL;DR

  • docker commit <container> <image:tag> saves the container's current state as a new image.
  • Captures the writable layer + the underlying image layers.
  • Optional flags: -m message, -a author, -c apply Dockerfile-style changes (CMD, ENV, etc.).
  • Anti-pattern for production. No Dockerfile, no reproducibility, no review trail, no peer review.
  • Useful for: debug snapshots, capturing exploratory state, salvaging a fixed-up container before recreating from a Dockerfile.

Quick example

bash
$ docker run -it --name explore alpine:3.21 sh / # apk add --no-cache curl / # echo 'custom config' > /etc/myconfig / # exit $ docker commit -m 'added curl + custom config' explore my-explorer:0.1 sha256:abc123... $ docker run --rm my-explorer:0.1 sh -c 'which curl && cat /etc/myconfig' /usr/bin/curl custom config

The interactive session's changes are now baked into a new image.

Syntax

bash
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] # Common options -m "message" # commit message (visible in docker history) -a "author" # author name -p / --pause=false # by default, container is paused during commit; --pause=false skips pausing -c "INSTRUCTION" # apply a Dockerfile instruction (CMD, ENV, EXPOSE, USER, WORKDIR, LABEL)

Why it is anti-pattern for production

bash
$ docker commit web myorg/web:1.0

What the resulting image is, exactly:

  • A snapshot of whatever the container had at this moment.
  • Includes anything that was in the writable layer — installed packages, config tweaks, accidental files.
  • No record of how to recreate it.

Problems:

  • Not reproducible. If you lose the image, you cannot rebuild from source.
  • No code review. A Dockerfile change goes through PR; a docker commit does not.
  • Hidden side effects. Whatever the container did at runtime (logs, scratch files) lands in the image.
  • Image bloat. Anonymous layers, no caching benefits, no .dockerignore.
  • Drift. The image is whatever the human typed in the container, not a versioned recipe.

In production: write a Dockerfile, commit it to git, build via CI. Always.

Legitimate uses

Debug snapshots

A prod container is misbehaving. You want a frozen copy to investigate later without keeping the live container around:

bash
$ docker commit -m 'snapshot before restart' broken-prod debug:broken-2026-04-30 $ docker push myreg/debug:broken-2026-04-30 # share with the team

Now you can docker run the snapshot in a sandbox to poke at it.

Capturing exploratory work

You are figuring out what packages an image needs. Iterate inside a container, then commit when you have it working — and use that to write the Dockerfile properly:

bash
# Exploration $ docker run -it --name explore alpine sh / # apk add --no-cache curl jq postgresql-client / # exit # Capture state $ docker commit explore exploration:scratch # Now write the Dockerfile from what you learned: # FROM alpine # RUN apk add --no-cache curl jq postgresql-client

The commit is a stepping stone, not the final artifact.

Disaster recovery

Prod container has a manual fix that has not been Dockerfile-ized yet, and the container is about to be replaced. Commit it to preserve the state until the Dockerfile is updated:

bash
$ docker commit prod-fix saved:before-redeploy # Open Dockerfile PR with the actual fix # Once merged: deploy from new Dockerfile, retire 'saved:before-redeploy'

Apply Dockerfile-style changes via -c

bash
docker commit -c 'CMD ["/bin/bash"]' -c 'ENV PATH=/app/bin:$PATH' explore my-tweaked:0.1

-c accepts a subset of Dockerfile instructions: CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, USER, VOLUME, WORKDIR. Useful for adjusting metadata without entering a shell.

Common mistakes

Treating docker commit as a deploy mechanism

bash
# WRONG: prod images via commit $ docker exec api npm install some-package $ docker commit api myreg/api:1.1 $ docker push myreg/api:1.1

Six months later: nobody knows what is in myreg/api:1.1. The Dockerfile says one thing, the image is another. Use Dockerfile + CI build + push instead.

Forgetting that pause is default

docker commit pauses the container by default to ensure consistency. For long commits or high-throughput services, that pause is visible. Use --pause=false if you can tolerate inconsistency.

Committing a container with secrets in env

bash
$ docker run -e DB_PASSWORD=hunter2 myapp $ docker commit myapp leaked:image # `leaked:image` now has DB_PASSWORD=hunter2 in its config $ docker inspect leaked:image --format '{{json .Config.Env}}' | jq ["DB_PASSWORD=hunter2", ...]

Committing bakes runtime env into the image. Push that and you have leaked the secret to anyone who can pull.

Treating commit-images as immutable history

Unlike Dockerfile-built images, you cannot easily diff two docker commit images. Reproducibility loss compounds: image A → tweak → commit → image B → tweak → commit → image C. Three weeks later, nobody knows what is in any of them.

Real-world usage

  • Hotfix snapshots: prod container has a manual fix; commit before nuking it; later turn the fix into a Dockerfile change.
  • Debug images for the support team: commit a problem container, push to a private registry, share with whoever needs to investigate.
  • Onboarding playgrounds: commit a customized dev container with the team's tools so new devs docker run once and have everything.
  • Migration freeze: during a multi-step migration where the running container is the source of truth; commit at milestones for rollback options.

Follow-up questions

Q: What is the difference between docker commit and docker save?


A: commit creates a NEW image from a container. save exports an existing image to a tarball. Different use cases entirely.

Q: How do I see the changes a commit captured?


A: docker diff <container> BEFORE you commit shows what changed in the writable layer. After commit, the new image's docker history lists it as one big layer with your message.

Q: Can I commit a stopped container?


A: Yes. The writable layer is still there until docker rm.

Q: Will the new image inherit the original container's command, env, etc.?


A: Yes — the image config inherits from the original image. Use -c flags during commit to override specific parts (CMD, ENV, etc.).

Q: (Senior) When is docker commit actively dangerous?


A: When it becomes the deployment mechanism. Teams that commit-tweak-commit lose Dockerfile discipline, fail audits, cannot reproduce builds, and accumulate "works on the server" magic. The first sign: the source repo's Dockerfile no longer reflects the prod image. Treat docker commit as exclusively a debugging tool — never as a build step.

Examples

Snapshot a misbehaving prod container for offline analysis

bash
# On prod $ docker commit -m 'OOM-killing every 5 min - 2026-04-30' \ api debug-snapshots:api-oom-2026-04-30 $ docker tag debug-snapshots:api-oom-2026-04-30 myreg/debug:api-oom-2026-04-30 $ docker push myreg/debug:api-oom-2026-04-30 # In dev $ docker pull myreg/debug:api-oom-2026-04-30 $ docker run -it --rm --entrypoint sh myreg/debug:api-oom-2026-04-30 # Now poke around in the same state prod was in.

Iterate, commit, write Dockerfile from learnings

bash
$ docker run -it --name lab python:3.13-slim bash root@lab:/# pip install --no-cache-dir requests pyyaml psycopg2-binary root@lab:/# echo 'export FOO=bar' >> /root/.bashrc root@lab:/# exit $ docker commit lab my-lab:0.1 # Wrote the equivalent Dockerfile based on what worked: # FROM python:3.13-slim # RUN pip install --no-cache-dir requests pyyaml psycopg2-binary # ENV FOO=bar

Use the commit to capture state during exploration; then translate to a Dockerfile for production.

Apply config changes via -c

bash
# Existing image, add labels and change default CMD $ docker commit \ -c 'LABEL maintainer="team@example.com"' \ -c 'CMD ["/bin/bash"]' \ -c 'ENV TZ=UTC' \ explore tweaked:0.1

Cheaper than rebuilding when you just want to adjust metadata on an existing image's snapshot.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Comments

No comments yet
How to create a Docker image from a running container? | ITLead