Podman Volumes: Create, Mount, Backup and Manage Persistent Storage

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-5.8.2-5.el10_2.x86_64
Applies to Any Linux host with Podman installed
Privilege Rootful examples on the lab host; most volume commands work rootless, but host-side podman volume mount may require podman unshare
Scope Creating and managing Podman-managed volumes — volume create/ls/inspect/rm/prune, named vs anonymous lifecycle, mounting with -v and --mount, copy-up, --uid/--gid, labels, host volume mount/unmount, volume export/import, and Podman 5 vs 6 prune behavior. Does not cover bind-mount choice depth, SELinux or UID permission troubleshooting, or storage.conf layout.
Related guides Install Podman on RHEL

A Podman volume is persistent storage Podman manages independently of any single container. Application data in a named volume survives container removal; another container can mount the same volume by name. This guide walks through creating volumes, mounting them, backing them up, and cleaning them up — without diving into bind mounts, SELinux labels, or storage-driver internals.


What is a Podman volume?

When a container writes under a mounted volume path, data lands in Podman-managed storage instead of the container writable layer:

text
Container
   │ /data
Podman named volume
Host-managed volume storage

The writable layer is tied to one container and disappears when you remove that container (unless you commit it to a new image). A named volume is a separate object:

  • You refer to it by name (podman-volumes-data), not by guessing paths under Podman storage
  • It can outlive any one container
  • Another container can mount the same volume at the same or a different path
  • Podman chooses and manages the host location — you normally use podman volume inspect when you need the mountpoint

Image archives from podman save do not include volume data. For that distinction, see Podman save, load, export and import.


Create a Podman volume

Create a named volume before you run a container:

bash
podman volume create podman-volumes-data

Podman prints the volume name and stores a local driver volume. No container starts at this step.

List volumes to confirm:

bash
podman volume ls

Sample output (truncated):

output
DRIVER      VOLUME NAME
local       podman-volumes-data
...

local is the default driver on a normal Linux host. This guide focuses on the local driver; Podman can also use other supported volume drivers or configured volume plugins, whose backend-specific behavior is outside this lifecycle guide. Creating a volume prepares the storage but does not populate application data by itself. On first container use, Podman's default copy-up behavior may seed an empty volume with files already present at the image's mount destination; otherwise data appears when a workload writes to the volume.

Re-run creation safely in scripts with --ignore:

bash
podman volume create --ignore podman-volumes-data

When the volume already exists, Podman prints the existing name and exits successfully. New options on that command are not applied to an existing volume — change labels or ownership only at first create, or remove and recreate the volume if you truly need different settings.


List Podman volumes

The default table shows driver and name:

bash
podman volume ls

For scripts, quiet mode prints names only:

bash
podman volume ls --quiet

Sample output:

output
podman-volumes-data
podman-run-data
...

Filter by name

Narrow a long list to volumes whose names match a substring:

bash
podman volume ls --filter name=podman-volumes

Sample output:

output
DRIVER      VOLUME NAME
local       podman-volumes-data

Find unused volumes

Volumes not referenced by any container are dangling:

bash
podman volume ls --filter dangling=true

Sample output:

output
DRIVER      VOLUME NAME
local       854477349e9801ec10831a9c3ab3634c6a0de87317e826a9520fd8dc4064b58b
...

Long hash-like names often belong to anonymous volumes; use the .Anonymous field when you need to distinguish them reliably.

Filter by label

Labels set at create time are queryable metadata (not SELinux filesystem labels):

bash
podman volume ls --filter label=environment=test

Sample output:

output
DRIVER      VOLUME NAME
local       podman-labeled-volume

Named vs anonymous in the list

On Podman 5.8.2, podman volume ls does not accept an anonymous= filter. Use a custom format or inspect instead:

bash
podman volume ls --format '{{.Name}} anonymous={{.Anonymous}}'

Sample output:

output
854477349e9801ec10831a9c3ab3634c6a0de87317e826a9520fd8dc4064b58b anonymous=true
podman-volumes-data anonymous=false

