Clean Up Podman Storage with `system prune` and `system df`

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-5.8.2-5.el10_2.x86_64
Applies to Linux hosts with Podman where you need to inspect local image, container, and volume disk usage and remove unused objects safely
Privilege Rootful examples on the lab host; rootless users prune their own graph root under $HOME/.local/share/containers
Scope podman system df and -v, --format JSON and Go templates, reclaimable-size caveats, podman system prune default scope, --all, --volumes, --filter, --build, --external, per-object container/image/network/volume/pod prune, measuring reclaimed space versus host df, Podman 5.8.2 versus Podman 6 volume prune behavior, transient_store pointer, and a safe cleanup sequence. Does not cover emergency no space left on device recovery, storage corruption reset, or manual deletion inside the graph root.
Related guides Remove containers with podman rm
List containers with podman ps

Before you delete anything, measure what Podman stores locally. podman system df summarizes images, containers, and volumes; podman system prune removes unused objects when you are ready. The two commands answer different questions — usage first, cleanup second.

This guide stays on routine maintenance. Emergency disk-full recovery and storage reset belong in dedicated troubleshooting articles, not broad rm -rf under the graph root.


Check Podman disk usage first

Start with the summary table:

bash
podman system df

Sample output on the lab host:

output
TYPE           TOTAL       ACTIVE      SIZE        RECLAIMABLE
Images         23          0           649.7MB     649.7MB (100%)
Containers     0           0           0B          0B (0%)
Local Volumes  0           0           0B          0B (0%)

Column meanings:

Column Meaning
TOTAL Objects Podman knows about in this category
ACTIVE Objects currently in use; meaning varies by category
SIZE Logical storage attributed to the category
RECLAIMABLE Space Podman estimates you could free with prune commands

For images, active generally means an image has containers associated with it; for containers, active means running; for volumes, active means referenced/in use.

Run podman system df before any prune so you know which category grew. Re-running it after cleanup shows whether the change matched your expectation.


Use verbose disk usage with -v

The summary hides which image or volume actually consumes space. Add -v:

bash
podman system df -v

The verbose report has three sections: Images (one row per local image reference), Containers (writable layer size and attached local volumes), and Local volumes (name, link count, and size).

Sample output (abbreviated from the lab host):

output
REPOSITORY                                   TAG      IMAGE ID      CREATED  SIZE     SHARED SIZE  UNIQUE SIZE  CONTAINERS
localhost/prune-demo                         v1       3390dee6d56a  ...      23.6MB   23.59MB      6.76kB       1
localhost/prune-demo                         v2       4f0c20a680a0  ...      23.6MB   23.59MB      9.33kB       0

CONTAINER ID  IMAGE                    COMMAND  LOCAL VOLUMES  SIZE  CREATED  STATUS   NAMES
f9a361b5dac4  localhost/prune-demo:v1  ...      0              ...   ...      Created  prune-demo-stopped

VOLUME NAME     LINKS  SIZE
prune-vol-test  0      0B

SHARED SIZE is layer data reused by other images. UNIQUE SIZE is data only this reference needs. CONTAINERS counts containers using the image.

Use -v to find the real consumer before you choose image prune versus volume prune.


Reclaimable image size is an estimate

RECLAIMABLE can overstate what a single prune will return. Images share layers — deleting one unused tag does not free a layer another image still references.

After creating two tags built from the same base and one stopped container, re-check the summary:

bash
podman system df

Sample output (images row on the lab host):

output
Images         25          1           649.7MB     649.7MB (100%)

One image was ACTIVE (referenced by the stopped container), yet the summary reported 100% reclaimable on the image row. Treat RECLAIMABLE as guidance, not a promise that exactly that many bytes will return to the filesystem.


Get machine-readable disk usage

Scripts can parse structured output instead of the human table:

bash
podman system df --format json

Sample output (trimmed):

output
[
    {"Type":"Images","Total":23,"Active":0,"RawSize":649718690,"RawReclaimable":649718690,"Size":"649.7MB","Reclaimable":"649.7MB (100%)"},
    {"Type":"Containers","Total":0,"Active":0,"RawSize":0,"RawReclaimable":0,"Size":"0B","Reclaimable":"0B (0%)"},
    {"Type":"Local Volumes","Total":0,"Active":0,"RawSize":0,"RawReclaimable":0,"Size":"0B","Reclaimable":"0B (0%)"}
]

A Go template line per category works for simple reporting:

bash
podman system df --format '{{.Type}} {{.Size}} {{.Reclaimable}}'

