skip to content

Why does a container that has already exited still consume disk space, and what do `docker rm`, `docker rm -f`, `docker run --rm`, and `docker container prune` each do about it?

level: middleimportance: should knowfreq 44%

answer

  1. Exited ≠ removed: writable layer + logs + name persist
  2. rm frees the layer; rm -f SIGKILLs first
  3. --rm auto-deletes on exit (and loses the logs)
  4. container prune bulk-removes stopped ones; --filter until=
  5. rm -v drops anonymous volumes only; system df to find the space

basics

~20 s

An exited container still exists as an object: its writable layer, logs, config and name are retained so you can inspect or restart it. docker rm deletes it and frees that space; docker rm -f kills a running one first; docker run --rm auto-deletes on exit; docker container prune bulk-removes all stopped containers.

solid answer

~60 s

Exiting ends the process, not the container. The container object survives with its **writable layer** (every file written outside a volume), its **JSON log file**, its configuration and its **name** — which is why `docker run --name api` fails with "name already in use" after the old one exited, and why `/var/lib/docker` fills up on long-lived hosts. - **`docker rm <c>`** deletes the container object and its writable layer. It refuses on a running container. - **`docker rm -f <c>`** sends SIGKILL first, then removes — no graceful shutdown, so avoid it on stateful services. - **`docker run --rm`** records auto-removal at creation: the daemon deletes the container as soon as it exits. Great for one-shot and CI containers; it also means you lose the logs and exit state, so do not use it while debugging crashes. - **`docker container prune`** removes all stopped containers at once (`--filter until=24h` to scope it). `docker system prune` goes further and also touches images, networks and build cache. Add `-v` to `docker rm` to also drop anonymous volumes the container created; named volumes are never removed this way.

code

bash · 4 lines
bash
docker system df -v
docker ps -as
docker ps -aq --filter status=exited | xargs -r docker rm
docker container prune --filter until=168h -f

go deeper

for a junior

Know that exited containers still take disk and that docker rm (or --rm at run time) is what frees it.

for a middle

Distinguish the writable layer from log files and volumes, and explain each command's semantics including the -f and -v flags.

for a senior

Drive it from docker system df -v and docker ps -as, configure log rotation at the daemon level, and use scoped prunes instead of blanket ones.

for a principal

Set host-level policy: data on volumes, rotation defaults in daemon config, scheduled scoped prunes, disk alerting — so no one ever has to free space under incident pressure.

## What survives an exit When a container's main process ends, Docker records the exit code and moves the container to `exited`. Everything else about the container persists: 1. **The writable layer** — the copy-on-write layer stacked over the image's read-only layers. Every file the process created or modified outside a mount lives here: temp files, downloaded caches, unrotated application logs written to disk, a database that was not put on a volume. This can be gigabytes. 2. **The container log file** — with the default `json-file` logging driver, stdout/stderr are stored under `/var/lib/docker/containers/<id>/<id>-json.log`. Without rotation configured this file can dwarf the writable layer. 3. **Configuration and state metadata** — command, env, mounts, ports, exit code, timestamps. This is what makes `docker inspect` and `docker logs` work post-mortem. 4. **The name** — names are unique among *all* containers, running or not. That retention is a feature: it is how you do forensics on a crashed container. It is also the most common way a Docker host runs out of disk, because nothing reclaims it automatically. ## The commands **`docker rm <container>`** deletes the container object, its writable layer and its log file. It refuses to touch a running container: `Error response from daemon: You cannot remove a running container`. Accepts multiple IDs, and composes well with `docker ps -aq`. **`docker rm -f <container>`** force-removes: the daemon SIGKILLs the process and then removes. There is **no graceful shutdown path** — no SIGTERM, no grace period — so a database or queue consumer gets no chance to flush or drain. Use `docker stop` then `docker rm` for anything stateful. **`docker rm -v <container>`** additionally removes **anonymous** volumes created by that container (those declared by `VOLUME` in the image or created without a name). **Named** volumes are independent objects and are never deleted by container removal — `docker volume rm`/`docker volume prune` handle those. **`docker run --rm ...`** sets auto-remove at creation time; the daemon deletes the container the moment it exits. Ideal for one-shot jobs, CLI tools in containers, and CI steps. The trade-off is real: once it is gone you cannot run `docker logs` or `docker inspect` on it, so when you are debugging a container that exits unexpectedly, drop `--rm` first. `--rm` is also incompatible with a restart policy, for the obvious reason. **`docker container prune`** removes every stopped container in one go, prompting for confirmation (`-f` to skip). `--filter until=24h` limits it to containers created more than 24 hours ago; `--filter label=...` scopes by label. Broader hammers: `docker system prune` (stopped containers + dangling images + unused networks + build cache) and `docker system prune -a --volumes`, which is destructive enough to deserve care on a shared host. ## Seeing where the space went `docker system df` is the first command to run: ``` TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 42 5 18.2GB 12.7GB Containers 137 3 6.1GB 6.0GB Local Volumes 19 4 44.3GB 31.2GB Build Cache - - 9.8GB 9.8GB ``` `docker system df -v` breaks it down per object. Per container, `docker ps -as` adds a SIZE column showing `<writable layer> (virtual <layer + image>)`. Note that log files are not counted in the container SIZE column, so a host can be full of log data that this view does not show — check `/var/lib/docker/containers/*/` directly, and configure log rotation (`max-size`, `max-file`) in the daemon config so it never gets there. ## Operational guidance - On developer machines and CI, prefer `--rm` for throwaway runs; it keeps the host clean by construction. - On servers, schedule a scoped prune (`docker container prune --filter until=168h -f`) rather than an unfiltered `system prune -a`, which will delete images you were relying on being cached. - Configure log rotation globally in `/etc/docker/daemon.json` rather than per container. - Write application data to **volumes**, not the writable layer, so removing a container is never a data-loss decision. - Never make `docker rm -f` the habitual stop command; it converts every removal into a crash.

  • Why is `docker rm -f` a poor default for stopping a service?
    It SIGKILLs the main process immediately with no SIGTERM and no grace period, so the application never drains connections, flushes buffers or commits in-flight work. For a database or queue consumer that means crash recovery or duplicate processing on the next run. The correct sequence is `docker stop` — which gives the graceful window — followed by `docker rm`.
  • Does `docker rm` delete the container's volumes?
    Not by default. Adding `-v` removes *anonymous* volumes that the container created, but named volumes are independent objects that survive container removal and require `docker volume rm` or `docker volume prune`. That asymmetry is deliberate: named volumes are meant to outlive the containers that use them.
  • You are debugging a container that exits unexpectedly. Why is `--rm` a problem there?
    Auto-removal deletes the container the instant it exits, taking its logs, exit code and inspect metadata with it — exactly the evidence you need. Run it without `--rm` so you can read `docker logs`, check `docker inspect -f '{{.State.ExitCode}}'`, and copy files out of the writable layer, then remove it manually.

saying these in an interview costs you the question

  • Assuming a stopped container releases its disk space automatically
  • Using `docker rm -f` as the routine way to stop services
  • Believing `docker rm -v` removes named volumes
  • Running `docker system prune -a` on a shared or production host as a first response to low disk
  • Debugging a crash-looping container that was started with `--rm` and wondering where the logs went

context