
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.
Choisir le bon type de stockage
Section intitulée « Choisir le bon type de stockage »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 ?
| Contexte | Type recommandé | Pourquoi |
|---|---|---|
| Dev : je modifie du code sur l'hôte, je veux voir le résultat immédiatement | Bind mount | Lien 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 build | tmpfs | Rapide (RAM), disparaît à l'arrêt |
Recette 1, Développement : bind mount
Section intitulée « Recette 1, Développement : bind mount »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.
Objectif
Section intitulée « Objectif »Monter un répertoire de votre machine dans le conteneur. Vous éditez les fichiers sur l'hôte, le conteneur les voit instantanément.
Mise en place
Section intitulée « Mise en place »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.
-
Créez un répertoire de travail
Fenêtre de terminal mkdir -p ~/dev-projetecho "<h1>Hello depuis l'hôte</h1>" > ~/dev-projet/index.html -
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 -
Vérifiez dans le navigateur
Ouvrez http://localhost:8080, vous voyez "Hello depuis l'hôte".
-
Modifiez le fichier sur l'hôte
Fenêtre de terminal echo "<h1>Modification en direct !</h1>" > ~/dev-projet/index.html -
Rafraîchissez le navigateur
Le changement est visible immédiatement, pas de rebuild, pas de redémarrage.
Vérification de l'écriture bidirectionnelle
Section intitulée « Vérification de l'écriture bidirectionnelle »Le conteneur peut aussi écrire sur l'hôte :
podman exec dev-nginx sh -c "echo 'Log du conteneur' > /usr/share/nginx/html/log.txt"cat ~/dev-projet/log.txtLog du conteneurNettoyage
Section intitulée « Nettoyage »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.
podman rm -f dev-nginxRecette 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.
Objectif
Section intitulée « Objectif »Persister les données d'une base PostgreSQL indépendamment du conteneur. Supprimer et recréer le conteneur ne perd pas les données.
Mise en place
Section intitulée « Mise en place »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.
-
Créez le volume
Fenêtre de terminal podman volume create postgres-data -
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 -
Créez une table de test
Fenêtre de terminal sleep 5 # Attendre le démarragepodman exec -it postgres-prod psql -U postgres -c \"CREATE TABLE test (id serial, message text); INSERT INTO test (message) VALUES ('Données persistantes');" -
Supprimez le conteneur
Fenêtre de terminal podman rm -f postgres-prod -
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 -
Vérifiez que les données sont toujours là
Fenêtre de terminal sleep 3podman exec -it postgres-prod psql -U postgres -c "SELECT * FROM test;"Résultat attendu id | message----+---------------------1 | Données persistantes
Où sont stockées les données ?
Section intitulée « Où sont stockées les données ? »Ne mémorisez pas un chemin fixe, demandez-le à Podman :
podman volume inspect postgres-data --format '{{.Mountpoint}}'Le chemin dépend du mode (rootless vs rootful) et de la configuration storage.
Nettoyage
Section intitulée « Nettoyage »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.
podman rm -f postgres-prod# Le volume existe toujours ; supprimez-le seulement si vous voulez perdre les données :# podman volume rm postgres-dataRecette 3, Temporaire : tmpfs pour le cache
Section intitulée « Recette 3, Temporaire : tmpfs pour le cache »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.
Objectif
Section intitulée « Objectif »Stocker des données temporaires en RAM. Rapide, volatile, parfait pour les caches ou les sessions.
Mise en place
Section intitulée « Mise en place »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.
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 "Filesystem Size Used Available Use% Mounted ontmpfs 64.0M 0 64.0M 0% /cacheDonnées temporairesQuand 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 :
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"Recette 4, Partager un volume entre conteneurs
Section intitulée « Recette 4, Partager un volume entre conteneurs »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.
Objectif
Section intitulée « Objectif »Deux conteneurs accèdent au même volume, un écrivain, un lecteur.
-
Créez le volume partagé
Fenêtre de terminal podman volume create shared-logs -
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" -
Lisez depuis un autre conteneur
Fenêtre de terminal sleep 5podman run --rm -v shared-logs:/logs:ro docker.io/library/alpine:3.20 \tail -5 /logs/events.logRésultat Thu Feb 13 10:15:02 UTC 2026Thu Feb 13 10:15:04 UTC 2026Thu Feb 13 10:15:06 UTC 2026
Nettoyage
Section intitulée « Nettoyage »L'ordre compte : Podman refuse de supprimer un volume encore référencé par un conteneur, même arrêté.
podman rm -f writerpodman volume rm shared-logsDépannage : les vrais problèmes
Section intitulée « Dépannage : les vrais problèmes »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 deniedCauses possibles :
| Cause | Solution |
|---|---|
| SELinux bloque l'accès | Ajoutez :Z ou :z au montage |
| UID mismatch rootless | Utilisez --userns=keep-id + :U |
| Permissions UNIX du dossier | chmod 777 ~/mon-dossier (test) ou ajustez les ACL |
Diagnostic rapide :
# Vérifiez si SELinux est actifgetenforce
# Vérifiez les permissions du dossierls -laZ ~/mon-dossierSolution SELinux :
# :Z = privé (un seul conteneur)podman run -v ~/mon-dossier:/app:Z ...
# :z = partagé (plusieurs conteneurs)podman run -v ~/mon-dossier:/app:z ...Problème 2 : fichiers créés en "root:root"
Section intitulée « Problème 2 : fichiers créés en "root:root" »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.
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-rw-r--r-- 1 bob bob 0 Feb 13 10:30 mon-fichier.txtLe 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): abc123Solution : supprimez d'abord le conteneur (même arrêté) :
podman rm abc123podman volume rm mon-volumeProblè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 :
# Backuppodman run --rm -v mon-volume:/source:ro -v ~/backups:/backup \ alpine tar cvf /backup/mon-volume.tar -C /source .Backup et restore
Section intitulée « Backup et restore »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.
Sauvegarder un volume
Section intitulée « Sauvegarder un volume »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 :
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 .Restaurer dans un nouveau volume
Section intitulée « Restaurer dans un nouveau volume »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.
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 /destVérifier la restauration
Section intitulée « Vérifier la restauration »Toujours vérifier avant de remettre en prod :
podman run --rm -v postgres-data-restored:/data docker.io/library/alpine:3.20 \ ls -la /dataGouvernance : labels et nettoyage
Section intitulée « Gouvernance : labels et nettoyage »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.
Organiser avec des labels
Section intitulée « Organiser avec des labels »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 :
podman volume create --label env=dev --label app=web dev-web-datapodman volume create --label env=prod --label app=db prod-db-data
# Filtrerpodman volume ls --filter label=env=prodNettoyer les volumes orphelins
Section intitulée « Nettoyer les volumes orphelins »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 :
# Voir les volumes non utiliséspodman volume ls -f dangling=true
# Supprimer les volumes inutilisés (avec confirmation)podman volume pruneEn 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.
Exercice pratique : environnement complet
Section intitulée « Exercice pratique : environnement complet »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.
-
Créez un volume pour une app
Fenêtre de terminal podman volume create --label app=exercice exercice-data -
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" -
Sauvegardez le volume
Fenêtre de terminal mkdir -p ~/backupspodman run --rm -v exercice-data:/source:ro -v ~/backups:/backup \docker.io/library/alpine:3.20 tar cvf /backup/exercice.tar -C /source . -
Supprimez le volume
Fenêtre de terminal podman volume rm exercice-data -
Restaurez et vérifiez
Fenêtre de terminal podman volume create exercice-data-restoredpodman run --rm -v exercice-data-restored:/dest -v ~/backups:/backup \docker.io/library/alpine:3.20 tar xvf /backup/exercice.tar -C /destpodman run --rm -v exercice-data-restored:/data docker.io/library/alpine:3.20 \cat /data/test.txtVous devez voir :
Test exercice
À retenir
Section intitulée « À retenir »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
Aide-mémoire des options
Section intitulée « Aide-mémoire des options »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.
| Option | Description | Exemple |
|---|---|---|
-v nom:/chemin | Volume nommé | -v app-data:/var/lib/app |
-v /hôte:/chemin | Bind mount | -v ~/code:/app |
:ro | Lecture seule | -v config:/etc/app:ro |
:Z | SELinux privé | -v ~/data:/data:Z |
:z | SELinux partagé | -v ~/shared:/shared:z |
:U | Ajuster UID (rootless) | -v vol:/data:U |
--mount type=tmpfs | RAM temporaire | --mount type=tmpfs,dst=/cache |