The most surprising thing about Podman volumes is how much they obscure the underlying filesystem, making container data management feel like magic until it’s not.

Let’s see this in action. Imagine we have a simple Nginx container storing its web content in a volume.

# Create a directory on the host to act as our volume source
mkdir -p /tmp/nginx_html

# Create a simple index.html file
echo "<h1>Hello from Podman Volume!</h1>" > /tmp/nginx_html/index.html

# Run an Nginx container, mounting the host directory as a volume
podman run -d --name my-nginx -p 8080:80 -v /tmp/nginx_html:/usr/share/nginx/html:Z nginx

# Verify the content is served
curl http://localhost:8080

You’ll see <h1>Hello from Podman Volume!</h1>. Now, let’s change the file on the host:

echo "<h1>Updated Content!</h1>" > /tmp/nginx_html/index.html
curl http://localhost:8080

The change is reflected immediately. This is the core idea: the volume acts as a persistent, shared filesystem location that Podman manages, decoupling data from the container’s lifecycle.

The Problem Volumes Solve

Containers are designed to be ephemeral. When a container stops, its filesystem changes are lost by default. This is great for stateless applications, but for anything that needs to retain data – databases, web server content, configuration files – we need persistence. Volumes provide this by mapping a directory on the host machine (or a managed volume managed by Podman) to a directory inside the container. This way, data written to the volume directory inside the container is actually written to the host, and it persists even after the container is removed.

Internal Mechanics: Bind Mounts vs. Named Volumes

Podman offers two primary ways to manage volumes:

  1. Bind Mounts: This is what we saw above. You explicitly tell Podman to map a specific directory from your host machine (e.g., /tmp/nginx_html) to a directory inside the container (e.g., /usr/share/nginx/html). The :Z or :z flag is crucial for SELinux-enabled systems, ensuring correct security contexts for shared files. :Z allows shared access, while :z is for private access.

    • Diagnosis Command: podman inspect my-nginx
    • What to look for: Under the Mounts section, you’ll see Type: bind, Source: /tmp/nginx_html, and Destination: /usr/share/nginx/html.
  2. Named Volumes: Podman can create and manage its own volumes, independent of specific host directories. You give the volume a name, and Podman handles where it’s stored on the host (typically under /var/lib/containers/storage/volumes/ or similar). This is generally preferred for data that doesn’t need direct host access or for better portability.

    • Command to create a named volume: podman volume create my-data-volume
    • Command to run a container with a named volume: podman run -d --name my-db -v my-data-volume:/var/lib/mysql mysql
    • Diagnosis Command: podman volume inspect my-data-volume
    • What to look for: Name: my-data-volume, Mountpoint: /var/lib/containers/storage/volumes/my-data-volume/_data (the exact path varies by system).

Backup and Restore Strategies

With data residing in volumes, backing it up means backing up the contents of those volume directories.

1. Backing Up Bind Mounts

Since bind mounts point directly to host directories, standard Linux backup tools work perfectly.

  • Diagnosis: Identify the source directory of your bind mount.

    • Command: podman inspect <container_name_or_id>
    • Look for: The Source field within the Mounts section.
  • Backup Command: Use tar to create an archive of the source directory.

    • Example: If the source is /home/user/my_app_data:
      tar -czvf /backup/my_app_data_backup_$(date +%Y%m%d_%H%M%S).tar.gz /home/user/my_app_data
      
    • Why it works: This creates a compressed archive of all files and subdirectories within the specified host directory, capturing the exact state of your persistent data.
  • Restore Command: Stop the container, remove the old data (or ensure the target directory is clean), and then extract the backup into the source directory.

    • Example:
      podman stop <container_name_or_id>
      rm -rf /home/user/my_app_data/* # Be careful!
      tar -xzvf /backup/my_app_data_backup_YYYYMMDD_HHMMSS.tar.gz -C /home/user/my_app_data
      podman start <container_name_or_id>
      
    • Why it works: This replaces the contents of the volume’s source directory on the host with the data from your backup archive.

2. Backing Up Named Volumes

Named volumes are a bit more abstract as Podman manages their location.

  • Diagnosis: Find the host path where Podman stores the named volume.

    • Command: podman volume inspect <volume_name>
    • Look for: The Mountpoint field.
  • Backup Command: Similar to bind mounts, use tar on the Mountpoint.

    • Example: If podman volume inspect my-data-volume shows Mountpoint: /var/lib/containers/storage/volumes/my-data-volume/_data:
      tar -czvf /backup/my-data-volume_backup_$(date +%Y%m%d_%H%M%S).tar.gz /var/lib/containers/storage/volumes/my-data-volume/_data
      
    • Why it works: This archives the actual data Podman is managing for that named volume.
  • Restore Command: Stop containers using the volume, clear the volume’s data directory (or remove and recreate the volume), and extract the backup.

    • Example:
      podman stop <container_name_or_id_using_volume>
      # Option A: Clear existing data (if volume already exists)
      rm -rf /var/lib/containers/storage/volumes/my-data-volume/_data/* # Be careful!
      tar -xzvf /backup/my-data-volume_backup_YYYYMMDD_HHMMSS.tar.gz -C /var/lib/containers/storage/volumes/my-data-volume/_data
      # Option B: Remove and recreate volume (if you want a clean slate)
      # podman volume rm my-data-volume
      # podman volume create my-data-volume
      
      # tar -xzvf /backup/my-data-volume_backup_YYYYMMDD_HHMMSS.tar.gz -C $(podman volume inspect my-data-volume --format '{{.Mountpoint}}')
      
      podman start <container_name_or_id_using_volume>
      
    • Why it works: This replaces the contents of Podman’s managed directory for the named volume with your backed-up data.

An Alternative: Container-Level Backup/Restore

You can also use podman cp to copy data in and out of a running container’s volume mount point. This is often simpler for ad-hoc backups or smaller datasets.

  • Backup Command:

    podman cp <container_name_or_id>:<container_path_to_volume_data> /backup/container_data_backup_$(date +%Y%m%d_%H%M%S)
    
    • Example:
      podman cp my-nginx:/usr/share/nginx/html /backup/nginx_html_backup_$(date +%Y%m%d_%H%M%S)
      
    • Why it works: podman cp streams the contents of the specified path within the container to a directory on your host.
  • Restore Command:

    podman cp /backup/container_data_backup_YYYYMMDD_HHMMSS <container_name_or_id>:<container_path_to_volume_data>
    
    • Example:
      podman cp /backup/nginx_html_backup_YYYYMMDD_HHMMSS my-nginx:/usr/share/nginx/html
      
    • Why it works: This copies data from your host directory into the specified path inside the running container. Note that this might require restarting the application within the container to pick up the changes if it doesn’t watch for file modifications.

The most overlooked aspect of volume backups is ensuring the application using the volume is in a consistent state before the backup begins. For databases, this means executing a proper database dump (pg_dump, mysqldump) rather than just copying raw data files, which could be in the middle of a transaction and lead to corruption.

Next, you’ll likely want to explore automating these backup processes using cron jobs or systemd timers.

Want structured learning?

Take the full Podman course →