Aller au contenu
Conteneurs & Orchestration medium

Volumes Podman : persistance et partage de données

18 min de lecture

logo podman

Les conteneurs sont éphémères : supprimer un conteneur supprime ses données internes. Le stockage se gère donc avec 3 mécanismes : volume nommé, bind mount, tmpfs. Ce cours vous apprend à les utiliser dans des contextes réels, dev, prod, temporaire, et à résoudre les problèmes de permissions qui bloquent 80% des débutants.

Les trois mécanismes ne s'opposent pas sur leurs performances mais sur qui possède les données. Un bind mount expose un répertoire de l'hôte, dont vous gardez le contrôle total ; un volume nommé délègue l'emplacement à Podman, qui le gère et le référence par son nom ; un tmpfs n'écrit rien sur disque et vit en mémoire vive. Avant de foncer sur les commandes, posez-vous la question : quel est mon contexte ?

ContexteType recommandéPourquoi
Dev : je modifie du code sur l'hôte, je veux voir le résultat immédiatementBind mountLien direct hôte ↔ conteneur
Prod : je veux persister des données (DB, uploads, logs)Volume nomméGéré par Podman, portable, sauvegardable
Temporaire : cache, sessions, fichiers de buildtmpfsRapide (RAM), disparaît à l'arrêt

En développement, le cycle « modifier, rebâtir l'image, relancer » est intenable. Le bind mount le supprime : le conteneur lit directement les fichiers de votre poste, chaque enregistrement est visible sans intervention. Cette recette monte un répertoire de sources dans un nginx et vérifie que la circulation fonctionne dans les deux sens.

Monter un répertoire de votre machine dans le conteneur. Vous éditez les fichiers sur l'hôte, le conteneur les voit instantanément.

Cinq étapes, dont deux ne servent qu'à constater le résultat. Le point à retenir se trouve à l'étape 2, dans le suffixe :Z du montage.

  1. Créez un répertoire de travail

    Fenêtre de terminal
    mkdir -p ~/dev-projet
    echo "<h1>Hello depuis l'hôte</h1>" > ~/dev-projet/index.html
  2. Lancez nginx avec un bind mount

    Fenêtre de terminal
    podman run -d --name dev-nginx -p 8080:80 \
    -v ~/dev-projet:/usr/share/nginx/html:Z \
    docker.io/library/nginx:alpine
  3. Vérifiez dans le navigateur

    Ouvrez http://localhost:8080, vous voyez "Hello depuis l'hôte".

  4. Modifiez le fichier sur l'hôte

    Fenêtre de terminal
    echo "<h1>Modification en direct !</h1>" > ~/dev-projet/index.html
  5. Rafraîchissez le navigateur

    Le changement est visible immédiatement, pas de rebuild, pas de redémarrage.

Le conteneur peut aussi écrire sur l'hôte :

Fenêtre de terminal
podman exec dev-nginx sh -c "echo 'Log du conteneur' > /usr/share/nginx/html/log.txt"
cat ~/dev-projet/log.txt
Résultat attendu
Log du conteneur

Le conteneur supprimé, le répertoire ~/dev-projet et son contenu restent intacts sur l'hôte : un bind mount ne fait qu'exposer un chemin existant, il ne le possède pas.

Fenêtre de terminal
podman rm -f dev-nginx

Recette 2, Production : volume nommé + base de données

Section intitulée « Recette 2, Production : volume nommé + base de données »

En production, la donnée doit survivre au conteneur qui l'a créée : montée de version de l'image, changement de paramètre, redémarrage après incident. Le volume nommé assure cette indépendance parce qu'il possède un cycle de vie propre, distinct de celui des conteneurs qui le montent. Cette recette le démontre en détruisant volontairement la base pour la voir revenir intacte.

Persister les données d'une base PostgreSQL indépendamment du conteneur. Supprimer et recréer le conteneur ne perd pas les données.