Volumes with anonymous=true are anonymous volumes; anonymous=false identifies named volumes. Long hash-like names are common for anonymous volumes, but use the field rather than the name format when classification matters.


Inspect a Podman volume

podman volume inspect returns JSON metadata for one volume:

bash
podman volume inspect podman-volumes-data

Sample output:

output
[
     {
          "Name": "podman-volumes-data",
          "Driver": "local",
          "Mountpoint": "/var/lib/containers/storage/volumes/podman-volumes-data/_data",
          "CreatedAt": "2026-08-22T22:45:50.252368481+05:30",
          "Labels": {},
          "Scope": "local",
          "Options": {},
          "MountCount": 0,
          "NeedsCopyUp": true,
          "NeedsChown": true,
          "LockNumber": 0
     }
]

Fields worth knowing:

  • Name — volume identifier you pass to -v and podman volume subcommands
  • Driver — normally local
  • Mountpoint — host path to volume data (rootful layout shown above)
  • Labels — Podman metadata from --label at create time
  • MountCount — how many active mounts Podman currently tracks for the volume
  • NeedsCopyUp / NeedsChown — internal flags for first-use copy-up and ownership adjustment
  • Anonymoustrue for auto-generated anonymous volumes (visible on inspect even when not shown in default ls output)

Do not use MountCount to decide whether a volume is referenced by a container; podman volume rm behavior demonstrates that separately.

Pull just the mountpoint for scripts:

bash
podman volume inspect --format '{{.Mountpoint}}' podman-volumes-data

Sample output:

output
/var/lib/containers/storage/volumes/podman-volumes-data/_data

That path is the right place to understand where data lives. For why rootful and rootless paths differ, and how storage.conf relocates storage, see Podman storage location — this article does not unpack graph-root layout.


Mount a named volume into a container

Write data through a container mount:

bash
podman run --rm -v podman-volumes-data:/data docker.io/library/alpine:3.20 sh -c 'echo "persistent data" > /data/example.txt'

Start a second container that reads the same volume:

bash
podman run --rm -v podman-volumes-data:/data docker.io/library/alpine:3.20 cat /data/example.txt

Sample output:

output
persistent data

The first container exited with --rm, but the named volume and its file remain. For one-off transfers between the host and a container path — including while the container is stopped — use copy files with podman cp instead of exec or a temporary mount.

The syntax breaks down as:

text
podman-volumes-data:/data
│                   │
│                   └── path inside the container
└── Podman volume name

Basic -v usage also appears in Run containers with podman run.


Named vs anonymous Podman volumes

Named volume

You choose the source name explicitly:

bash
podman run --rm -v podman-volumes-data:/data docker.io/library/alpine:3.20 echo ok

A named volume:

  • Has a stable name you can list and back up
  • Persists after the container exits
  • Is not removed by podman run --rm alone
  • Is removed only when you podman volume rm it (or prune it while unused)

Anonymous volume

Omit the source name and supply only a container path:

bash
podman run --rm -v /data docker.io/library/alpine:3.20 sh -c 'echo anon > /data/anon.txt'

Podman creates a randomly named volume internally. It still uses the volume driver — this is not a bind mount. With --rm, the anonymous volume is discarded when the container exits. Without --rm, the anonymous volume remains after the container exits. Removing the container normally does not remove it; use podman rm --volumes CONTAINER to remove anonymous volumes associated with that container, or prune them later. Named volumes are excluded from both container --rm and podman rm --volumes.

List volumes after creating anonymous ones without --rm and you will see hash-like names with anonymous=true in inspect output. That is expected — assign a name up front when you need data to survive and be easy to find.


Automatically create a named volume with -v

If the named volume does not exist, Podman creates it on first use:

bash
podman run --rm -v automatically-created:/data docker.io/library/alpine:3.20 sh -c 'echo auto > /data/auto.txt'

Confirm the new volume object:

bash
podman volume ls --filter name=automatically-created

Sample output:

output
DRIVER      VOLUME NAME
local       automatically-created

For automation that must not silently create storage, create the volume explicitly first:

bash
podman volume create automatically-created

Podman 6 adds a :nocreate mount option so podman run -v myvolume:/data:nocreate fails when myvolume is missing instead of creating it. That option is not available on the Podman 5.8.2 host used for this article (invalid option type "nocreate"). See Podman 5 to 6 migration when your distribution ships Podman 6.


-v vs --mount in Podman

Both attach a Podman-managed volume. Pick based on readability, not performance — both configure the same mount for the runtime.

-v

The short form names the volume and container path in one argument:

bash
podman run --rm -v podman-volumes-data:/data docker.io/library/alpine:3.20 echo ok

Short and familiar if you already use Docker-style examples.

--mount

The structured form spells out type, source, and destination separately:

bash
podman run --rm --mount type=volume,src=podman-volumes-data,dst=/data docker.io/library/alpine:3.20 echo ok

Structured key/value syntax — easier to read when several mount options appear together.

Need Recommended syntax
Simple named volume -v is concise
Several mount options in one mount --mount is clearer
Scripts that spell out type=volume --mount
Familiar Docker/Podman examples Either works

Bind mounts, SELinux relabel options (:z, :Z), and permission failures belong in Bind mount vs volume and Fix Podman volume permission denied — not repeated here.


Read-only Podman volumes

Mount existing data read-only with :ro:

bash
podman run --rm -v podman-volumes-data:/data:ro docker.io/library/alpine:3.20 cat /data/example.txt

Sample output:

output
persistent data

A write attempt fails at the filesystem level:

bash
podman run --rm -v podman-volumes-data:/data:ro docker.io/library/alpine:3.20 sh -c 'echo fail > /data/ro-test.txt'

Sample output:

output
sh: can't create /data/ro-test.txt: Read-only file system

The same restriction in --mount form:

bash
podman run --rm --mount type=volume,src=podman-volumes-data,dst=/data,ro docker.io/library/alpine:3.20 cat /data/example.txt

How named volume copy-up works

When a new named volume mounts over a directory that already contains files in the image, Podman can copy image contents into the volume on first use. That seeds configuration or default data without baking it into the volume object ahead of time.

Build a small image with content under /seed:

bash
podman build -t localhost/copyup-demo:v1 -f - . <<'EOF'
FROM docker.io/library/alpine:3.20
RUN mkdir -p /seed && echo seeded-content > /seed/default.txt
EOF

Create a fresh volume and mount it over /seed:

bash
podman volume create podman-copy-demo

Mount the empty volume over the image directory that already contains default.txt:

bash
podman run --rm -v podman-copy-demo:/seed localhost/copyup-demo:v1 cat /seed/default.txt

Sample output:

output
seeded-content

The image file was copied into the empty volume on first mount. Copy-up runs once per volume when applicable — not on every subsequent container start.

Disable copy-up with the nocopy mount option on Podman 5.8.2:

bash
podman volume create podman-nocopy-demo
bash
podman run --rm \
  -v podman-nocopy-demo:/seed:nocopy \
  localhost/copyup-demo:v1 \
  ls -la /seed

Sample output:

output
total 8
drwxr-xr-x    2 root     root          4096 Mar 15  2024 .
drwxr-xr-x    1 root     root          4096 Mar 15  2024 ..

nocopy disables the normal first-use copy-up, so image files already present under /seed are hidden by the empty volume rather than copied into it.

Inspect the volume directly on the host when you need to verify files without a container:

bash
podman volume mount podman-copy-demo

Sample output:

output
/var/lib/containers/storage/volumes/podman-copy-demo/_data

List the directory, then release the host mount:

bash
ls -la /var/lib/containers/storage/volumes/podman-copy-demo/_data

Release the host mount when you are finished inspecting files:

bash
podman volume unmount podman-copy-demo