Sample output:

output
Images 649.7MB 649.7MB (100%)
Containers 0B 0B (0%)
Local Volumes 0B 0B (0%)

This article stops at occasional checks — not a full monitoring pipeline.


What podman system prune removes by default

Default prune asks for confirmation. On Podman 5.8.2 the prompt lists stopped containers, unused networks, dangling images, and dangling build cache — unused pods are removed too even when the warning omits them:

bash
podman system prune

Sample prompt on Podman 5.8.2:

output
WARNING! This command removes:
	- all stopped containers
	- all networks not used by at least one container
	- all dangling images
	- all dangling build cache

Are you sure you want to continue? [y/N]

Pass -f to skip the prompt in scripts:

bash
podman system prune -f

Sample result after a stopped container and a dangling image existed:

output
Deleted Containers
f9a361b5dac4f0bb7e1929dae25ee493a998c8d6a5c562f8088173f39de7f74b
Deleted Images
4ecb1a33d817bc73db7d9ae33eca7a0c5c4e1ba3eeff4569aa62a6a8e50c71be
Deleted Networks
podman-default-kube-network
Total reclaimed space: 156MB

Tagged images such as localhost/prune-demo:v1 and named volumes were left intact. Volumes are not in the default scope — that protects persistent data. Unused pods are included in default system prune even when the confirmation prompt does not list them.


Remove all unused images with --all

--all extends image cleanup to every image without an associated container, not only dangling <none> layers:

bash
podman system prune --all

Sample warning:

output
WARNING! This command removes:
	- all stopped containers
	- all networks not used by at least one container
	- all images without at least one container associated with them
	- all build cache

Are you sure you want to continue? [y/N]

On the lab host podman system prune --all -f removed thirteen image IDs including both prune-demo tags and reported Total reclaimed space: 767.7MB. Volumes were still present afterward.

--all does not imply volume deletion. Add --volumes only when you intend to prune unused volumes too.


Prune volumes with system prune

Named data needs an explicit flag:

bash
podman system prune --volumes

Sample warning:

output
WARNING! This command removes:
	- all stopped containers
	- all networks not used by at least one container
	- all volumes not used by at least one container
	- all dangling images
	- all dangling build cache

Are you sure you want to continue? [y/N]

Combine both aggressive image and volume cleanup only after review:

bash
podman system prune --all --volumes -f

Unused means not referenced by any container right now — not safe to delete. A named volume holding database files can show LINKS 0 in podman system df -v while still being data you need. List volumes and back up before --volumes:

bash
podman volume ls

Use filters on system prune

Limit cleanup by age:

bash
podman system prune --filter until=24h -f

Label filters work when your objects carry metadata:

bash
podman system prune --filter label=temporary=true -f

Filters apply to the prune operation as a whole; they do not behave identically for every resource type. Podman 5.8.2 does not offer --dry-run on system prune — test with a narrow per-object prune or a disposable lab host first.


Clean up stale build containers with --build

Interrupted builds can leave build containers behind:

bash
podman system prune --build -f

Upstream warns that --build can interfere with active builds. Run it only when no build is in progress — not on every scheduled cleanup.


podman system prune --external

--external is a different mode, not a stronger system prune:

bash
podman system prune --external -f

It removes storage data under the graph root that current Podman metadata does not reference — for example after an unclean shutdown or leftover writable layers when transient_store is enabled. On a healthy host the command may print nothing and exit successfully.

It cannot combine with normal prune options:

bash
podman system prune --external --all

Sample error:

output
Error: system prune --external cannot be combined with other options

Do not pass --all, --filter, or --volumes with --external.


--external and transient_store

When transient_store = true in storage.conf, container metadata is intentionally temporary and writable layers can survive a reboot. Upstream documentation recommends podman system prune --external during boot for that configuration. See Podman storage location for graph-root layout — do not enable transient_store just to exercise this flag on a normal server.


Per-object prune commands

Narrow cleanup is easier to reason about than a broad system prune:

Resource Command
Stopped containers podman container prune
Images podman image prune
Networks podman network prune
Volumes podman volume prune
Stopped pods podman pod prune
Combined default set podman system prune

If only old images are the problem, podman image prune is safer than system prune --all until you have checked podman system df -v.


podman image prune

Default image prune removes dangling images only:

bash
podman image prune

Sample warning:

output
WARNING! This command removes all dangling images.

Add --all to drop every image not used by a container:

bash
podman image prune --all

Sample warning:

output
WARNING! This command removes all images without at least one container associated with them.

Tag, inspect, and history workflows live in Manage images with podman images.


podman container prune

Remove all non-running containers:

bash
podman container prune

Sample warning:

output
WARNING! This will remove all non running containers.

Confirm with podman ps -a first — stopped containers you plan to restart are still candidates. Filters such as until= and labels are supported on container prune.


podman network prune

Delete custom networks with no attached containers:

bash
podman network prune -f

Sample output:

output
prune-unused-net

Built-in networks such as podman are not removed like an unused custom bridge. Network design details belong in the networking articles.


Volume prune: Podman 5.8.2 versus Podman 6

Version behavior diverges — copy commands from the docs that match your installed Podman.

Podman 5.8.2 (this lab)

podman volume prune removes every volume not used by at least one container, including named volumes:

bash
podman volume prune

Sample prompt listing names before confirm:

output
WARNING! This will remove all volumes not used by at least one container. The following volumes will be removed:
prune-vol-test
6ac7c48e21bfa76c1df2e14339bea4031423d1cc4486ee2d1918e72a1882ff46
Are you sure you want to continue? [y/N]

After -f, both the named volume and the anonymous hash-named volume were deleted. There is no --all or --dry-run on volume prune in 5.8.2.

Podman 6 and later

Default podman volume prune removes unused anonymous volumes only. Named unused volumes require podman volume prune --all. Newer releases may add --dry-run to preview deletions. Verify with podman volume prune --help on the host you administer — do not assume 6.x behavior on a 5.x machine.

Volume create, mount, and backup patterns are covered in Podman volumes.


Measure reclaimed space

Compare Podman logical totals with filesystem free space before and after cleanup.

Check the graph root filesystem:

bash
df -h /var/lib/containers/storage

Sample output on the lab host:

output
Filesystem             Size  Used Avail Use% Mounted on
/dev/mapper/rhel-root   25G   25G  717M  98% /

Record Podman usage:

bash
podman system df

Run your chosen prune, then repeat both commands. Logical Total reclaimed space from prune and df free-space delta can differ because of shared layers, filesystem allocation, open deleted files, and unrelated host activity. Small prunes may show 0B reclaimed in Podman while the graph root is still nearly full from other data.


Do not manually delete overlay directories

Never free space with:

bash
rm -rf /var/lib/containers/storage/overlay/*

Rootless stores use paths under $HOME/.local/share/containers — the same rule applies. Hand-deleting layer directories desynchronizes metadata from on-disk storage.

Use podman rm, podman rmi, podman volume rm, and prune subcommands. If metadata is already broken, follow Podman storage corruption and reset. If the filesystem is completely full, see Fix Podman no space left on device.


Routine maintenance on Podman 5.8.2:

  1. Run podman system df -v and identify which type — images, containers, or volumes — grew.
  2. Prefer per-object prune (image prune, container prune, volume prune) when only one category is the problem.
  3. Re-run podman system df and confirm the summary changed as expected.
  4. Use podman system prune when you want the default bundle (stopped containers, unused pods, unused networks, dangling images, dangling build cache).
  5. Add --all only when tagged unused images should go.
  6. Add --volumes only after podman volume ls and backups — unused named data is still data.
  7. Use --build only when no build is running.
  8. Reserve --external for orphan storage after transient store or unclean shutdown — never combined with --all.

That sequence keeps inspection before deletion and matches how Podman 5.8.2 actually scopes each flag.


Cleanup

If you created lab fixtures during practice, remove leftover objects with targeted commands rather than editing storage by hand:

bash
podman rm -f prune-demo-stopped 2>/dev/null
podman volume rm prune-vol-test 2>/dev/null
podman rmi localhost/prune-demo:v1 localhost/prune-demo:v2 2>/dev/null

Summary

podman system df shows where Podman disk usage lives; podman system prune removes unused objects when you choose the right flags. Always measure with df and -v before pruning — RECLAIMABLE can overstate frees when images share layers.

Default system prune clears stopped containers, unused pods, unused networks, dangling images, and dangling build cache but keeps tagged unused images and all volumes. --all adds unused tagged images; --volumes is separate and dangerous for named data. On Podman 5.8.2, volume prune already targets unused named volumes — Podman 6 narrows the default unless you pass --all. --external cleans orphan storage and cannot mix with normal prune options.

Never delete overlay trees manually. For corruption or a full disk, use the dedicated troubleshooting guides instead of broad rm -rf under the graph root.


References

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)