Les six étapes forment une démonstration complète : création, écriture, destruction, recréation, relecture. L'étape 4 supprime réellement le conteneur, c'est ce qui donne sa valeur à la vérification finale.

  1. Créez le volume

    Fenêtre de terminal
    podman volume create postgres-data
  2. Lancez PostgreSQL avec ce volume

    Fenêtre de terminal
    podman run -d --name postgres-prod \
    -e POSTGRES_PASSWORD=secret \
    -v postgres-data:/var/lib/postgresql/data \
    docker.io/library/postgres:16-alpine
  3. Créez une table de test

    Fenêtre de terminal
    sleep 5 # Attendre le démarrage
    podman exec -it postgres-prod psql -U postgres -c \
    "CREATE TABLE test (id serial, message text); INSERT INTO test (message) VALUES ('Données persistantes');"
  4. Supprimez le conteneur

    Fenêtre de terminal
    podman rm -f postgres-prod
  5. Recréez un conteneur avec le même volume

    Fenêtre de terminal
    podman run -d --name postgres-prod \
    -e POSTGRES_PASSWORD=secret \
    -v postgres-data:/var/lib/postgresql/data \
    docker.io/library/postgres:16-alpine
  6. Vérifiez que les données sont toujours là

    Fenêtre de terminal
    sleep 3
    podman exec -it postgres-prod psql -U postgres -c "SELECT * FROM test;"
    Résultat attendu
    id | message
    ----+---------------------
    1 | Données persistantes

Ne mémorisez pas un chemin fixe, demandez-le à Podman :

Fenêtre de terminal
podman volume inspect postgres-data --format '{{.Mountpoint}}'

Le chemin dépend du mode (rootless vs rootful) et de la configuration storage.

La suppression du conteneur ne touche pas au volume, qui reste listé par podman volume ls. C'est exactement le comportement recherché, mais cela signifie qu'un volume oublié occupe du disque indéfiniment.

Fenêtre de terminal
podman rm -f postgres-prod
# Le volume existe toujours ; supprimez-le seulement si vous voulez perdre les données :
# podman volume rm postgres-data

Certaines données n'ont aucune raison d'atteindre le disque : sessions, caches, fichiers intermédiaires de compilation. Les écrire en tmpfs, c'est-à-dire en mémoire vive, évite l'écriture inutile et garantit qu'il n'en reste aucune trace après l'arrêt. C'est aussi la brique qui rend utilisable un conteneur monté en lecture seule.

Stocker des données temporaires en RAM. Rapide, volatile, parfait pour les caches ou les sessions.

Le tmpfs se déclare avec --mount, pas avec -v. L'option tmpfs-size borne la mémoire consommée : sans elle, le montage peut grossir jusqu'à la moitié de la RAM de l'hôte.

Fenêtre de terminal
podman run --rm --mount type=tmpfs,destination=/cache,tmpfs-size=64M \
docker.io/library/alpine:3.20 sh -c "
echo 'Données temporaires' > /cache/session.txt
df -h /cache
cat /cache/session.txt
"
Résultat
Filesystem Size Used Available Use% Mounted on
tmpfs 64.0M 0 64.0M 0% /cache
Données temporaires

Quand le conteneur s'arrête, tout disparaît.

Cas d'usage typique : conteneur read-only avec tmpfs

Section intitulée « Cas d'usage typique : conteneur read-only avec tmpfs »

Pour durcir un conteneur, montez le système de fichiers en lecture seule mais autorisez /tmp en tmpfs :

Fenêtre de terminal
podman run --rm --read-only \
--mount type=tmpfs,destination=/tmp \
docker.io/library/alpine:3.20 sh -c "echo test > /tmp/ok.txt && cat /tmp/ok.txt"

Un même volume nommé peut être monté par plusieurs conteneurs simultanément, sans configuration particulière. C'est le mécanisme derrière les schémas producteur/consommateur : un conteneur produit des fichiers, un autre les exploite. La seule précaution porte sur l'écriture concurrente, que Podman n'arbitre pas.

Deux conteneurs accèdent au même volume, un écrivain, un lecteur.

  1. Créez le volume partagé

    Fenêtre de terminal
    podman volume create shared-logs
  2. Lancez le conteneur écrivain

    Fenêtre de terminal
    podman run -d --name writer -v shared-logs:/logs docker.io/library/alpine:3.20 \
    sh -c "while true; do date >> /logs/events.log; sleep 2; done"
  3. Lisez depuis un autre conteneur

    Fenêtre de terminal
    sleep 5
    podman run --rm -v shared-logs:/logs:ro docker.io/library/alpine:3.20 \
    tail -5 /logs/events.log
    Résultat
    Thu Feb 13 10:15:02 UTC 2026
    Thu Feb 13 10:15:04 UTC 2026
    Thu Feb 13 10:15:06 UTC 2026

