Aller au contenu
Conteneurs & Orchestration medium

Concepts fondamentaux de Podman

24 min de lecture

logo podman

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é.

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.

ConceptCe que ça signifie
DaemonlessPas de daemon central requis, chaque conteneur est un processus indépendant supervisé par conmon
RootlessLes conteneurs tournent sans privilèges root grâce aux user namespaces (/etc/subuid, /etc/subgid)
Pods natifsGroupe 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

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.

Docker utilise une architecture client-serveur : la CLI docker communique avec un daemon (dockerd) qui gère les conteneurs.

Architecture Docker avec daemon centralisé

Ce modèle a des implications :

AspectConséquence
Daemon permanentUn processus tourne en permanence, même sans conteneurs actifs
Accès au socketL'accès au socket Docker (groupe docker) équivaut à des privilèges root sur la machine, documenté par Docker
Point unique de défaillanceSi dockerd crash, tous les conteneurs sont affectés

Podman n'a pas de daemon central requis pour exécuter des conteneurs. Chaque commande podman run lance directement le runtime (crun/runc) :

Architecture Podman daemonless avec conmon

Vérifiez qu'aucun daemon Podman ne tourne par défaut :

Fenêtre de terminal
pgrep -a podman
# Aucun résultat = pas de daemon permanent

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)
Fenêtre de terminal
podman run -d --name demo docker.io/library/alpine:3.21 sleep 300
ps aux | grep conmon | grep -v grep
bob 3857458 /usr/bin/conmon --api-version 1 -c d070dd3e... -n demo ...

"Daemonless" ne signifie pas "aucun service possible". Pour les outils nécessitant l'API Docker, Podman peut exposer un socket compatible :

Fenêtre de terminal
# Activer le service API (rootless, à la demande)
systemctl --user enable --now podman.socket
# Vérifier le socket
ls -la /run/user/$(id -u)/podman/podman.sock

Ce service s'active à la demande (socket activation) et ne tourne pas en permanence. Documentation : podman-system-service

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.

AspectDocker (daemon)Podman (daemonless)
Process permanentdockerd toujours actifAucun (ou socket à la demande)
Crash du daemonConteneurs affectésAucun impact
Mise à jour CLIRedémarrer dockerdTransparent
Multi-utilisateursDaemon global partagéStores séparés par utilisateur
Ressources au reposDaemon en mémoireRien

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.

En mode rootless, Podman exécute les conteneurs sans aucun privilège root. Le processus du conteneur appartient à votre utilisateur :

Fenêtre de terminal
podman run -d --name test docker.io/library/alpine:3.20 sleep 100
ps aux | grep "sleep 100" | grep -v grep
bob 3857490 sleep 100

Le processus appartient à bob, pas à root.

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 :

Fenêtre de terminal
grep $USER /etc/subuid /etc/subgid
/etc/subuid:bob:100000:65536
/etc/subgid:bob:100000:65536

Cela 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.

Mapping des UIDs entre conteneur et hôte via user namespaces

Documentation : rootless tutorial

Podman propose plusieurs modes de mapping qui influencent les permissions sur les volumes :

ModeComportementCas d'usage
Par défautUID 0 conteneur → UID 100000+ hôteIsolation maximale
--userns=keep-idVotre UID réel = même UID dans le conteneurVolumes partagés avec l'hôte
--userns=autoAllocation automatique d'UIDs uniquesMulti-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

En mode rootless, les images et conteneurs sont stockés dans votre home :

Fenêtre de terminal
podman info --format "{{.Store.GraphRoot}}"
/home/bob/.local/share/containers/storage

Structure du répertoire :

ÉlémentContenu
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.dbBase de données (SQLite ou BoltDB selon version)

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.

LimitationCauseSolution
Ports < 1024Réservés à rootsysctl net.ipv4.ip_unprivileged_port_start=80
PingNécessite capabilitysysctl net.ipv4.ping_group_range="0 65536"
OverlayfsKernel < 5.11fuse-overlayfs (automatique)
Performances réseauStack userspaceUtiliser rootful si critique

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

Le réseau est la principale source de problèmes en mode rootless. Comprendre les différences évite beaucoup de frustration.

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.

StackModeCaractéristiques
pastaRootless (défaut Podman 5+)Performant, remplace slirp4netns
slirp4netnsRootless (legacy)Plus lent, compatible ancien
netavarkRootfulPerformances natives, pare-feu

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ômeCauseSolution
Pas de connectivité DNSRésolveur mal configuréVérifier /etc/resolv.conf dans le conteneur
Port non accessible depuis l'hôteMapping port incorrectUtiliser -p 8080:80 explicitement
Lenteur réseauslirp4netnsMettre à jour vers Podman 5+ (pasta)

Pour une configuration avancée, voir le guide Réseau Podman.

Discussion : architecture réseau rootless

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.

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.

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

Fenêtre de terminal
podman pod create --name mon-pod
podman pod ps
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
363aa4fe9e83 mon-pod Created 1 second ago 1ed785cc2461 1

