
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.
Une alternative daemonless et rootless à Docker
Podman (POD MANager) est un moteur de conteneurs open source conforme à OCI, créé par Red Hat et désormais hébergé par la CNCF. Il exécute des conteneurs et des pods, avec deux différences clés face à Docker :- Daemonless : pas de démon central, chaque commande lance directement le conteneur.
- Rootless : les conteneurs tournent avec vos droits utilisateur, sans root.
alias docker=podman fonctionne dans la majorité des cas). Il ajoute l'intégration systemd via Quadlet et le support des pods façon Kubernetes.podman run -d -p 8080:80 docker.io/library/nginx:alpine
Une majeure surtout faite de suppressions
Podman 6 est sortie fin juin 2026. Elle finalise des retraits entamés en 5.x :| Supprimé | Remplacé par |
|---|---|
| CNI | Netavark |
| slirp4netns | Pasta |
| cgroups v1 | cgroups v2 |
| iptables | nftables |
| BoltDB | SQLite (migration auto) |
podman volume prune ne supprime plus que les volumes anonymes par défaut (--all pour tout), Quadlet range ses fichiers en sous-répertoires, et les compagnons doivent être alignés (Buildah 1.44, Skopeo 1.23, Netavark/Aardvark 2.0).Aujourd'hui, seuls Arch et Homebrew livrent la 6.0 ; Fedora et Debian restent en 5.x.dockerd.Comparaison architecturale
Docker : Client → dockerd (root) → containerd → runc → conteneur
Podman : Client → conmon (user) → runc → conteneur
Avantages du daemonless
| Aspect | Docker (daemon) | Podman (daemonless) |
|---|---|---|
| Point de défaillance | dockerd = SPOF | Pas de SPOF |
| Redémarrage service | Tue tous les conteneurs | Conteneurs indépendants |
| Surface d'attaque | Daemon root exposé | Pas de daemon |
| Ressources | Daemon permanent | Processus à la demande |
En pratique
# Pas de service à démarrer
podman run -d nginx # Crée directement le processus
# Chaque conteneur est un processus indépendant
ps aux | grep conmon
# user 1234 conmon --cid abc123...
# user 5678 conmon --cid def456...
→ Concepts PodmanPrincipe de fonctionnement
┌─────────────────────────────────────────────────────────┐
│ HÔTE │
│ Utilisateur bob (UID 1000) │
│ └── Plage subuid : 100000-165535 │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ CONTENEUR │ │
│ │ root (UID 0) → mappé sur UID 100000 (hôte) │ │
│ │ user (UID 1000) → mappé sur UID 101000 (hôte) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Configuration
# Vérifier les plages subuid/subgid
cat /etc/subuid
# bob:100000:65536
# Initialiser le user namespace
podman system migrate
# Lancer un conteneur rootless
podman run -d nginx
Sécurité
| Scénario | Impact rootless |
|---|---|
| Évasion du conteneur | Attaquant = UID 100000 (non privilégié) |
| Accès au socket | Pas de socket root exposé |
| Montage /etc/passwd | Impossible sans droits |
Ce que partagent les conteneurs d'un pod
| Ressource | Partagée ? | Implication |
|---|---|---|
| Namespace réseau | ✅ Oui | Tous les conteneurs utilisent localhost |
| Namespace IPC | ✅ Oui | Mémoire partagée possible |
| Namespace UTS | ✅ Oui | Même hostname |
| Volumes | ⚙️ Configurable | Partage de données |
| Namespace PID | ❌ Non | Processus isolés |
Exemple pratique
# Créer un pod avec un port exposé
podman pod create --name webapp -p 8080:80
# Ajouter une BDD (accessible via localhost dans le pod)
podman run -d --pod webapp \
--name db \
-e POSTGRES_PASSWORD=secret \
postgres:16-alpine
# Ajouter l'app (accède à la BDD via localhost:5432)
podman run -d --pod webapp \
--name app \
-e DATABASE_HOST=localhost \
mon-app:latest
Étape 1 : Installer Podman
# Ubuntu/Debian
sudo apt install podman
# Fedora
sudo dnf install podman
Étape 2 : Créer l'alias
# Dans ~/.bashrc ou ~/.zshrc
alias docker=podman
Étape 3 : Migrer les commandes
| Docker | Podman | Notes |
|---|---|---|
docker run |
podman run |
✅ Identique |
docker build |
podman build |
✅ Identique |
docker-compose |
podman-compose |
Installer via pip |
Dockerfile |
Containerfile |
Les deux acceptés |
Étape 4 : Docker Compose
# Option 1 : podman-compose
pip install podman-compose
podman-compose up -d
# Option 2 : podman compose (4.x+)
podman compose up -d
Ce qui fonctionne directement
- ✅ Images Docker Hub
- ✅ Dockerfile/Containerfile
- ✅ Volumes et réseaux
- ✅ Variables d'environnement
Différences mineures
- Registres multiples par défaut (pas seulement Docker Hub)
- Socket différent (
/run/podman/podman.sock) - Mode rootless par défaut
Ports ≥ 1024 (fonctionnent directement)
# Aucune configuration spéciale
podman run -d -p 8080:80 nginx
curl http://localhost:8080
Ports < 1024 (nécessitent configuration)
# Par défaut, erreur :
podman run -d -p 80:80 nginx
# Error: rootlessport cannot expose privileged port 80
Solution 1 : Modifier sysctl# Autoriser les ports à partir de 80
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
# Rendre permanent
echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/podman-ports.conf
Solution 2 : Utiliser rootlesskit# Dans ~/.config/containers/containers.conf
[network]
rootless_networking = "pasta"
Tableau récapitulatif
| Port | Mode rootless | Solution |
|---|---|---|
| ≥ 1024 | ✅ Direct | -p 8080:80 |
| < 1024 | ⚠️ Bloqué | sysctl ou rootlesskit |
| 443 (HTTPS) | ⚠️ Bloqué | sysctl ou reverse proxy |
podman build utilise Buildah en interne pour la construction d'images.Architecture de build
podman build ─────┐
▼
Buildah (bibliothèque)
│
▼
Image OCI
Différences d'usage
| Outil | Usage | Commande type |
|---|---|---|
| podman build | Build via Containerfile | podman build -t app . |
| buildah | Build interactif ou scripté | buildah from, buildah run, buildah commit |
Exemple Buildah interactif
# Créer un conteneur de travail
ctr=$(buildah from alpine:3.21)
# Exécuter des commandes
buildah run $ctr apk add --no-cache curl jq
# Configurer l'image
buildah config --entrypoint '["curl"]' $ctr
# Committer l'image
buildah commit $ctr mon-curl:1.0
Quand utiliser Buildah directement ?
- Scripts de build complexes sans Dockerfile
- Modification d'images existantes
- Pipelines CI/CD avec contrôle fin
- Images minimales construites instruction par instruction
Diagnostic
# Vérifier les permissions
podman run --rm -v ~/data:/data alpine ls -la /data
# Permission denied → problème d'UID ou SELinux
Solution 1 : Option :U (ajustement UID)
# :U ajuste automatiquement le propriétaire pour le mapping rootless
podman run -v ~/data:/data:U mon-app
Solution 2 : SELinux avec :Z ou :z
# :Z = label privé (un seul conteneur)
podman run -v ~/data:/data:Z mon-app
# :z = label partagé (plusieurs conteneurs)
podman run -v ~/shared:/shared:z mon-app
Solution 3 : keep-id
# Votre UID (1000) devient l'UID 1000 dans le conteneur
podman run --userns=keep-id -v ~/data:/data mon-app
Tableau des options
| Option | Problème résolu | Effet |
|---|---|---|
:U |
UID rootless | Chown récursif |
:Z |
SELinux | Label privé |
:z |
SELinux partagé | Label partagé |
--userns=keep-id |
Mapping UID | Conserve votre UID |
:ro |
Sécurité | Lecture seule |
Générer un manifest depuis un conteneur/pod
# Depuis un conteneur unique
podman run -d --name web -p 8080:80 nginx
podman generate kube web > web-pod.yaml
# Depuis un pod complet
podman pod create --name webapp -p 8080:80
podman run -d --pod webapp --name db postgres:16
podman run -d --pod webapp --name app mon-app
podman generate kube webapp > webapp.yaml
Exemple de manifest généré
apiVersion: v1
kind: Pod
metadata:
name: webapp
spec:
containers:
- name: db
image: postgres:16
- name: app
image: mon-app:latest
Exécuter un manifest Kubernetes localement
# Déployer un manifest existant
podman play kube webapp.yaml
# Avec un ConfigMap
podman play kube webapp.yaml --configmap config.yaml
# Arrêter et supprimer
podman play kube webapp.yaml --down
Workflow dev → prod
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Dev local │ ──▶ │ generate │ ──▶ │ Kubernetes │
│ (podman) │ │ kube │ │ (prod) │
└─────────────┘ └─────────────┘ └─────────────┘
→ Concepts podsLimiter la mémoire
# Limite à 512 Mo de RAM
podman run -d --memory 512m nginx
# Limite RAM + swap
podman run -d --memory 512m --memory-swap 1g nginx
Limiter le CPU
# Limite à 1.5 cœurs CPU
podman run -d --cpus 1.5 nginx
# Limite en millicores (comme Kubernetes)
podman run -d --cpus 0.5 nginx # 500m
# Limiter à certains cœurs spécifiques
podman run -d --cpuset-cpus 0,1 nginx
Limiter les processus
# Maximum 100 processus
podman run -d --pids-limit 100 nginx
Vérifier les limites
# Statistiques en temps réel
podman stats
# Inspecter les limites configurées
podman inspect nginx --format '{{.HostConfig.Memory}}'
Tableau récapitulatif
| Option | Effet | Exemple |
|---|---|---|
--memory |
Limite RAM | --memory 256m |
--memory-swap |
Limite RAM+swap | --memory-swap 512m |
--cpus |
Limite cœurs | --cpus 2 |
--cpuset-cpus |
Cœurs spécifiques | --cpuset-cpus 0,2 |
--pids-limit |
Limite processus | --pids-limit 50 |
Réseau par défaut : pas de DNS
# Le réseau 'podman' par défaut n'a pas de DNS
podman run -d --name db postgres:16
podman run --rm alpine ping db
# ping: bad address 'db' ← Échec !
Réseau personnalisé : DNS activé
# Créer un réseau avec DNS intégré
podman network create mon-reseau
# Lancer les conteneurs sur ce réseau
podman run -d --network mon-reseau --name db postgres:16
podman run --rm --network mon-reseau alpine ping db
# PING db (10.89.0.2): 56 data bytes ← Succès !
Alias DNS
# Ajouter des alias pour un conteneur
podman run -d --network mon-reseau \
--name postgres \
--network-alias db \
--network-alias database \
postgres:16
# Accessible via 'postgres', 'db' ou 'database'
Tableau récapitulatif
| Réseau | DNS | Isolation |
|---|---|---|
podman (défaut) |
❌ Non | Minimale |
| Personnalisé | ✅ Oui | Par réseau |
host |
Hôte | Aucune |
none |
❌ Non | Totale |
podman generate systemd qui est déprécié.Créer un fichier Quadlet
# Rootless : ~/.config/containers/systemd/
# Rootful : /etc/containers/systemd/
mkdir -p ~/.config/containers/systemd
Exemple minimal : nginx.container
[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
Volume=/data/nginx:/usr/share/nginx/html:ro,Z
[Service]
Restart=always
[Install]
WantedBy=default.target
Activer le service
# Recharger systemd pour détecter le fichier
systemctl --user daemon-reload
# Démarrer le conteneur
systemctl --user start nginx.service
# Activer au boot
systemctl --user enable nginx.service
Avantages de Quadlet
| Aspect | generate systemd | Quadlet |
|---|---|---|
| Maintenance | Régénérer si config change | Fichier déclaratif |
| Statut | Déprécié | Recommandé |
| Logs | podman logs |
journalctl |
| Restart | Configurable | Natif systemd |
Architecture Podman Machine
macOS/Windows VM Podman Machine
┌─────────────────┐ ┌─────────────────┐
│ podman CLI │ ◄─────────▶ │ Kernel Linux │
│ (commandes) │ socket │ + Podman │
│ │ │ + Conteneurs │
└─────────────────┘ └─────────────────┘
Commandes de base
# Initialiser la VM (télécharge l'image Fedora CoreOS)
podman machine init
# Démarrer la VM
podman machine start
# Vérifier le statut
podman machine list
# Se connecter à la VM
podman machine ssh
Options de virtualisation
| Plateforme | Backend par défaut | Alternative |
|---|---|---|
| macOS Intel | QEMU | HyperKit |
| macOS Apple Silicon | QEMU (arm64) | - |
| Windows | WSL 2 | Hyper-V |
Forwarding de ports
Les ports exposés par les conteneurs sont automatiquement accessibles sur localhost grâce au forwarding intégré.→ Installation PodmanArbre de décision
| Critère | → Podman | → Docker |
|---|---|---|
| Sécurité rootless | ✅ Natif | ⚠️ Expérimental |
| Intégration systemd | ✅ Quadlet | ❌ Workarounds |
| Pods Kubernetes | ✅ Natif | ❌ Non supporté |
| Docker Compose | ⚠️ Compat partielle | ✅ Natif |
| Écosystème existant | ⚠️ Migration | ✅ Déjà en place |
| Support enterprise | Red Hat | Docker Inc |
Recommandations
Choisir Podman si :- Environnement RHEL/Fedora/CentOS
- Sécurité rootless requise
- Services systemd (self-hosting)
- Préparation vers Kubernetes
- Équipe formée Docker Compose
- Intégrations tierces Docker-only
- Pas de contrainte rootless
En pratique
Beaucoup d'équipes utilisent les deux : Docker Desktop pour le dev, Podman pour la prod sur serveurs Linux.→ Comparaison DockerProblème 1 : Ports < 1024
# Erreur typique
podman run -p 80:80 nginx
# Error: rootlessport cannot expose privileged port 80
Solution :sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
Problème 2 : DNS entre conteneurs
# Le réseau par défaut n'a pas de DNS
podman run --rm alpine ping other-container
# ping: bad address 'other-container'
Solution : créer un réseau personnalisépodman network create mon-reseau
podman run -d --network mon-reseau --name db postgres
podman run --rm --network mon-reseau alpine ping db # ✅ fonctionne
Problème 3 : Performance réseau
slirp4netns est plus lent que le bridge kernel.Solution : utiliser pasta (Podman 4.x+)# ~/.config/containers/containers.conf
[network]
default_rootless_network_cmd = "pasta"
Tableau des backends réseau rootless
| Backend | Perf | DNS | Disponibilité |
|---|---|---|---|
| slirp4netns | Lente | ❌ | Partout |
| pasta | Rapide | ✅ | Podman 4.4+ |
podman play kube permet de déployer des manifests Kubernetes localement sans avoir besoin d'un cluster.Cas d'usage
- Test local avant déploiement en prod
- Machines edge sans Kubernetes
- CI/CD pour valider les manifests
- Développement avec les mêmes fichiers que la prod
Exemple pratique
# webapp.yaml
apiVersion: v1
kind: Pod
metadata:
name: webapp
spec:
containers:
- name: web
image: nginx:alpine
ports:
- containerPort: 80
hostPort: 8080
# Déployer le manifest
podman play kube webapp.yaml
# Vérifier
podman pod ps
podman ps
curl http://localhost:8080
# Arrêter et supprimer
podman play kube webapp.yaml --down
Ressources Kubernetes supportées
| Ressource | Support |
|---|---|
| Pod | ✅ Complet |
| Deployment | ✅ Complet |
| ConfigMap | ✅ Complet |
| Secret | ✅ Complet |
| Service | ⚠️ Partiel |
| PersistentVolume | ⚠️ Limité |
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.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_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
: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.
- slirp4netns : legacy, plus lent, compatibilité large
- pasta : défaut depuis Podman 5, plus performant, remplace slirp4netns
podman info --format '{{.Host.NetworkBackend}}'.- 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%
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.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 stockage
podman info --format '{{.Store.GraphRoot}}'
# ~/.local/share/containers/storage = rootless
# /var/lib/containers/storage = rootful