L'ordre compte : Podman refuse de supprimer un volume encore référencé par un conteneur, même arrêté.

Fenêtre de terminal
podman rm -f writer
podman volume rm shared-logs

En Podman, 80% des problèmes de volumes viennent de deux sources : permissions rootless et SELinux. Voici comment les résoudre.

Problème 1 : "Permission denied" sur un bind mount

Section intitulée « Problème 1 : "Permission denied" sur un bind mount »

C'est l'erreur la plus fréquente, et elle recouvre trois causes distinctes qu'il faut savoir départager avant d'agir. Un chmod appliqué au hasard sur un problème SELinux ne changera rien, et inversement.

Symptôme :

/bin/sh: can't create /app/file.txt: Permission denied

Causes possibles :

CauseSolution
SELinux bloque l'accèsAjoutez :Z ou :z au montage
UID mismatch rootlessUtilisez --userns=keep-id + :U
Permissions UNIX du dossierchmod 777 ~/mon-dossier (test) ou ajustez les ACL

Diagnostic rapide :

Fenêtre de terminal
# Vérifiez si SELinux est actif
getenforce
# Vérifiez les permissions du dossier
ls -laZ ~/mon-dossier

Solution SELinux :

Fenêtre de terminal
# :Z = privé (un seul conteneur)
podman run -v ~/mon-dossier:/app:Z ...
# :z = partagé (plusieurs conteneurs)
podman run -v ~/mon-dossier:/app:z ...

Symptôme : les fichiers créés par le conteneur appartiennent à root sur l'hôte, vous ne pouvez pas les modifier.

Cause : en rootless, l'UID dans le conteneur est mappé différemment.

Solution : --userns=keep-id conserve votre UID, et :U ajuste le volume.

Fenêtre de terminal
podman run --rm --userns=keep-id -v ~/mon-dossier:/app:U \
docker.io/library/alpine:3.20 touch /app/mon-fichier.txt
ls -la ~/mon-dossier/mon-fichier.txt
Résultat
-rw-r--r-- 1 bob bob 0 Feb 13 10:30 mon-fichier.txt

Le fichier vous appartient.

Problème 3 : "volume in use" lors de la suppression

Section intitulée « Problème 3 : "volume in use" lors de la suppression »

Podman refuse de supprimer un volume tant qu'un conteneur le référence, y compris un conteneur arrêté ou en échec. Le message d'erreur donne l'identifiant du coupable.

Symptôme :

Error: volume "mon-volume" is being used by the following container(s): abc123

Solution : supprimez d'abord le conteneur (même arrêté) :

Fenêtre de terminal
podman rm abc123
podman volume rm mon-volume

Problème 4 : données perdues après podman machine reset

Section intitulée « Problème 4 : données perdues après podman machine reset »

Symptôme (macOS/Windows) : après un podman machine rm, tous les volumes ont disparu.

Cause : les volumes sont stockés dans la VM. Supprimer la VM = supprimer les volumes.

Solution : sauvegardez vos volumes avant de toucher à la machine :

Fenêtre de terminal
# Backup
podman run --rm -v mon-volume:/source:ro -v ~/backups:/backup \
alpine tar cvf /backup/mon-volume.tar -C /source .

Podman ne propose pas de commande de sauvegarde de volume. La méthode consiste à lancer un conteneur jetable qui monte deux choses : le volume à sauvegarder en lecture seule, et un répertoire de l'hôte où déposer l'archive. Le même schéma inversé sert à la restauration.

Le conteneur alpine ne sert qu'à exécuter tar ; --rm le supprime dès la fin. Le montage :ro garantit qu'une erreur de commande ne peut pas altérer la source :

Fenêtre de terminal
podman run --rm \
-v postgres-data:/source:ro \
-v ~/backups:/backup \
docker.io/library/alpine:3.20 \
tar cvf /backup/postgres-data-$(date +%Y%m%d).tar -C /source .

Restaurez toujours dans un volume neuf, jamais par-dessus l'original : tant que la restauration n'est pas vérifiée, la donnée d'origine reste votre seul filet.