La 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.

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.

Fenêtre de terminal
podman run -d --pod mon-pod --name nginx docker.io/library/nginx:alpine
podman run -d --pod mon-pod --name sidecar docker.io/library/alpine:3.20 sleep 600

Listez les conteneurs du pod :

Fenêtre de terminal
podman ps --filter "pod=mon-pod" --format "table {{.Names}}\t{{.Image}}"
NAMES IMAGE
363aa4fe9e83-infra localhost/podman-pause:5.8.4-0
nginx docker.io/library/nginx:alpine
sidecar docker.io/library/alpine:3.20

Les conteneurs partagent le même namespace réseau. Le sidecar peut accéder à nginx via localhost :

Fenêtre de terminal
podman exec sidecar wget -qO- localhost:80 | head -3
<!DOCTYPE html>
<html>
<head>

Les deux conteneurs utilisent exactement le même namespace réseau :

Fenêtre de terminal
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-8c76d38cd7e2

Même chemin = même namespace réseau.

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.

PatternDescriptionExemple
SidecarConteneur auxiliaire qui complète l'app principaleCollecteur de logs, proxy
AmbassadorProxy vers des services externesEnvoy, connexion DB
AdapterTransforme les sorties de l'appConvertisseur de métriques
Init containerPrépare l'environnement avant l'appMigration DB, téléchargement config

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.

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é)

Quadlet permet de définir des conteneurs comme des unités systemd via des fichiers .container :

~/.config/containers/systemd/webapp.container
[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
[Service]
Restart=always
[Install]
WantedBy=default.target
Fenêtre de terminal
# Recharger systemd et démarrer
systemctl --user daemon-reload
systemctl --user start webapp

Le 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.

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.

Podman est conçu comme un drop-in replacement de Docker. La plupart des commandes fonctionnent à l'identique :

Fenêtre de terminal
# Ces commandes fonctionnent avec podman ET docker
podman pull nginx
podman run -d -p 8080:80 nginx
podman ps
podman logs <container>
podman stop <container>

Vous pouvez même créer un alias :

Fenêtre de terminal
alias docker=podman
docker --version
podman version 4.9.3

Podman utilise Buildah en interne et comprend les Dockerfiles standard :

FROM alpine:3.21@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d
RUN apk add --no-cache curl
CMD ["curl", "--version"]
Fenêtre de terminal
podman build -t mon-image .

Podman fonctionne avec tous les registries OCI :

  • Docker Hub (docker.io)
  • Quay.io (quay.io)
  • GitHub Container Registry (ghcr.io)
  • Registries privés

Pour les outils qui nécessitent l'API Docker, Podman peut exposer un socket compatible :

Fenêtre de terminal
# Activer le service API (rootless)
systemctl --user enable --now podman.socket
# Vérifier le socket
ls -la /run/user/$(id -u)/podman/podman.sock

Cela permet d'utiliser des outils comme docker-compose avec Podman.

  1. Daemonless : pas de daemon central requis, mais un service API optionnel (podman.socket) existe pour la compatibilité

  2. Rootless par défaut : user namespaces + subuid/subgid, option --userns=keep-id pour les volumes partagés

  3. Réseau rootless : stack userspace (pasta/slirp4netns), comportement différent du rootful

  4. Pods natifs : infra container maintient les namespaces, pattern sidecar/ambassador possible

  5. Compatibilité Docker : migration souvent simple, mais différences sur Compose et réseau rootless

  6. Quadlet : intégration systemd recommandée (remplace generate systemd)

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.

Écosystème containers/ : Podman, Buildah, Skopeo partageant containers/image et containers/storage

OutilUtilitéCommande exemple
PodmanRun, create, exec, logs...podman run nginx
BuildahBuild sans daemonbuildah build -t app .
SkopeoCopier entre registriesskopeo copy docker://a oci://b
CRI-ORuntime pour KubernetesUtilisé par OpenShift, K3s

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.

AspectDockerPodman
ArchitectureClient-daemonDaemonless (+ socket optionnel)
Sécurité par défautDaemon root (rootless disponible)Rootless par défaut
Pods natifsNonOui (infra container)
Intégration systemddocker-composeQuadlet natif
Socket/APIToujours actifÀ la demande (socket activation)
CLIdockerpodman (compatible)
ImagesOCIOCI (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.

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 :

Fenêtre de terminal
podman unshare cat /proc/self/uid_map

Solutions, 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 :U sur 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.

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.

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}}'.

Non. La compatibilité est élevée sur les usages CLI courants, mais des différences existent :

  • Compose : utilisez podman-compose ou le mode intégré
  • Réseau rootless : comportement différent (pas d'iptables)
  • Certaines options docker build non 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.

Fenêtre de terminal
podman info --format '{{.Host.Security.Rootless}}'
# true = rootless, false = rootful
# Vérifier aussi le chemin de stockage
podman info --format '{{.Store.GraphRoot}}'
# ~/.local/share/containers/storage = rootless
# /var/lib/containers/storage = rootful

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