
Un pod Podman regroupe plusieurs conteneurs qui partagent le même espace réseau. Ils communiquent via localhost, démarrent et s'arrêtent ensemble. C'est idéal pour les patterns sidecar (app + reverse proxy, app + agent de logs).
À la fin de ce guide, vous saurez :
- Créer un pod avec plusieurs conteneurs
- Comprendre ce qui est partagé et ce qui ne l'est pas
- Exposer correctement les ports
- Éviter les pièges classiques
Droit au but
Section intitulée « Droit au but »Quatre entrées directes vers les questions les plus fréquentes sur les pods. Si vous découvrez la notion, suivez plutôt le guide dans l'ordre : chaque section s'appuie sur le vocabulaire posé par la précédente.
Pourquoi utiliser un pod ?
Section intitulée « Pourquoi utiliser un pod ? »Le modèle « un conteneur, un processus » est une bonne règle, mais il laisse un cas non résolu : deux processus si étroitement liés qu'ils forment une seule application. Les faire cohabiter dans une même image casse l'isolation et complique les mises à jour ; les séparer sur deux réseaux distincts ajoute une latence et une configuration inutiles. Le pod est la réponse à cette situation intermédiaire.
Le problème : conteneurs qui doivent communiquer étroitement
Section intitulée « Le problème : conteneurs qui doivent communiquer étroitement »Certains couples de conteneurs ne survivent pas l'un sans l'autre : un proxy qui n'a rien à servir, un agent de collecte de logs sans application à observer. Les traiter comme deux services indépendants oblige à gérer un réseau, une résolution de noms et un ordre de démarrage, pour un couplage qui est de toute façon total. Ces applications nécessitent plusieurs processus qui doivent :
- Communiquer sur
localhost(pas via le réseau) - Partager des volumes pour échanger des fichiers
- Être démarrés/arrêtés ensemble
Exemples :
| Pattern | Conteneur principal | Sidecar |
|---|---|---|
| Reverse proxy | Application | nginx/envoy |
| Logging | Application | Fluentd/Vector |
| Monitoring | Application | Prometheus exporter |
| PHP-FPM | nginx | php-fpm |
La solution : le pod
Section intitulée « La solution : le pod »Un pod est une unité de gestion qui enveloppe plusieurs conteneurs et met en commun une partie de leur isolation. Le terme vient de Kubernetes, dont Podman reprend le modèle pour permettre de tester localement les mêmes assemblages. Concrètement, un pod est un groupe de conteneurs qui partagent :
- Le namespace réseau (même interface, même
localhost) - Le namespace IPC (communication inter-processus)
- Optionnellement des volumes
Premier pod en 5 minutes
Section intitulée « Premier pod en 5 minutes »La création d'un pod se fait en deux temps : d'abord le pod lui-même avec sa configuration réseau, ensuite les conteneurs qu'on y rattache avec --pod. Cet ordre n'est pas négociable, puisque le pod détient déjà l'espace réseau que les conteneurs vont rejoindre.
-
Créer le pod
Fenêtre de terminal podman pod create --name web-stack -p 8080:80Important : le port est exposé au niveau du pod, pas des conteneurs.
-
Ajouter nginx
Fenêtre de terminal podman run -d --pod web-stack --name nginx nginx:alpine -
Vérifier
Fenêtre de terminal podman pod psRésultat POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERSabc123def456 web-stack Running 1 minute ago def456ghi789 2 -
Tester
Fenêtre de terminal curl http://localhost:8080Vous voyez la page d'accueil nginx.
-
Voir les conteneurs du pod
Fenêtre de terminal podman pod inspect web-stack --format '{{range .Containers}}{{.Name}} {{end}}'Résultat abc123def456-infra nginx
Notez qu'il y a 2 conteneurs : nginx + le conteneur "infra" (on y revient).
Ce qui est partagé
Section intitulée « Ce qui est partagé »Un namespace est un mécanisme du noyau Linux qui donne à un processus sa propre vue d'une ressource : ses propres interfaces réseau, sa propre table de processus, son propre système de fichiers. L'isolation d'un conteneur repose entièrement sur ces cloisons. Un pod ne les supprime pas, il en met certaines en commun entre plusieurs conteneurs, et laisse les autres intactes.
Le namespace réseau
Section intitulée « Le namespace réseau »Tous les conteneurs d'un pod partagent la même interface réseau. Concrètement :
- Ils ont la même IP
- Ils peuvent communiquer via localhost
- Un conteneur peut accéder aux ports des autres sans configuration
Démonstration :
# Créer un pod avec deux conteneurspodman pod create --name demo-net
podman run -d --pod demo-net --name server \ python:3.12-alpine python -m http.server 9000
podman run -d --pod demo-net --name client \ alpine:3.21 sleep infinity
# Le client peut accéder au serveur via localhostpodman exec client wget -qO- http://localhost:9000<!DOCTYPE HTML><html>...Sans pod, il faudrait un réseau dédié et utiliser le nom du conteneur.
Le conteneur infra (pause)
Section intitulée « Le conteneur infra (pause) »Quand vous créez un pod, Podman démarre automatiquement un conteneur spécial appelé infra (ou "pause"). Ce conteneur ne fait rien d'utile en apparence, il dort. Mais c'est lui qui détient les namespaces réseau et IPC que tous les autres conteneurs du pod partagent.
Son rôle :
| Fonction | Explication |
|---|---|
| Maintenir les namespaces | Même si tous les conteneurs s'arrêtent, le namespace réseau persiste |
| Ancrer l'IP | L'IP du pod ne change pas quand un conteneur redémarre |
| Léger | Utilise ~1 MB de mémoire |
# Voir le conteneur infrapodman ps --pod | grep infrad75b260a41e0 localhost/podman-pause:5.8.4-0 ... Up web-stack-infra web-stackCe qui N'est PAS partagé
Section intitulée « Ce qui N'est PAS partagé »Le partage se limite au réseau et aux IPC. Chaque conteneur conserve son propre système de fichiers, sa propre vision des processus, et ses propres limites de ressources. C'est une différence importante avec les VM où tout est partagé par défaut.
| Ressource | Partagée ? | Explication |
|---|---|---|
| Réseau | ✅ | Même interface, même localhost |
| IPC | ✅ | Communication inter-processus |
| Volumes | ⚠️ | Seulement si montés explicitement |
| PID namespace | ❌ | Chaque conteneur a ses propres PIDs |
| Filesystem | ❌ | Chaque conteneur a son propre rootfs |
| Mémoire/CPU | ❌ | Limites par conteneur |
Partager des volumes
Section intitulée « Partager des volumes »Le système de fichiers, lui, n'est jamais mis en commun automatiquement. Pour échanger des fichiers, il faut monter explicitement le même volume dans chaque conteneur. Le suffixe :Z demande à Podman de réétiqueter le contenu pour SELinux, sans quoi l'accès est refusé sur Fedora, RHEL et Rocky Linux ; ro monte le volume en lecture seule côté consommateur.
# Créer un pod avec volume partagépodman pod create --name shared-data
# Conteneur qui écritpodman run -d --pod shared-data \ -v data:/shared:Z \ --name writer \ alpine:3.21 sh -c 'while true; do date >> /shared/log.txt; sleep 5; done'
# Conteneur qui litpodman run -d --pod shared-data \ -v data:/shared:Z,ro \ --name reader \ alpine:3.21 tail -f /shared/log.txtLifecycle : pod vs conteneurs
Section intitulée « Lifecycle : pod vs conteneurs »Deux niveaux de gestion coexistent : les commandes podman classiques, qui visent un conteneur, et les commandes podman pod, qui visent le groupe entier. Savoir laquelle utiliser évite la confusion la plus fréquente, celle d'un pod affiché comme actif alors qu'aucun conteneur applicatif ne tourne.
Commandes de pod
Section intitulée « Commandes de pod »Les commandes podman pod agissent sur tous les conteneurs du pod en une seule opération. C'est l'intérêt principal : gérer un groupe comme une unité.
| Commande | Effet |
|---|---|
podman pod start <pod> | Démarre tous les conteneurs |
podman pod stop <pod> | Arrête tous les conteneurs |
podman pod restart <pod> | Redémarre tous les conteneurs |
podman pod rm <pod> | Supprime le pod ET tous ses conteneurs |
podman pod pause <pod> | Met en pause tous les conteneurs |
podman pod unpause <pod> | Reprend tous les conteneurs |
Démonstration du lifecycle
Section intitulée « Démonstration du lifecycle »La séquence ci-dessous crée deux conteneurs sans intérêt fonctionnel (sleep infinity les maintient simplement en vie) pour observer qu'un seul pod stop les arrête tous les deux :
# Créer un pod avec 2 conteneurspodman pod create --name lifecycle-demopodman run -d --pod lifecycle-demo --name c1 alpine:3.21 sleep infinitypodman run -d --pod lifecycle-demo --name c2 alpine:3.21 sleep infinity
# Vérifier l'étatpodman pod pspodman ps --pod
# Arrêter le pod (arrête c1 et c2)podman pod stop lifecycle-demo
# Vérifierpodman ps -a --pod | grep lifecycleabc123 alpine:3.21 Exited c1 lifecycle-demodef456 alpine:3.21 Exited c2 lifecycle-demoComportement au redémarrage
Section intitulée « Comportement au redémarrage »Le pod n'assure aucune supervision de ses membres : il fournit un espace réseau commun, pas une garantie de fonctionnement. Un conteneur qui s'arrête laisse donc le pod dans un état incohérent, actif en apparence mais amputé d'une de ses fonctions. Quand un conteneur dans un pod s'arrête inopinément :
| Situation | Comportement |
|---|---|
| Un conteneur crash | Les autres continuent |
| Tous les conteneurs crashent | Le pod reste "Running" (infra actif) |
| Le conteneur infra crash | Le pod est détruit |
Important : contrairement à Kubernetes, Podman ne redémarre pas automatiquement les conteneurs crashés. Utilisez Quadlet ou systemd pour ça.
Exposition réseau : ports et limites
Section intitulée « Exposition réseau : ports et limites »La publication de ports est le point sur lequel butent presque tous les débutants, parce qu'elle ne se comporte pas comme avec un conteneur isolé. La cause est directe : le conteneur infra détient le namespace réseau, donc c'est lui, et lui seul, qui peut publier un port vers l'hôte.
Règle fondamentale
Section intitulée « Règle fondamentale »Les ports sont exposés au niveau du pod, pas des conteneurs individuels. La conséquence pratique est qu'il faut connaître les ports à publier au moment de créer le pod : les ajouter après coup impose de détruire le pod et de le recréer.
# ✅ Correct : port sur le podpodman pod create --name web -p 8080:80podman run -d --pod web --name nginx nginx:alpine
# ❌ Incorrect : -p ignoré quand --pod est utilisépodman run -d --pod web -p 9090:80 --name nginx nginx:alpine# Warning: Le -p est ignoré !Exposer plusieurs ports
Section intitulée « Exposer plusieurs ports »L'option -p se répète autant de fois que nécessaire sur la commande de création, y compris pour des ports servis par des conteneurs différents du même pod :
podman pod create --name multi-port \ -p 8080:80 \ -p 9000:9000 \ -p 3000:3000Limites du réseau pod
Section intitulée « Limites du réseau pod »Le modèle réseau d'un pod est volontairement simple : tout passe par localhost. C'est parfait pour des conteneurs couplés (nginx → php-fpm), mais insuffisant pour des architectures distribuées.
| Fonctionnalité | Disponible ? | Alternative |
|---|---|---|
| localhost entre conteneurs | ✅ | - |
| Ports exposés à l'hôte | ✅ | - |
| Service discovery (DNS) | ❌ | Utiliser podman-compose ou des réseaux bridge |
| Load balancing | ❌ | Traefik, nginx reverse proxy |
Recette : nginx + php-fpm
Section intitulée « Recette : nginx + php-fpm »Le cas d'usage classique : nginx sert les fichiers statiques et fait proxy vers php-fpm pour le PHP. Les deux processus doivent voir les mêmes fichiers sources et dialoguer en FastCGI, un protocole binaire qui n'a aucun mécanisme de découverte de service. Un pod répond exactement à ces deux besoins avec un volume commun et une adresse fixe.
Architecture
Section intitulée « Architecture »Le schéma montre le trajet d'une requête : elle entre par le port publié du pod, atteint nginx, qui bascule les fichiers .php vers php-fpm sur 127.0.0.1:9000. Les deux conteneurs montent le même répertoire de sources.
Implémentation
Section intitulée « Implémentation »Six étapes, à exécuter dans l'ordre : les fichiers de configuration doivent exister sur l'hôte avant d'être montés, et le pod avant les conteneurs.
-
Créer le contenu PHP
Fenêtre de terminal mkdir -p ~/lab-pod/html~/lab-pod/html/index.php <?phpecho "<h1>PHP dans un pod Podman</h1>";echo "<p>Hostname: " . gethostname() . "</p>";echo "<p>Date: " . date('Y-m-d H:i:s') . "</p>";phpinfo();?> -
Configuration nginx
~/lab-pod/nginx.conf server {listen 80;server_name localhost;root /var/www/html;index index.php index.html;location ~ \.php$ {# php-fpm sur localhost:9000 (même pod)fastcgi_pass 127.0.0.1:9000;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;include fastcgi_params;}location / {try_files $uri $uri/ =404;}} -
Créer le pod avec le port
Fenêtre de terminal podman pod create --name web-php -p 8080:80 -
Lancer php-fpm
Fenêtre de terminal podman run -d --pod web-php \--name php-fpm \-v ~/lab-pod/html:/var/www/html:Z \php:8.3-fpm-alpine -
Lancer nginx
Fenêtre de terminal podman run -d --pod web-php \--name nginx \-v ~/lab-pod/html:/var/www/html:Z,ro \-v ~/lab-pod/nginx.conf:/etc/nginx/conf.d/default.conf:Z,ro \nginx:alpine -
Vérifier
Fenêtre de terminal # État du podpodman pod ps# Conteneurspodman ps --pod# Testercurl http://localhost:8080/index.php
Vérifications
Section intitulée « Vérifications »Trois contrôles suffisent à confirmer que l'assemblage est correct : l'appartenance des conteneurs au pod, l'ouverture du port FastCGI vue depuis nginx, et l'absence d'erreur au démarrage dans les journaux.
# Vérifier que les conteneurs sont dans le même podpodman pod inspect web-php --format '{{range .Containers}}{{.Name}} {{end}}'# php-fpm nginx abc123-infra
# Vérifier que localhost:9000 fonctionne depuis nginxpodman exec nginx sh -c 'echo "test" | nc -z 127.0.0.1 9000 && echo "OK"'# OK
# Logspodman logs nginxpodman logs php-fpmPods vs Compose : règle de décision
Section intitulée « Pods vs Compose : règle de décision »Podman supporte les deux : pods natifs et Compose. La question revient souvent : "Pourquoi utiliser un pod plutôt que Compose ?"
La réponse tient en une phrase : les pods sont pour les conteneurs qui doivent partager localhost, Compose pour les services qui communiquent via le réseau.
Tableau de décision
Section intitulée « Tableau de décision »Lisez ce tableau en partant de votre contrainte la plus forte, pas en comptant les coches. Un seul critère bloquant, comme le besoin de résolution DNS entre services, suffit à trancher.
| Critère | Pod | Compose |
|---|---|---|
| Conteneurs sur localhost | ✅ Idéal | ⚠️ Possible mais pas natif |
| Services indépendants | ❌ | ✅ Idéal |
| Stack multi-services (db + api + front) | ⚠️ | ✅ |
| Pattern sidecar | ✅ Idéal | ⚠️ |
| Compatibilité Kubernetes YAML | ✅ | ❌ |
| Service discovery (DNS) | ❌ | ✅ |
| Déjà un docker-compose.yml | ❌ | ✅ |
Règle simple
Section intitulée « Règle simple »Si vous ne deviez retenir qu'un critère, c'est le mode de communication attendu entre les composants.
- Pod : conteneurs couplés qui doivent partager localhost
- Compose : services découplés qui communiquent via le réseau
Exemples
Section intitulée « Exemples »Cinq situations réelles et le choix qui s'impose, avec la raison qui le motive entre parenthèses.
| Situation | Meilleur choix |
|---|---|
| nginx + php-fpm | Pod (localhost:9000) |
| App + redis + postgres | Compose (services séparés) |
| App + envoy sidecar | Pod (proxy localhost) |
| Microservices | Compose (service discovery) |
| Test d'un YAML Kubernetes | Pod (kube play) |
Différences avec les pods Kubernetes
Section intitulée « Différences avec les pods Kubernetes »Les pods Podman s'inspirent de Kubernetes, mais ne sont pas identiques. Podman est un outil local sans orchestrateur, les fonctionnalités qui dépendent d'un cluster (scheduling, service discovery, restart automatique) sont absentes.
Si vous connaissez Kubernetes, attention aux différences :
| Aspect | Podman pods | Kubernetes pods |
|---|---|---|
| Restart automatique | ❌ Non | ✅ Oui (restartPolicy) |
| Service discovery DNS | ❌ Non | ✅ Oui |
| Load balancing | ❌ Non | ✅ Oui (Services) |
| Scheduling | ❌ Local uniquement | ✅ Multi-nodes |
| Health checks | ⚠️ Manuels | ✅ livenessProbe, readinessProbe |
| Resource limits | ⚠️ Par conteneur | ✅ Pod-level + container-level |
Ce qui pique quand on passe à Kubernetes
Section intitulée « Ce qui pique quand on passe à Kubernetes »Trois habitudes prises avec les pods Podman ne se transposent pas telles quelles, et trois réflexes acquis sur Kubernetes n'ont pas d'équivalent local.
- Pas de restart auto : en production, utilisez Quadlet/systemd
- Pas de DNS : utilisez
localhost, pas des noms de service - Ports au pod : pas de
-psurpodman run --pod
Dépannage
Section intitulée « Dépannage »Les symptômes se ressemblent tous : un service qui ne répond pas. La démarche consiste à remonter la chaîne dans l'ordre, du port publié par le pod jusqu'à l'interface d'écoute du processus dans le conteneur.
Erreurs courantes
Section intitulée « Erreurs courantes »La plupart des problèmes avec les pods viennent de deux sources : les ports mal configurés, et les conteneurs qui ne sont pas dans le bon pod. Le tableau ci-dessous couvre 90% des cas.
| Erreur | Cause | Solution |
|---|---|---|
port already in use | Port exposé sur un pod existant | podman pod rm <pod> |
-p ignoré avec --pod | Ports = responsabilité du pod | Créer le pod avec -p |
| Conteneur ne voit pas l'autre | Mauvais port ou conteneur pas dans le pod | Vérifier avec podman pod inspect |
| Connection refused localhost | Le service n'écoute pas sur toutes les interfaces | Vérifier que le service écoute sur 0.0.0.0 |
Commandes de diagnostic
Section intitulée « Commandes de diagnostic »Ces commandes se lancent dans l'ordre où elles sont listées : d'abord l'inventaire, puis l'inspection détaillée, enfin le test réseau depuis l'intérieur d'un conteneur. La dernière détruit le pod, ne l'exécutez qu'une fois le diagnostic terminé.
# Lister les podspodman pod ps
# Voir les conteneurs d'un podpodman ps --pod --filter pod=<pod-name>
# Inspecter un podpodman pod inspect <pod-name>
# Voir les ports d'un podpodman pod inspect <pod-name> --format '{{.InfraConfig.PortBindings}}'
# Logs de tous les conteneurs du podpodman pod logs <pod-name>
# Tester localhost depuis un conteneurpodman exec <container> wget -qO- http://localhost:<port>
# Nettoyerpodman pod rm -f <pod-name>Le conteneur ne démarre pas dans le pod
Section intitulée « Le conteneur ne démarre pas dans le pod »Un conteneur qui refuse de démarrer dans un pod échoue le plus souvent pour une raison qui n'a rien à voir avec le pod : image absente, commande erronée, volume introuvable. Les journaux du conteneur tranchent immédiatement entre les deux hypothèses.
# Vérifier les logspodman logs <container-name>
# Vérifier que le pod est Runningpodman pod ps
# Recréer le podpodman pod rm -f <pod-name>podman pod create --name <pod-name> -p <ports>Bonnes pratiques
Section intitulée « Bonnes pratiques »Un pod créé à la main pour un essai est une chose ; un pod destiné à durer en est une autre. Trois points font la différence sur le long terme : des noms qu'on peut relire six mois plus tard, un découpage cohérent, et un mécanisme de démarrage qui ne dépend pas d'une session de terminal.
Podman attribue un nom aléatoire aux pods et conteneurs anonymes, ce qui rend toute commande ultérieure pénible. Nommez systématiquement, en préfixant les conteneurs par le nom du pod :
# ✅ Noms explicitespodman pod create --name api-stackpodman run --pod api-stack --name api-server ...podman run --pod api-stack --name api-cache ...
# ❌ Noms génériquespodman pod create --name pod1podman run --pod pod1 --name container1 ...Organisation
Section intitulée « Organisation »Le périmètre d'un pod se décide sur un critère unique : les conteneurs partagent-ils vraiment un cycle de vie et une adresse ? Si la réponse est non pour l'un d'eux, il n'a rien à faire là.
- Un pod = un groupe fonctionnel (app + sidecars)
- Pas de pod monolithique avec 10 services indépendants
- Volumes partagés seulement si nécessaire
Production
Section intitulée « Production »Pour la production, ne lancez pas les pods manuellement. Utilisez :
- Quadlet : fichiers déclaratifs → systemd (voir le guide)
- kube play : fichiers YAML Kubernetes (voir le guide)
À retenir
Section intitulée « À retenir »- Pod = groupe de conteneurs partageant le réseau (même localhost)
- Conteneur infra : maintient les namespaces, ne pas supprimer
- Ports exposés au pod :
-psurpodman pod create, pas surrun - Pas de restart auto : utilisez Quadlet/systemd en production
- Pas de DNS : communication via
localhost:<port> - Pod vs Compose : pod pour couplage fort, Compose pour services séparés