Set volume ownership with --uid and --gid

Set ownership applied when the volume mountpoint is created:

bash
podman volume create --uid 1000 --gid 1000 podman-owned-volume

Verify the stored values:

bash
podman volume inspect podman-owned-volume --format 'UID={{.UID}} GID={{.GID}} Name={{.Name}}'

Sample output:

output
UID=1000 GID=1000 Name=podman-owned-volume

--uid and --gid on podman volume create are not the same as driver mount options such as --opt o=uid=1000, which pass filesystem mount options to the local driver. When processes inside the container cannot read or write the volume, the diagnostic tree lives in Fix Podman volume permission denied — not in this volume lifecycle guide.


Volume labels

Attach metadata at create time:

bash
podman volume create --label application=demo --label environment=test podman-labeled-volume

Filter the list by label:

bash
podman volume ls --filter label=environment=test

These labels are Podman object metadata for automation and filtering. They are unrelated to SELinux security contexts on the mounted files — both happen to use the word "label" in different contexts.


Mount and unmount a volume on the host

podman volume mount exposes the volume directory on the host for inspection or maintenance:

bash
podman volume mount podman-volumes-data

Sample output:

output
/var/lib/containers/storage/volumes/podman-volumes-data/_data

Read or back up files at that path, then unmount:

bash
podman volume unmount podman-volumes-data

Prefer these commands over guessing paths under Podman storage. In rootless mode, podman volume mount generally needs to run inside the Podman user namespace. Enter it with podman unshare, then run podman volume mount VOLUME. Podman 5.8.2 documents file volumes as an exception. See Podman unshare when you need namespace-aware file operations.


Backup a Podman volume

Export volume contents to a tar archive:

bash
podman volume export --output podman-volumes-data.tar podman-volumes-data

List archive members:

bash
tar -tf podman-volumes-data.tar

Sample output:

output
example.txt

podman volume export archives files currently in the volume. For databases and other buffered applications, stop or quiesce the workload before export if you need a consistent on-disk snapshot. This section shows the mechanics — not database-specific backup procedures.


Restore a Podman volume

The destination volume must exist before import:

bash
podman volume create restored-volume

Import the archive:

bash
podman volume import restored-volume podman-volumes-data.tar

Verify by mounting into a container:

bash
podman run --rm -v restored-volume:/data docker.io/library/alpine:3.20 cat /data/example.txt

Sample output:

output
persistent data

podman volume import merges archive paths into the volume. It does not truncate the destination first. Add a file to the volume, then import a second archive:

bash
podman run --rm -v restored-volume:/data docker.io/library/alpine:3.20 sh -c 'echo existing > /data/keep.txt'

Build a small tar that adds only archive-only.txt:

bash
echo 'archive-only' > /tmp/archive-only.txt && tar -cf /tmp/merge-test.tar -C /tmp archive-only.txt

Import the second archive into the same volume:

bash
podman volume import restored-volume /tmp/merge-test.tar

After import, both the original example.txt, the locally added keep.txt, and archive-only.txt from the archive coexist. Overlapping paths take the archive content. Plan imports accordingly when you expected a full replace.


Copy data directly between volumes

Stream export into import when both volumes already exist:

bash
podman volume create old-volume

Create the destination volume before you import into it:

bash
podman volume create new-volume

Write a file into the source volume:

bash
podman run --rm -v old-volume:/data docker.io/library/alpine:3.20 sh -c 'echo old-data > /data/file.txt'

Pipe export output straight into import on the destination:

bash
podman volume export old-volume | podman volume import new-volume -

Confirm the copy:

bash
podman run --rm -v new-volume:/data docker.io/library/alpine:3.20 cat /data/file.txt

Sample output:

output
old-data

new-volume must be created before the import on stdin — the same rule as file-based import.


Remove a Podman volume

Delete an unused volume by name:

bash
podman volume rm podman-volumes-data

If a container still references the volume, removal fails:

output
Error: volume podman-volumes-data is being used by the following container(s): 2977b356d041: volume is being used

Stop and remove the container first, then remove the volume. podman volume rm --force is much more destructive: if containers use the volume, Podman removes those containers first and then removes the volume. Prefer removing or recreating the dependent containers explicitly so you know what is being deleted.


Prune unused Podman volumes

Unused volumes accumulate from forgotten containers and anonymous mounts. Prune behavior changed between major versions — confirm your Podman release before running this in production.

Podman 5.x (including 5.8.2 on this lab host)

On Podman 5.x, confirm the prompt carefully — prune is aggressive:

bash
podman volume prune

removes all unused volumes after confirmation, including unused named volumes. The help text states: "Remove all unused volumes. Note all data will be destroyed." On Podman 5.x, treating volume prune as safe housekeeping for only anonymous volumes is a mistake — named application data can be deleted if no container references it.

Podman 6.x

Podman 6 aligns closer to Docker defaults:

  • podman volume prune removes only unused anonymous volumes
  • podman volume prune --all includes unused named volumes
  • podman volume prune --dry-run previews candidates without deleting

If you migrate from Podman 5 to 6, re-read prune semantics — see Podman 5 to 6 migration.


Podman volume vs container writable layer

Container writable layer Podman volume
Tied to one container instance Independent storage object
Removed with the container (unless committed to an image) Can survive container removal
Suited to temporary runtime changes Suited to persistent application data
Hard to reuse across containers intentionally Mount the same name from any container

Volumes solve persistence inside Podman-managed storage. Bind mounts, which attach an arbitrary host directory, are a separate mount type — see Bind mount vs volume when you need that comparison.


References

Summary

Podman volumes are named storage objects managed separately from container filesystems. You created podman-volumes-data, mounted it with -v podman-volumes-data:/data, and proved persistence by reading the same file from a second container after the first exited with --rm.

Named volumes use explicit names and survive ordinary container removal. Anonymous volumes appear as hash-like names, carry Anonymous=true in inspect output, and disappear with podman run --rm when only a container path was specified. Both -v and --mount type=volume attach the same driver-backed storage; choose whichever reads clearer in your script.

Copy-up seeds image directories into a new volume on first mount — useful for default configuration trees. Back up with podman volume export and restore with podman volume import into a volume that already exists; import merges rather than replacing the destination wholesale. Use podman volume mount when you need host-level access without starting a container.

On Podman 5.8.2, podman volume prune deletes every unused volume after confirmation, named or anonymous. Podman 6 narrows the default prune to anonymous volumes. Check your version before pruning on a host that holds production data, and route permission or SELinux mount failures to the dedicated troubleshooting guides rather than guessing at volume paths under storage roots.


Frequently Asked Questions

1. What is the difference between a Podman volume and a container writable layer?

The writable layer lives with one container and is removed when that container is deleted unless you commit it to a new image. A Podman volume is a separate storage object you manage with podman volume commands. It can survive container removal and mount into another container by name.

2. Does podman volume import replace all data in the destination volume?

No. podman volume import merges archive files into the existing volume. Paths from the archive overwrite matching paths in the destination. It does not empty the volume first, so leftover files remain unless you remove them separately.

3. Can I back up a database volume while the container is running?

podman volume export captures the on-disk files in the volume at export time. For databases and other applications that buffer writes, stop or quiesce the workload first if you need a transaction-consistent backup. The export command itself does not freeze application I/O.

4. What happens to anonymous volumes when I use podman run --rm?

Podman creates an anonymous volume for a mount like -v /data with no source name. With --rm, that anonymous volume is removed when the container exits. Named volumes are separate objects and are not removed by --rm alone.

5. Does podman volume prune delete named volumes on Podman 5?

On Podman 5.x, podman volume prune removes all unused volumes, including unused named volumes, after you confirm the prompt. Podman 6 changed the default to prune only unused anonymous volumes unless you pass --all. Check your version before running prune in production.
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)