Prune Docker Volumes Without Guessing What Gets Deleted
docker volume prune deletes unused volumes for good, and its default scope is narrower than most people expect. This guide gets you to a controlled cleanup that removes only volumes no container references, while keeping named application data out of the blast radius by default.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need access to the Docker daemon and a shell. The examples use Docker CLI 29.8.1 from docker-ce-cli package version 5:29.8.1-1~ubuntu.24.04~noble.
Warning
This is a destructive operation. A pruned volume and its contents are not restored by Docker. Take a backup or export anything you may need before continuing. Docker access is often granted through the docker group; use sudo only if your local setup requires it.
1. Check the command you have installed
Read the local command's help before choosing an option. This is a read-only check:
$ docker volume prune --help
Usage: docker volume prune [OPTIONS]
Remove unused local volumes
Options:
-a, --all Remove all unused volumes, not just anonymous ones
--filter filter Provide filter values (e.g. "label=<label>")
-f, --force Do not prompt for confirmation
Checkpoint
Confirm the client and package version as well:
$ docker version --format '{{.Client.Version}}'
29.8.1
$ dpkg-query -W -f='${Package} ${Version}\n' docker-ce-cli
docker-ce-cli 5:29.8.1-1~ubuntu.24.04~noble
The server must also be reachable. If docker version reports a daemon error, fix the Docker context or daemon access first: do not treat sudo as a repair for a stopped or remote daemon.
2. Understand the default before touching data
- Without
--all, only unused anonymous volumes go. An unused volume is one no container references. Named volumes stay out of this default cleanup, which is why a plaindocker volume prunedoes less than many people expect. - With
--all, named volumes are on the table too. That can include database, uploads and cache volumes created by Compose or another deployment tool. Do not add the flag merely to make the command remove more.
List volumes without changing them:
$ docker volume ls
DRIVER VOLUME NAME
local example_cache
local example_database
For a closer look at what Docker currently considers dangling, use the read-only listing filter:
$ docker volume ls --quiet --filter dangling=true
example_cache
Treat this as an inspection checkpoint, not a reservation. Containers can be created or removed between the listing and the prune, so re-check active deployments immediately before deletion.
3. Check which containers use important volumes
Inspect a volume you recognise before deciding it is disposable:
$ docker volume inspect example_database
[
{
"Name": "example_database",
"Mountpoint": "/var/lib/docker/volumes/example_database/_data"
}
]
The real JSON has more fields and varies by Docker version; name, labels and mountpoint are the useful first checks. For a container-level view, show mounts on running containers:
$ docker ps --format 'table {{.Names}}\t{{.Mounts}}'
NAMES MOUNTS
web example_uploads
database example_database
Also check stopped containers before removing a named volume. A stopped container can still hold the reference, so it does not automatically make the volume a prune candidate, but it may reveal an application due to restart later. If a Compose project owns the volume, inspect that project's configuration and backup policy rather than guessing safety from the volume's name.
4. Use a label filter when ownership is explicit
The prune command accepts key=value filters, and the supported key is label, in both positive and negative form. This targets only unused volumes carrying a deliberate cleanup label:
$ docker volume prune --all --filter 'label=cleanup=approved'
WARNING! This will remove all unused local volumes with the selected filter.
Are you sure you want to continue? [y/N]
Do not press y until the candidate set is understood. The label does not protect a volume from deletion; it selects it for deletion once the volume is unused. Create and maintain such labels as part of the deployment process, not after a cleanup has already gone wrong.
A negative filter like --filter 'label!=retention=keep' selects volumes without that label: an exclusion rule, not a list of volumes explicitly approved for removal. Multiple filters with different keys combine with AND logic; repeating the same key combines its values with OR logic. Keep the command short enough to review. A filter expression is not a substitute for a backup.
5. Run the narrow cleanup interactively
After the checks above, run the default form without --force:
$ docker volume prune
WARNING! This will remove anonymous local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
<volume id or name>
Total reclaimed space: <amount>
Docker asks for confirmation, and any response other than y leaves the volumes in place. The deleted-volume list and reclaimed-space value depend on daemon state, so do not script against a fixed line count or amount.
Checkpoint
List dangling volumes again:
$ docker volume ls --quiet --filter dangling=true
Any remaining entries may be named volumes excluded by the default, volumes created during the operation, or ones that still need a separate review. An empty result is useful, but it is not proof that every application has a current backup.
6. Widen the operation only with an explicit decision
Use --all only when named volumes are part of the intended cleanup and you have checked their contents or retained a recoverable backup:
$ docker volume prune --all --filter 'label=cleanup=approved'
WARNING! This will remove all unused local volumes matching the filter.
Are you sure you want to continue? [y/N]
Warning
This can remove persistent data from an unused database or an old deployment, and there is no Docker undo for a successful prune. Recovery means restoring from a backup or recreating the application from its deployment source. If you cannot name that recovery path, stop at the confirmation prompt.
Do not use --force for a first run. It suppresses the prompt and is meant for a reviewed, non-interactive procedure. If you later automate a filtered prune, log the exact command, keep the filter in source control, and schedule it only after the backup and retention checks have succeeded.
Common traps
- Adding
--allby habit: this changes the operation from anonymous-only cleanup to deletion of unused named volumes too. - Confusing unused with empty: Docker's candidate rule is about container references, not whether the volume directory contains files.
- Trusting a volume name: names such as
cache,dataor a Compose project prefix are not a backup policy. - Assuming the prompt is a preview: it describes the operation, but it does not provide a reversible transaction. Review the host and labels before invoking prune.
- Running as root unnecessarily: use ordinary Docker access when it works. If a privileged invocation is required, make the privilege boundary visible and review the command as carefully as the deletion.
Done means
- Access confirmed. You confirmed the installed Docker CLI and daemon access.
- Scope decided. You checked the default scope and decided whether named volumes are in or out.
- Ownership checked. You inspected important volumes, their labels and their container references.
- Backup in hand. You have a backup or recovery path for every volume that could hold needed data.
- Prune reviewed. You ran an interactive, appropriately filtered prune and reviewed its output.
- Result verified. You checked the remaining dangling-volume list and recorded what was removed.