An engineer ran `docker volume prune` on a busy production host to reclaim disk space. Which volumes does that command actually delete, which does it spare, and how would you reclaim volume space safely on such a host?
answer
- unused = zero container references (stopped counts)
- 23.0+: anonymous only by default; --all reaches named
- compose down → prune -a → data gone
- docker system df -v, LINKS column
- prune --filter label=... ; ban system prune --volumes
basics
~20 sIt deletes volumes no container references. On Docker Engine 23.0+ that means anonymous ones only by default; --all extends it to unused named volumes — the dangerous flag, since a stopped service's database volume counts as unused. Safe practice: inspect with docker volume ls -f dangling=true and docker system df -v, prune by label, back up first.
solid answer
~50 s`docker volume prune` removes volumes that **no container currently references** — running or stopped. Since Docker Engine 23.0 the default is restricted to **anonymous** volumes, which is the safe subset; `--all`/`-a` widens it to unused **named** volumes too, and that is where data dies: a database whose container was removed during a redeploy has a perfectly healthy named volume that looks "unused" for those few seconds. Safely reclaiming space is a three-step routine. First measure: `docker system df -v` ranks volumes by size, `docker volume ls -f dangling=true` lists candidates. Second, scope the deletion: `docker volume prune --filter label=ephemeral=true` or `--filter label!=keep`, which is why volumes should be labelled at creation. Third, delete named volumes individually with `docker volume rm` after confirming the owner, ideally after a helper-container backup. On CI hosts the better fix is upstream — `docker run --rm` so anonymous volumes never accumulate.
code
bash · 10 linesdocker system df # is it volumes at all?
docker system df -v | sed -n '/VOLUME NAME/,$p'
docker volume ls -f dangling=true
# who still holds it?
docker ps -a --filter volume=app-data
# label-scoped cleanup
docker volume prune --filter label=lifecycle=ephemeral
docker volume rm build-cache-2026-07 # targeted, loud on failurego deeper
Know that prune deletes volumes no container is using, and that it is not a safe reflex on a machine holding real data.
State the exact definition of unused, the 23.0 anonymous-by-default versus --all split, and that docker system prune needs --volumes to touch them.
Give the operational routine — measure with docker system df -v, identify owners, prune by label, remove named volumes individually after a backup — and name the compose-down-then-prune data-loss sequence.
Set policy: labelling standards at volume creation, forbidden flags in automation, separation of stateful and build hosts, disk alerting early enough that nobody prunes under pressure, and backups as the thing that makes any of it survivable.
## What "unused" means to Docker Prune's definition of unused is narrow and mechanical: **not referenced by any container object**. Not "not written to recently", not "not attached to a service", not "not mentioned in a Compose file". A container that is stopped, exited, or merely created still counts as a reference and protects its volumes. The instant that container object is removed — which every `docker compose up -d` recreate and every `docker rm` does — the volume becomes eligible. That gap is the danger. The sequence that destroys a database is ordinary: ``` docker compose down # containers removed, named volumes kept docker volume prune --all # "cleaning up before the new deploy" docker compose up -d # fresh, empty database ``` Nothing errors. The service comes up and initialises an empty data directory. ## The version split you must state On Docker Engine **23.0 and later**, `docker volume prune` removes only **anonymous** unused volumes; named ones are spared unless you add `--all`/`-a`. That change was made precisely because the old behaviour deleted named volumes by default and cost people data. On **older** engines, prune removed every unused volume, named included, and the only protection was the interactive confirmation prompt (suppressed by `-f`). So the first question on an unfamiliar host is `docker version`. Muscle memory built on one behaviour is wrong on the other, and `-f` (force, skip the prompt) plus `-a` on an old engine is a data-loss command with no warning at all. ## Reclaiming space safely **1. Measure before deleting.** ``` docker system df # is VOLUMES actually the problem? docker system df -v # per-volume size + link count docker volume ls -f dangling=true ``` Often the disk is going to images and build cache, not volumes, and `docker image prune` / `docker builder prune` is the real answer. `docker system df -v`'s LINKS column shows how many containers reference each volume; zero means prune-eligible. **2. Identify owners.** For any named volume you are about to delete: `docker ps -a --filter volume=<name>` to see whether a container still holds it, `docker volume inspect <name>` for labels and creation time, and the Compose files or unit files on the host to see whether something will want it back after the next deploy. Creation time alone is a weak signal — an old volume can be the most important one there. **3. Scope the deletion with labels.** This is the practice that turns pruning from a gamble into an operation. Label at creation, then filter: ``` docker volume create --label lifecycle=ephemeral build-cache docker volume prune --filter label=lifecycle=ephemeral docker volume prune --all --filter label!=lifecycle=persistent ``` Prune also accepts `--filter until=720h` on modern engines to restrict to volumes older than a duration. **4. Prefer targeted removal for named volumes.** `docker volume rm one-name` deletes exactly what you meant, fails loudly if it is in use, and appears in shell history as a specific act. Back it up with a helper container first if the contents are not reproducible. **5. Fix the source of the garbage.** Anonymous volume creep is a symptom: images declaring `VOLUME`, and CI running containers without `--rm`. Adding `--rm` to CI invocations, and `docker compose down --volumes` at the end of ephemeral test stacks, prevents the accumulation that tempts people to run a broad prune under disk pressure at 3am. ## Guardrails worth having - **Never put `docker system prune -a --volumes` in automation** on a host that carries state. `docker system prune` includes volumes only when `--volumes` is passed — that flag is the one to ban in runbooks and CI scripts. - **Separate the hosts.** Stateful workloads on hosts where aggressive pruning is forbidden; build/CI workloads on hosts where a cron prune is fine. Mixing them means the safe cleanup policy is set by the riskiest workload. - **Alert on disk before it is urgent.** Pruning decisions made at 95% disk with a pager going off are how `--all -f` gets typed. - **Backups make prune survivable.** The difference between an incident and a shrug is whether the volume you just deleted has a tested restore. ## Answering the actual question in an interview Say the definition (no container references it), name the 23.0 default/`--all` split, give the concrete data-loss sequence, and then describe the routine: measure with `system df -v`, identify owners, prune by label, remove named volumes one at a time, and fix the upstream `--rm` habit so bulk pruning is rarely needed.
- Does `docker system prune -a` delete volumes?Not by default — it removes stopped containers, unused networks, dangling (and with `-a`, all unused) images and the build cache, but volumes only if you add `--volumes`. That flag is the one to keep out of runbooks and CI scripts on any host holding state, since combined with `-f` it deletes unreferenced named volumes with no prompt.
- How do you make a critical volume survive an operator running a broad prune?Docker has no protection flag, so the defences are procedural: keep a container referencing the volume so it is never counted as unused, label volumes and prune only by label, restrict which hosts carry state, and keep tested off-host backups. Documenting `--volumes` and `--all` as forbidden in automation is part of it.
saying these in an interview costs you the question
- Thinking prune only removes volumes that are 'empty' or 'old' rather than unreferenced.
- Believing a stopped container's volumes are prune-eligible — they are protected until the container object is removed.
- Assuming the modern anonymous-only default applies on every engine version.
- Putting `docker system prune -a --volumes -f` in a cron job or CI cleanup step on a stateful host.
- Treating volume pruning as the first response to a full disk without checking whether images or build cache are the real consumer.