Fenêtre de terminal
podman volume create postgres-data-restored
podman run --rm \
-v postgres-data-restored:/dest \
-v ~/backups:/backup \
docker.io/library/alpine:3.20 \
tar xvf /backup/postgres-data-20260213.tar -C /dest

Toujours vérifier avant de remettre en prod :

Fenêtre de terminal
podman run --rm -v postgres-data-restored:/data docker.io/library/alpine:3.20 \
ls -la /data

Sur un poste qui sert plusieurs projets, la liste des volumes devient vite illisible et personne n'ose plus rien supprimer. Deux mécanismes évitent cette accumulation : les labels, qui rattachent chaque volume à un contexte identifiable, et la commande de purge, qui élimine ce que plus aucun conteneur ne référence.

Un label est un simple couple clé/valeur attaché au volume à sa création. Il ne change rien au fonctionnement, mais rend les listes filtrables :

Fenêtre de terminal
podman volume create --label env=dev --label app=web dev-web-data
podman volume create --label env=prod --label app=db prod-db-data
# Filtrer
podman volume ls --filter label=env=prod

Un volume est dit dangling quand plus aucun conteneur ne le monte. Commencez systématiquement par lister avant de purger, la suppression étant définitive et sans corbeille :

Fenêtre de terminal
# Voir les volumes non utilisés
podman volume ls -f dangling=true
# Supprimer les volumes inutilisés (avec confirmation)
podman volume prune

En Podman 5, podman volume prune supprime tous les volumes inutilisés (nommés et anonymes). En Podman 6, le comportement s'aligne sur Docker : il ne cible plus que les volumes anonymes par défaut. Ajoutez --all pour retrouver l'ancien comportement, et --dry-run pour voir ce qui serait supprimé sans rien effacer.

Cet exercice enchaîne les gestes des recettes précédentes sur un cas complet : créer, remplir, sauvegarder, détruire, restaurer, vérifier. Il valide surtout le point le plus souvent négligé, la relecture effective de la donnée restaurée. Une sauvegarde dont on n'a jamais lu le contenu ne prouve rien.

  1. Créez un volume pour une app

    Fenêtre de terminal
    podman volume create --label app=exercice exercice-data
  2. Lancez un conteneur qui écrit dedans

    Fenêtre de terminal
    podman run --rm -v exercice-data:/data docker.io/library/alpine:3.20 \
    sh -c "echo 'Test exercice' > /data/test.txt"
  3. Sauvegardez le volume

    Fenêtre de terminal
    mkdir -p ~/backups
    podman run --rm -v exercice-data:/source:ro -v ~/backups:/backup \
    docker.io/library/alpine:3.20 tar cvf /backup/exercice.tar -C /source .
  4. Supprimez le volume

    Fenêtre de terminal
    podman volume rm exercice-data
  5. Restaurez et vérifiez

    Fenêtre de terminal
    podman volume create exercice-data-restored
    podman run --rm -v exercice-data-restored:/dest -v ~/backups:/backup \
    docker.io/library/alpine:3.20 tar xvf /backup/exercice.tar -C /dest
    podman run --rm -v exercice-data-restored:/data docker.io/library/alpine:3.20 \
    cat /data/test.txt

    Vous devez voir : Test exercice

Types de stockage

  • Bind mount : dev, hot-reload, accès direct aux fichiers hôte
  • Volume nommé : prod, persistance, portable
  • tmpfs : cache, sessions, données volatiles

Résoudre les permissions

  • SELinux : ajoutez :Z (privé) ou :z (partagé)
  • Rootless : utilisez --userns=keep-id + :U
  • Fichiers root:root : combinez keep-id et :U

Toutes ces options se placent après le chemin de destination, séparées par des deux-points, et se combinent (-v vol:/data:ro,Z). Le tableau sert de référence rapide une fois les recettes assimilées.

OptionDescriptionExemple
-v nom:/cheminVolume nommé-v app-data:/var/lib/app
-v /hôte:/cheminBind mount-v ~/code:/app
:roLecture seule-v config:/etc/app:ro
:ZSELinux privé-v ~/data:/data:Z
:zSELinux partagé-v ~/shared:/shared:z
:UAjuster UID (rootless)-v vol:/data:U
--mount type=tmpfsRAM temporaire--mount type=tmpfs,dst=/cache

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn