
Podman est un moteur de conteneurs sans daemon central qui exécute les conteneurs en mode rootless par défaut. Ce guide vous explique les concepts fondamentaux qui différencient Podman de Docker et comment ils améliorent la sécurité.
En résumé (TL;DR)
Section intitulée « En résumé (TL;DR) »| Concept | Ce que ça signifie |
|---|---|
| Daemonless | Pas de daemon central requis — chaque conteneur est un processus indépendant supervisé par conmon |
| Rootless | Les conteneurs tournent sans privilèges root grâce aux user namespaces (/etc/subuid, /etc/subgid) |
| Pods natifs | Groupe de conteneurs partageant le même réseau via un infra container (concept Kubernetes) |
| Compatibilité | CLI compatible Docker, images OCI, registries standard — avec quelques différences sur Compose et le réseau |
Architecture daemonless
Section intitulée « Architecture daemonless »Le modèle Docker (client-daemon)
Section intitulée « Le modèle Docker (client-daemon) »Docker utilise une architecture client-serveur : le CLI docker communique avec un daemon (dockerd) qui gère les conteneurs.
Ce modèle a des implications :
| Aspect | Conséquence |
|---|---|
| Daemon permanent | Un processus tourne en permanence, même sans conteneurs actifs |
| Accès au socket | L’accès au socket Docker (groupe docker) équivaut à des privilèges root sur la machine — documenté par Docker |
| Point unique de défaillance | Si dockerd crash, tous les conteneurs sont affectés |
Le modèle Podman (fork/exec + conmon)
Section intitulée « Le modèle Podman (fork/exec + conmon) »Podman n’a pas de daemon central requis pour exécuter des conteneurs. Chaque commande podman run lance directement le runtime (crun/runc) :
Vérifiez qu’aucun daemon Podman ne tourne par défaut :
pgrep -a podman# Aucun résultat = pas de daemon permanentconmon : le superviseur léger
Section intitulée « conmon : le superviseur léger »Chaque conteneur est supervisé par conmon (container monitor), un processus minimal qui :
- Maintient le TTY et les flux stdin/stdout/stderr
- Capture le code de sortie du conteneur
- Permet de détacher/rattacher (
podman attach)
podman run -d --name demo docker.io/library/alpine:3.21 sleep 300ps aux | grep conmon | grep -v grepbob 3857458 /usr/bin/conmon --api-version 1 -c d070dd3e... -n demo ...Service API optionnel (podman.socket)
Section intitulée « Service API optionnel (podman.socket) »“Daemonless” ne signifie pas “aucun service possible”. Pour les outils nécessitant l’API Docker, Podman peut exposer un socket compatible :
# Activer le service API (rootless, à la demande)systemctl --user enable --now podman.socket
# Vérifier le socketls -la /run/user/$(id -u)/podman/podman.sockCe service s’active à la demande (socket activation) et ne tourne pas en permanence. Documentation : podman-system-service
Comparaison daemon vs daemonless
Section intitulée « Comparaison daemon vs daemonless »| Aspect | Docker (daemon) | Podman (daemonless) |
|---|---|---|
| Process permanent | dockerd toujours actif | Aucun (ou socket à la demande) |
| Crash du daemon | Conteneurs affectés | Aucun impact |
| Mise à jour CLI | Redémarrer dockerd | Transparent |
| Multi-utilisateurs | Daemon global partagé | Stores séparés par utilisateur |
| Ressources au repos | Daemon en mémoire | Rien |
Mode rootless par défaut
Section intitulée « Mode rootless par défaut »Le principe
Section intitulée « Le principe »En mode rootless, Podman exécute les conteneurs sans aucun privilège root. Le processus du conteneur appartient à votre utilisateur :
podman run -d --name test docker.io/library/alpine:3.20 sleep 100ps aux | grep "sleep 100" | grep -v grepbob 3857490 sleep 100Le processus appartient à bob, pas à root.
User namespaces et subuid/subgid
Section intitulée « User namespaces et subuid/subgid »Comment le conteneur peut-il être “root” à l’intérieur tout en étant un utilisateur normal à l’extérieur ? Grâce aux user namespaces.
Podman mappe l’UID 0 (root) du conteneur vers un UID non privilégié sur l’hôte. Cette configuration est définie dans /etc/subuid et /etc/subgid :
grep $USER /etc/subuid /etc/subgid/etc/subuid:bob:100000:65536/etc/subgid:bob:100000:65536Cela signifie :
- L’utilisateur
bobpeut utiliser les UIDs 100000 à 165535 (65536 UIDs) - L’UID 0 dans le conteneur = UID 100000 sur l’hôte
- L’UID 1000 dans le conteneur = UID 101000 sur l’hôte
Documentation : rootless tutorial
Modes de mapping UID (userns)
Section intitulée « Modes de mapping UID (userns) »Podman propose plusieurs modes de mapping qui influencent les permissions sur les volumes :
| Mode | Comportement | Cas d’usage |
|---|---|---|
| Par défaut | UID 0 conteneur → UID 100000+ hôte | Isolation maximale |
--userns=keep-id | Votre UID réel = même UID dans le conteneur | Volumes partagés avec l’hôte |
--userns=auto | Allocation automatique d’UIDs uniques | Multi-conteneurs isolés |
L’option --userns=keep-id est particulièrement utile pour éviter les problèmes de permissions sur les volumes montés depuis l’hôte.
En savoir plus : modes user namespace
Stockage rootless
Section intitulée « Stockage rootless »En mode rootless, les images et conteneurs sont stockés dans votre home :
podman info --format "{{.Store.GraphRoot}}"/home/bob/.local/share/containers/storageStructure du répertoire :
| Élément | Contenu |
|---|---|
overlay/ | Layers des images (système de fichiers) |
overlay-images/ | Métadonnées des images |
overlay-containers/ | Données des conteneurs |
volumes/ | Volumes nommés |
db.sql ou bolt_state.db | Base de données (SQLite ou BoltDB selon version) |
Limitations du mode rootless
Section intitulée « Limitations du mode rootless »| Limitation | Cause | Solution |
|---|---|---|
| Ports < 1024 | Réservés à root | sysctl net.ipv4.ip_unprivileged_port_start=80 |
| Ping | Nécessite capability | sysctl net.ipv4.ping_group_range="0 65536" |
| Overlayfs | Kernel < 5.11 | fuse-overlayfs (automatique) |
| Performances réseau | Stack userspace | Utiliser rootful si critique |
Quand utiliser le mode rootful
Section intitulée « Quand utiliser le mode rootful »Certains cas nécessitent sudo podman :
- Ports privilégiés (80, 443) sans sysctl
- Montage de certains systèmes de fichiers
- Accès direct aux périphériques (/dev/…)
- Performances réseau maximales
Réseau rootless
Section intitulée « Réseau rootless »Le réseau est la principale source de problèmes en mode rootless. Comprendre les différences évite beaucoup de frustration.
Pourquoi c’est différent
Section intitulée « Pourquoi c’est différent »En rootful, Podman utilise netavark (ou CNI) pour créer des ponts réseau et configurer iptables. En rootless, ces opérations nécessitent des privilèges — Podman utilise donc une stack réseau userspace.
| Stack | Mode | Caractéristiques |
|---|---|---|
| pasta | Rootless (défaut Podman 5+) | Performant, remplace slirp4netns |
| slirp4netns | Rootless (legacy) | Plus lent, compatible ancien |
| netavark | Rootful | Performances natives, iptables |
Symptômes courants et solutions
Section intitulée « Symptômes courants et solutions »| Symptôme | Cause | Solution |
|---|---|---|
| Pas de connectivité DNS | Résolveur mal configuré | Vérifier /etc/resolv.conf dans le conteneur |
| Port non accessible depuis l’hôte | Mapping port incorrect | Utiliser -p 8080:80 explicitement |
| Lenteur réseau | slirp4netns | Mettre à jour vers Podman 5+ (pasta) |
Pour une configuration avancée, voir le guide Réseau Podman.
Discussion : architecture réseau rootless
Le concept de Pod
Section intitulée « Le concept de Pod »Pourquoi les pods ?
Section intitulée « Pourquoi les pods ? »Un Pod est un groupe de conteneurs qui partagent les mêmes namespaces (réseau, IPC). Ce concept vient de Kubernetes où il représente l’unité de déploiement de base.
Podman implémente nativement les pods — une fonctionnalité absente de Docker.
L’infra container
Section intitulée « L’infra container »Quand vous créez un pod, Podman lance automatiquement un conteneur spécial appelé infra (ou pause). Son rôle : maintenir les namespaces actifs même si tous les autres conteneurs s’arrêtent.
Documentation : podman-pod-create
podman pod create --name mon-podpodman pod psPOD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS363aa4fe9e83 mon-pod Created 1 second ago 1ed785cc2461 1Le conteneur infra a un seul rôle : maintenir les namespaces actifs même si tous les autres conteneurs s’arrêtent.
Partage du réseau
Section intitulée « Partage du réseau »Ajoutez des conteneurs au pod :
podman run -d --pod mon-pod --name nginx docker.io/library/nginx:alpinepodman run -d --pod mon-pod --name sidecar docker.io/library/alpine:3.20 sleep 600Listez les conteneurs du pod :
podman ps --filter "pod=mon-pod" --format "table {{.Names}}\t{{.Image}}"NAMES IMAGE363aa4fe9e83-infra localhost/podman-pause:4.9.3-0nginx docker.io/library/nginx:alpinesidecar docker.io/library/alpine:3.20Les conteneurs partagent le même namespace réseau. Le sidecar peut accéder à nginx via localhost :
podman exec sidecar wget -qO- localhost:80 | head -3<!DOCTYPE html><html><head>Vérifier le partage de namespace
Section intitulée « Vérifier le partage de namespace »Les deux conteneurs utilisent exactement le même namespace réseau :
podman inspect nginx --format '{{.NetworkSettings.SandboxKey}}'podman inspect sidecar --format '{{.NetworkSettings.SandboxKey}}'/run/user/1000/netns/netns-a54048f2-209e-ec00-2125-8c76d38cd7e2/run/user/1000/netns/netns-a54048f2-209e-ec00-2125-8c76d38cd7e2Même chemin = même namespace réseau.
Cas d’usage des pods
Section intitulée « Cas d’usage des pods »| Pattern | Description | Exemple |
|---|---|---|
| Sidecar | Conteneur auxiliaire qui complète l’app principale | Collecteur de logs, proxy |
| Ambassador | Proxy vers des services externes | Envoy, connexion DB |
| Adapter | Transforme les sorties de l’app | Convertisseur de métriques |
| Init container | Prépare l’environnement avant l’app | Migration DB, téléchargement config |
Intégration systemd : Quadlet
Section intitulée « Intégration systemd : Quadlet »Pourquoi Quadlet ?
Section intitulée « Pourquoi Quadlet ? »La commande podman generate systemd est dépréciée. Red Hat recommande désormais Quadlet pour intégrer les conteneurs avec systemd.
Documentation : podman-generate-systemd (déprécié)
Principe de Quadlet
Section intitulée « Principe de Quadlet »Quadlet permet de définir des conteneurs comme des unités systemd via des fichiers .container :
[Container]Image=docker.io/library/nginx:alpinePublishPort=8080:80
[Service]Restart=always
[Install]WantedBy=default.target# Recharger systemd et démarrersystemctl --user daemon-reloadsystemctl --user start webappLe conteneur démarre au boot, redémarre en cas d’échec, et s’intègre parfaitement avec journalctl.
Voir le guide Quadlet pour une configuration complète.
Compatibilité Docker
Section intitulée « Compatibilité Docker »CLI compatible
Section intitulée « CLI compatible »Podman est conçu comme un drop-in replacement de Docker. La plupart des commandes fonctionnent à l’identique :
# Ces commandes fonctionnent avec podman ET dockerpodman pull nginxpodman run -d -p 8080:80 nginxpodman pspodman logs <container>podman stop <container>Vous pouvez même créer un alias :
alias docker=podmandocker --versionpodman version 4.9.3Support des Dockerfiles
Section intitulée « Support des Dockerfiles »Podman utilise Buildah en interne et comprend les Dockerfiles standard :
FROM alpine:3.21RUN apk add --no-cache curlCMD ["curl", "--version"]podman build -t mon-image .Registries compatibles
Section intitulée « Registries compatibles »Podman fonctionne avec tous les registries OCI :
- Docker Hub (
docker.io) - Quay.io (
quay.io) - GitHub Container Registry (
ghcr.io) - Registries privés
API Docker
Section intitulée « API Docker »Pour les outils qui nécessitent l’API Docker, Podman peut exposer un socket compatible :
# Activer le service API (rootless)systemctl --user enable --now podman.socket
# Vérifier le socketls -la /run/user/$(id -u)/podman/podman.sockCela permet d’utiliser des outils comme docker-compose avec Podman.
À retenir
Section intitulée « À retenir »-
Daemonless : pas de daemon central requis, mais un service API optionnel (
podman.socket) existe pour la compatibilité -
Rootless par défaut : user namespaces +
subuid/subgid— option--userns=keep-idpour les volumes partagés -
Réseau rootless : stack userspace (pasta/slirp4netns) — comportement différent du rootful
-
Pods natifs : infra container maintient les namespaces — pattern sidecar/ambassador possible
-
Compatibilité Docker : migration souvent simple, mais différences sur Compose et réseau rootless
-
Quadlet : intégration systemd recommandée (remplace
generate systemd)
Écosystème containers
Section intitulée « Écosystème containers »Podman fait partie d’un écosystème cohérent :
| Outil | Utilité | Commande exemple |
|---|---|---|
| Podman | Run, create, exec, logs… | podman run nginx |
| Buildah | Build sans daemon | buildah build -t app . |
| Skopeo | Copier entre registries | skopeo copy docker://a oci://b |
| CRI-O | Runtime pour Kubernetes | Utilisé par OpenShift, K3s |
Tableau comparatif Podman vs Docker
Section intitulée « Tableau comparatif Podman vs Docker »| Aspect | Docker | Podman |
|---|---|---|
| Architecture | Client-daemon | Daemonless (+ socket optionnel) |
| Sécurité par défaut | Daemon root (rootless disponible) | Rootless par défaut |
| Pods natifs | Non | Oui (infra container) |
| Intégration systemd | docker-compose | Quadlet natif |
| Socket/API | Toujours actif | À la demande (socket activation) |
| CLI | docker | podman (compatible) |
| Images | OCI | OCI (compatible) |
Docker est-il rootless ?
Section intitulée « Docker est-il rootless ? »Oui, Docker propose un mode rootless depuis la version 20.10. Cependant, ce mode n’est pas activé par défaut et nécessite une installation séparée. L’architecture daemon reste, avec un daemon et un socket utilisateur.
Pourquoi podman.socket existe si Podman est daemonless ?
Section intitulée « Pourquoi podman.socket existe si Podman est daemonless ? »“Daemonless” signifie qu’aucun daemon n’est requis pour exécuter des conteneurs via la CLI. Le socket podman.socket est un service optionnel qui expose l’API Docker pour les outils tiers (docker-compose, Portainer, etc.). Il s’active à la demande (socket activation) et ne tourne pas en permanence.
Pourquoi mes volumes ont des “permission denied” ?
Section intitulée « Pourquoi mes volumes ont des “permission denied” ? »En rootless, l’UID 0 du conteneur correspond à un UID non privilégié sur l’hôte (100000+). Vos fichiers appartiennent à votre UID réel (ex: 1000), pas à 100000. Solutions :
--userns=keep-id: aligne votre UID réel avec l’UID du conteneur- Suffixe
:Usur le volume : Podman ajuste les permissions (avec précaution)
Pourquoi les pods ont un infra container ?
Section intitulée « Pourquoi les pods ont un infra container ? »L’infra container (ou pause container) maintient les namespaces actifs même si tous les autres conteneurs du pod s’arrêtent. Sans lui, le namespace réseau serait détruit dès l’arrêt du premier conteneur. C’est le même principe que dans Kubernetes.
Quelle différence entre slirp4netns et pasta ?
Section intitulée « Quelle différence entre slirp4netns et pasta ? »Ce sont deux stacks réseau userspace pour le mode rootless :
- slirp4netns : legacy, plus lent, compatibilité large
- pasta : défaut depuis Podman 5, plus performant, remplace slirp4netns
Vérifiez avec podman info --format '{{.Host.NetworkBackend}}'.
Podman est-il 100% compatible Docker ?
Section intitulée « Podman est-il 100% compatible Docker ? »Non. La compatibilité est élevée sur les usages CLI courants, mais des différences existent :
- Compose : utilisez
podman-composeou le mode intégré - Réseau rootless : comportement différent (pas d’iptables)
- Certaines options
docker buildnon supportées - API : compatible via socket, mais pas identique à 100%
Pourquoi Quadlet plutôt que podman generate systemd ?
Section intitulée « Pourquoi Quadlet plutôt que podman generate systemd ? »podman generate systemd créait des fichiers statiques qui devenaient obsolètes. Quadlet utilise des fichiers .container déclaratifs que systemd interprète dynamiquement. C’est plus maintenable et recommandé par Red Hat.
Comment savoir si je suis en rootless ou rootful ?
Section intitulée « Comment savoir si je suis en rootless ou rootful ? »podman info --format '{{.Host.Security.Rootless}}'# true = rootless, false = rootful
# Vérifier aussi le chemin de stockagepodman info --format '{{.Store.GraphRoot}}'# ~/.local/share/containers/storage = rootless# /var/lib/containers/storage = rootful