Virtualisation / Tutoriel

Conteneurs rootless gérés par systemd avec Podman et Quadlet

Déclarer un conteneur dans un simple fichier, le faire démarrer avec la machine sans root, et le mettre à jour automatiquement.

Virtualisation Tutoriel Avancé Publié le 1 octobre 2026 11 min de lecture Testé sur : Podman 4.4 et suivants (Quadlet intégré). Exemples écrits pour Podman 5.
Conteneurs empilés et grues dans un port
Photo : taro ohtani sur Unsplash

Avant de commencer

  • Podman 4.4 ou plus récent : podman --version
  • Un compte utilisateur non privilégié dédié aux services (ici admin)
  • Être à l'aise avec systemd (systemctl, journalctl)

Docker Compose fait démarrer des conteneurs, mais il faut un démon et des droits root, et l'intégration avec le système reste extérieure. Quadlet, intégré à Podman depuis la version 4.4, transforme un fichier .container en véritable service systemd : démarrage au boot, redémarrage en cas d'échec, journaux dans journalctl, dépendances, le tout sans root.

Le principe

Vous écrivez un fichier déclaratif. Au daemon-reload, un générateur systemd le transforme en unité .service classique.

Emplacement Pour
~/.config/containers/systemd/ Conteneurs rootless d'un utilisateur
/etc/containers/systemd/ Conteneurs exécutés en root (à éviter si possible)

Le fichier web.container devient le service web.service.

1. Permettre aux services de l'utilisateur de tourner sans session

Par défaut, les services utilisateur s'arrêtent à la déconnexion et ne démarrent pas au boot. Activez le linger :

sudo loginctl enable-linger admin
loginctl show-user admin --property=Linger    # attendu : Linger=yes

2. Écrire le fichier Quadlet

Exemple : un serveur nginx qui publie un dossier statique.

mkdir -p ~/.config/containers/systemd ~/site
echo '<h1>Ça marche</h1>' > ~/site/index.html
nano ~/.config/containers/systemd/web.container
[Unit]
Description=Serveur web statique
After=network-online.target

[Container]
ContainerName=web
Image=docker.io/library/nginx:stable
PublishPort=8080:80
# %h = dossier personnel ; ro = lecture seule ; Z = étiquette SELinux privée
Volume=%h/site:/usr/share/nginx/html:ro,Z
# Mise à jour automatique depuis le registre (voir étape 5)
AutoUpdate=registry
# Sécurité : aucun gain de privilège possible dans le conteneur
NoNewPrivileges=true

[Service]
Restart=on-failure
TimeoutStartSec=300

[Install]
WantedBy=default.target

Ports privilégiés

Un utilisateur non privilégié ne peut pas écouter sur un port inférieur à 1024. Publiez sur un port haut (8080) et placez un reverse proxy devant, ou abaissez net.ipv4.ip_unprivileged_port_start en connaissance de cause.

3. Générer et démarrer le service

systemctl --user daemon-reload
systemctl --user start web.service
systemctl --user status web.service
curl -s http://localhost:8080

Important

N'utilisez pas systemctl --user enable web.service : l'unité est générée, la commande échoue. Le démarrage au boot est assuré par la section [Install] du fichier .container.

En cas d'erreur, le générateur ne crée tout simplement pas le service. Pour voir pourquoi :

/usr/libexec/podman/quadlet -dryrun -user

Le chemin peut varier selon la distribution (/usr/lib/podman/quadlet sur certaines).

4. Journaux et exploitation

journalctl --user -u web.service -f          # journaux en direct
systemctl --user restart web.service          # redémarrer
podman ps                                     # vue Podman habituelle

Après chaque modification du fichier .container : systemctl --user daemon-reload, puis restart.

5. Mises à jour automatiques

Avec AutoUpdate=registry, Podman compare l'image locale à celle du registre et redémarre le service si une nouvelle version existe. Un timer fourni par Podman s'en charge chaque jour :

systemctl --user enable --now podman-auto-update.timer
podman auto-update --dry-run                  # voir ce qui serait mis à jour

Si le nouveau conteneur ne démarre pas, Podman revient automatiquement à l'image précédente (rollback, activé par défaut).

Attention

Réservez l'auto-update aux images dont l'étiquette suit des versions compatibles (stable, 1.2, etc.). Sur une étiquette latest qui peut changer de version majeure, vous risquez une migration non voulue. Pour vos images construites localement, utilisez AutoUpdate=local.

6. Plusieurs conteneurs qui se parlent

Quadlet gère aussi les réseaux, volumes et pods avec des fichiers .network, .volume et .pod. Exemple d'un réseau partagé :

# ~/.config/containers/systemd/appli.network
[Network]
NetworkName=appli

Puis, dans chaque .container :

[Container]
Network=appli.network

Les conteneurs du même réseau se joignent par leur nom (ContainerName), et systemd démarre le réseau avant eux.

À retenir

Action Commande
Prendre en compte un fichier modifié systemctl --user daemon-reload
Démarrer, arrêter, redémarrer systemctl --user start nom.service (ou stop, restart)
Comprendre une erreur de génération /usr/libexec/podman/quadlet -dryrun -user
Journaux journalctl --user -u nom.service
Démarrage au boot sans session sudo loginctl enable-linger <utilisateur>

À lire aussi