← Back to list

Podman Volumes: Managing Persistent Data (4/5)

Containers are disposable by design. Stop one, remove it, and everything written inside its writable layer disappears with it. That’s a…

Om Parkash Sarangani · 2026-06-29 17:32 · 0 claps · 3.0 min read
#podman #containers #docker-volume #data-persistence #containerization
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud

Podman Volumes: Managing Persistent Data (4/5)

Containers are disposable by design. Stop one, remove it, and everything written inside its writable layer disappears with it. That’s a feature for stateless workloads — and a serious problem the moment you run a database, an upload directory, or anything else that needs to survive a restart.

This part of the series covers how Podman handles persistent storage: named volumes, bind mounts, the permission issues that trip up almost everyone on SELinux systems, and how to share data safely between containers.

Why Containers Need Volumes

A container’s filesystem is a writable layer stacked on top of a read-only image. When the container is removed, that writable layer goes with it. Volumes solve this by attaching storage that lives outside the container’s lifecycle.

Podman gives you two main approaches:

Type Managed by Best for Named volume Podman Databases, application data, anything you want Podman to track and back up Bind mount You (host path) Local development, injecting config files, sharing a specific host directory

1. Named Volumes

Creating a Volume

podman volume create app-data

Podman creates the volume in its managed storage location and confirms the name.

Inspecting and Listing Volumes

podman volume ls
podman volume inspect app-data

inspect returns the mount point, driver, and labels as JSON.

Mounting a Named Volume

podman run -d --name postgres \
  -v app-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:16

If app-data doesn't exist yet, Podman creates it automatically — you don't have to run volume create first.

Verifying Persistence

podman stop postgres
podman rm postgres

# Recreate the container with the same volume
podman run -d --name postgres \
  -v app-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:16

The data is still there. The container was disposable; the volume wasn’t.

2. Bind Mounts

Bind mounts map a specific host directory directly into the container — useful when you need direct access to source code, config files, or logs.

podman run -d --name web \
  -v ~/site-config:/usr/share/nginx/html:ro \
  -p 8080:80 \
  nginx:alpine

The :ro suffix mounts it read-only. Unlike named volumes, the host path must already exist — Podman will not create it for you.

3. The SELinux Permission Problem

If you’re on Fedora, RHEL, or another SELinux-enforcing system, this is the error almost everyone hits eventually:

permission denied

…even though the file permissions look fine. SELinux labels are the real culprit, and Podman has a built-in fix: relabeling suffixes on the volume mount.

Suffix Meaning :z (lowercase) Shared label — multiple containers can read/write this volume :Z (uppercase) Private label — only this container can access it

podman run -d --name secure-app \
  -v ~/private-config:/config:ro,Z \
  nginx:alpine

If two containers need to share the same bind-mounted directory, use :z instead — :Z will lock the other container out.

4. Sharing a Volume Between Containers

Named volumes aren’t tied to a single container — multiple containers can mount the same one simultaneously.

podman volume create shared-data

# Writer
podman run -d --name writer \
  -v shared-data:/output \
  alpine sh -c "while true; do date >> /output/log.txt; sleep 5; done"
# Reader
podman run -d --name reader \
  -v shared-data:/input:ro \
  alpine tail -f /input/log.txt
podman exec reader cat /input/log.txt

This pattern is common for log shippers, sidecar exporters, and any setup where one container produces data another consumes.

5. The --mount Flag (the Explicit Alternative)

The -v shorthand is convenient, but --mount is more readable for complex configurations — especially in scripts and CI pipelines:

podman run -d --name app \
  --mount type=volume,source=app-data,target=/data \
  alpine

Bind mount with --mount:

podman run -d --name app \
  --mount type=bind,source=/home/user/config,target=/etc/app/config,readonly=true \
  alpine

Both flags do the same job — pick -v for quick one-liners, --mount when readability matters more than brevity.

Conclusion

Volumes are what turn Podman from “a way to run disposable processes” into something you can actually trust with production data. The pattern to default to: named volumes for anything you want Podman to manage and back up, bind mounts for config and development workflows, and :Z/:z whenever SELinux throws permission errors at you.

In the final part of this series, we’ll bring networking, volumes, and multi-container setups together using pods and Podman’s systemd integration — running container groups as proper, declarative system services.

Further reading:

  1. Podman volume documentation
  2. Podman — mount option reference

메타데이터
post_id
7b6dd83b166f
slug
podman-volumes-managing-persistent-data-4-5-7b6dd83b166f
url
https://medium.com/@parkashsarangani/podman-volumes-managing-persistent-data-4-5-7b6dd83b166f
canonical_url
https://medium.com/@parkashsarangani/podman-volumes-managing-persistent-data-4-5-7b6dd83b166f
author_url
https://medium.com/@parkashsarangani
status
ok
fetched_at
2026-07-13 06:23:13