
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) »Quatre notions suffisent à comprendre ce qui sépare Podman de Docker, et chacune a sa section détaillée plus bas. Retenez surtout la première : l'absence de daemon obligatoire est ce qui rend possible le mode rootless par défaut, et donc tout le reste.
| 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 »La différence la plus visible entre les deux moteurs tient à ce qui tourne sur la
machine quand vous ne lancez rien. Docker maintient un processus permanent
qui possède tous les conteneurs ; Podman n'en a pas et laisse chaque conteneur
vivre comme un processus indépendant. Cette section compare les deux modèles,
présente le superviseur conmon qui remplace le daemon, puis montre le service
API optionnel que Podman propose pour rester compatible avec l'outillage Docker.
Le modèle Docker (client-daemon)
Section intitulée « Le modèle Docker (client-daemon) »Docker utilise une architecture client-serveur : la 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 »Ce récapitulatif met côte à côte les conséquences pratiques des deux architectures. La ligne Multi-utilisateurs est celle qui surprend le plus au quotidien : avec Podman, chaque compte possède son propre stockage, une image téléchargée par un utilisateur n'est donc pas visible par les autres.
| 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 mode rootless est l'argument de sécurité principal de Podman : une évasion de conteneur ne donne pas les droits root sur l'hôte, puisque le processus n'a jamais eu ces droits. Cette section explique le mécanisme qui rend la chose possible, les user namespaces, puis ses conséquences concrètes sur le stockage, les permissions de fichiers et les ports réseau. Ces conséquences expliquent l'essentiel des surprises rencontrées au démarrage.
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 que l'utilisateur bob peut utiliser les UIDs 100000 à 165535
(65536 UIDs) pour les processus de ses conteneurs. Mais l'UID 0 n'en fait pas
partie, et c'est le point que presque tout le monde se représente de travers.
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 »Ces quatre limitations découlent toutes du même fait : sans privilèges, Podman ne
peut pas demander au noyau ce qui est réservé à root. Aucune n'est bloquante, les
contournements de la colonne de droite sont des réglages sysctl de l'hôte, à
poser une fois pour toutes et à documenter.
| 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 »Le mode rootful n'est pas un mode dégradé, c'est le fonctionnement classique
avec un stockage commun dans /var/lib/containers. Il reste nécessaire quand
l'opération demandée touche directement le noyau ou le matériel :
- 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 (le backend par défaut depuis Podman 5) pour créer des ponts réseau et configurer le pare-feu. 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, pare-feu |
Symptômes courants et solutions
Section intitulée « Symptômes courants et solutions »Trois pannes reviennent en boucle sur le réseau rootless. Elles ont un point commun utile au diagnostic : le conteneur démarre normalement, seul le trafic pose problème, ce qui écarte d'emblée les questions d'image ou de permissions de fichiers.
| 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 »Le pod est la fonctionnalité que Docker n'a pas : un groupe de conteneurs qui
partagent des namespaces, exactement comme dans Kubernetes. C'est ce qui permet
de tester en local, sur un poste de développement, une composition
application + sidecar avant de la porter sur un cluster. Cette section montre
comment un pod se crée, quel est le rôle du conteneur infra que Podman ajoute
automatiquement, et comment vérifier que le partage réseau fonctionne réellement.
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 1La colonne # OF CONTAINERS affiche déjà 1 alors qu'aucun conteneur applicatif n'a été lancé : ce conteneur, c'est l'infra, et il est compté comme les autres.
Partage du réseau
Section intitulée « Partage du réseau »Les conteneurs rejoignent un pod existant avec l'option --pod, au moment du podman run. À partir de là, ils partagent le namespace réseau de l'infra : une seule adresse IP pour l'ensemble, et un espace de ports commun où deux conteneurs ne peuvent pas écouter sur le même numéro.
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:5.8.4-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 »Ces quatre patterns viennent du monde Kubernetes et se transposent tels quels dans Podman. Le sidecar est de loin le plus fréquent ; les trois autres sont des variantes plus spécialisées, à connaître mais que vous croiserez moins souvent.
| 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 »Un conteneur lancé à la main disparaît au redémarrage de la machine. Sans daemon
pour les relancer, Podman confie ce rôle à systemd, le gestionnaire de
services de la distribution. Quadlet est le mécanisme qui fait le lien :
vous décrivez le conteneur dans un fichier, systemd en dérive une unité et le
gère comme n'importe quel autre service, avec redémarrage automatique et logs
dans journalctl.
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 »Podman a été conçu pour qu'une équipe habituée à Docker n'ait presque rien à réapprendre : mêmes commandes, mêmes Dockerfiles, mêmes registries, même format d'image OCI. La compatibilité couvre l'usage courant, elle n'est pas totale, et les écarts se concentrent sur Compose et sur le réseau en mode rootless.
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.21@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07dRUN 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 n'est pas un outil isolé mais l'un des quatre projets de l'organisation
containers/, qui se partagent les mêmes bibliothèques containers/image et
containers/storage. Concrètement, une image construite par Buildah est
immédiatement utilisable par Podman sans transfert, parce que les deux écrivent
dans le même stockage local. Cette séparation en outils spécialisés est
l'inverse du choix Docker, qui réunit build, exécution et distribution dans un
seul binaire.
| 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 »Ce tableau résume les écarts abordés dans cette page, ligne par ligne. Notez que plusieurs différences sont des valeurs par défaut plutôt que des capacités exclusives : Docker sait aussi fonctionner en rootless, mais il faut l'installer et l'activer, là où Podman démarre déjà ainsi.
| 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) |
Les questions ci-dessous reprennent les points qui reviennent le plus souvent, notamment les deux contradictions apparentes de Podman : un moteur « sans daemon » qui propose pourtant un socket, et une compatibilité Docker annoncée qui n'est pas totale.
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" ? »La cause n'est pas celle qu'on croit. En rootless, l'UID 0 du conteneur correspond à votre propre compte sur l'hôte, et un fichier écrit par root dans le conteneur vous appartient donc normalement. Le problème vient des autres UID : dès qu'un processus tourne sous un UID non nul dans le conteneur (le cas de la plupart des images applicatives, qui n'exécutent pas en root), ses fichiers atterrissent dans la plage subuid de l'hôte, par exemple 590823 pour l'UID 1000 du conteneur. Ce compte n'existant pas, vous ne pouvez plus lire vos propres données.
Vérifiez le mapping avant de chercher ailleurs :
podman unshare cat /proc/self/uid_mapSolutions, par ordre de préférence :
--userns=keep-id: aligne votre UID réel avec le même UID dans le conteneur. C'est la réponse la plus propre pour un volume partagé avec votre compte.- Suffixe
:Usur le volume : Podman ajuste récursivement le propriétaire des fichiers. Efficace, mais il réécrit les permissions du répertoire hôte, donc à manier avec précaution. podman unshare chown: entrer dans le namespace pour corriger un propriétaire existant sans toucher au reste.
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 le dit lui-même, sans avoir à deviner à partir de la présence de sudo. Le chemin de stockage confirme la réponse, et c'est aussi lui qui explique pourquoi une image « disparaît » quand on change de mode.
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