L'unité systemd, cette brique qu'on ne regarde jamais
Ce fichier, c'est une unité. Et tant que tout va bien, tu n'as aucune raison de savoir ce qu'il y a dedans. Le problème arrive le jour où un service refuse de démarrer à 3 heures du matin, ou quand tu veux lancer ton propre script au boot et que tu te retrouves à copier-coller des recettes trouvées sur un forum.
Là, il faut comprendre ce qu'est réellement une unité. Pas la mémoriser. La comprendre.
PID 1 et le reste du monde
systemd démarre comme premier processus de l'espace utilisateur. PID 1. Tout ce qui tourne ensuite sur ta machine descend de lui, directement ou non.Ce n'est pas un détail cosmétique. C'est ce qui donne à systemd le droit de décider qui démarre quand, qui attend qui, et qui a le droit de mourir.
L'unité est l'unité de base de cette gestion. Une abstraction. Un fichier texte qui décrit une ressource du système et la manière dont systemd doit la traiter.
Une unité peut décrire un service, un point de montage, une socket réseau, un timer, un périphérique. Le format reste le même. C'est là que ça devient intéressant.
Ce qu'une unité remplace
Avant, tu avais des scripts shell dans `/etc/init.d/`. Ils s'exécutaient dans un ordre fixe, défini par des numéros dans des noms de fichiers comme `S20nginx` ou `K80apache`.Personne ne vérifiait vraiment si l'étape précédente avait réussi. Le script se lançait, croisait les doigts, et le suivant prenait le relais.
Une unité fait l'inverse. Tu décris ce que tu veux. Pas comment y arriver.
Tu écris `After=network.target` et systemd se débrouille. Tu écris `Requires=postgresql.service` et systemd refuse de démarrer ton service si la base ne répond pas.
C'est déclaratif. Et le déclaratif, ça se relit six mois plus tard sans avoir envie de pleurer.
Les types d'unités que tu croiseras vraiment
Huit types existent. Tu en utiliseras trois dans ta vie quotidienne.Le service décrit un programme à exécuter. Un daemon, un script, un binaire. C'est 90 % de ce que tu écriras.
La socket décrit une socket réseau ou un FIFO. Elle permet à systemd d'écouter à ta place et de ne lancer le service qu'à la première connexion. C'est ce qu'on appelle l'activation par socket, et ça permet à un service lourd de ne pas tourner pour rien.
Le timer remplace cron. Il déclenche une unité de service selon un calendrier. Plus de crontab éparpillée, plus de scripts perdus dans `/etc/cron.daily/`.
Les autres — device, mount, swap, target, path — servent aussi, mais tu les croiseras surtout dans la configuration générée automatiquement.
Un `target` mérite quand même qu'on s'y arrête. C'est un groupe d'unités. `multi-user.target` correspond grosso modo à l'ancien runlevel 3. `graphical.target` à l'ancien runlevel 5.
Quand tu écris `systemctl enable monservice`, tu dis à systemd de créer un lien symbolique vers ce service depuis un target. C'est tout. Rien de magique.
Où vivent les fichiers
Trois répertoires. Une hiérarchie claire.`/usr/lib/systemd/system/` contient les unités livrées par les paquets. Tu n'y touches pas. Une mise à jour de paquet écrasera tes modifications.
`/run/systemd/system/` contient les unités générées à la volée. Elles disparaissent au reboot. Tu n'y touches pas non plus.
`/etc/systemd/system/` est ton terrain. C'est ici que tu écris tes propres unités, ou que tu surcharges celles des paquets.
Quand deux fichiers portent le même nom dans deux répertoires, systemd prend celui de `/etc/`. C'est ce qui te permet de modifier un comportement sans toucher au fichier d'origine.
La bonne pratique : ne jamais éditer un fichier dans `/usr/lib/`. Tu le copies dans `/etc/` et tu modifies la copie. Ou mieux, tu utilises `systemctl edit`, qui crée automatiquement un fragment de surcharge.
``` systemctl edit nginx.service ```
Cette commande ouvre un éditeur sur `/etc/systemd/system/nginx.service.d/override.conf`. Tu n'y mets que ce que tu changes. Le reste reste géré par le paquet.
C'est propre. C'est la manière de faire.
Anatomie d'un fichier
Une unité se lit par sections. Trois reviennent partout.`[Unit]` contient les métadonnées. La description, les dépendances, les conditions de démarrage.
`[Service]` n'existe que pour les services. Elle dit quoi lancer, sous quel utilisateur, comment redémarrer en cas d'échec.
`[Install]` dit comment l'unité s'active. Généralement en s'accrochant à un target.
Prenons un exemple concret. Un script Python que tu veux faire tourner en permanence.
``` [Unit] Description=Mon collecteur de métriques After=network-online.target Wants=network-online.target
[Service] Type=simple User=metrics Group=metrics WorkingDirectory=/opt/metrics ExecStart=/usr/bin/python3 /opt/metrics/collector.py Restart=on-failure RestartSec=5
[Install] WantedBy=multi-user.target ```
Chaque ligne compte.
`After=network-online.target` dit à systemd de ne pas démarrer avant que le réseau soit réellement disponible. Pas juste configuré. Disponible.
`Type=simple` dit que le processus lancé par `ExecStart` reste au premier plan. C'est le cas le plus courant. Si ton script fork et rend la main, il faut `Type=forking`, et là ça devient plus délicat.
`User=` et `Group=` font tourner le service sous un compte non privilégié. Ne jamais lancer un service applicatif en root sans raison. Jamais.
`Restart=on-failure` relance le service s'il plante. Pas s'il sort proprement. La distinction compte.
`WantedBy=multi-user.target` fait que `systemctl enable` accrochera l'unité au démarrage normal du système.
Tu remarques qu'il n'y a pas de `[Install]` obligatoire. Une unité sans `[Install]` peut être démarrée manuellement, mais pas activée au boot. C'est parfois exactement ce qu'on veut.
Les dépendances, là où tout se joue
C'est la partie que la plupart des gens découvrent trop tard.`Requires=` crée une dépendance dure. Si l'unité requise échoue, celle-ci ne démarre pas. Si l'unité requise s'arrête, celle-ci s'arrête aussi.
`Wants=` crée une dépendance souple. Si l'unité voulue échoue, celle-ci démarre quand même. C'est presque toujours ce que tu veux.
`After=` contrôle l'ordre. Pas la dépendance. Une unité peut être `After=` une autre sans en dépendre du tout.
`Before=` fait l'inverse.
La confusion classique : croire que `After=` implique `Requires=`. Ce n'est pas le cas. Tu peux parfaitement ordonner deux unités indépendantes l'une de l'autre.
Règle simple : `Wants=` pour la dépendance, `After=` pour l'ordre. La combinaison `Wants=` + `After=` couvre 95 % des besoins.
`Requires=` a sa place, mais il rend le système fragile. Une dépendance dure qui échoue entraîne tout le reste dans sa chute.
Les generators, ou la configuration qui se fabrique toute seule
systemd ne lit pas seulement tes fichiers. Il en génère.`systemd-fstab-generator` lit `/etc/fstab` et crée une unité de montage pour chaque ligne. Tu n'écris jamais d'unité `.mount` à la main pour un disque déclaré dans `fstab`.
`systemd-gpt-auto-generator` détecte les partitions GPT et crée automatiquement les unités correspondantes pour `/home`, `/srv`, ou le swap.
Ces programmes tournent à chaque boot, avant que systemd ne charge les unités. Ils transforment des sources de configuration classiques en unités natives.
L'intérêt : tu gardes tes habitudes — un `fstab`, une table de partitions — et systemd s'aligne dessus. Pas besoin de tout réécrire.
Le revers : quand quelque chose ne monte pas, il faut savoir que ce n'est pas ton `fstab` qui est en cause, mais l'unité générée à partir de lui. `systemctl status` sur l'unité concernée te le dira.
Vérifier ce qui est réellement en vigueur
Tu modifies un fichier. Tu recharges. Le comportement ne change pas. Tu doutes.`systemd-delta` existe pour ça. Il liste les unités dont la configuration a été surchargée ou modifiée par rapport à l'original.
``` systemd-delta ```
Tu vois immédiatement quels fichiers dans `/etc/` écrasent quoi dans `/usr/lib/`. Et si tu as oublié une surcharge il y a huit mois, tu la retrouves.
Pour l'état d'une unité :
``` systemctl status mon-service.service ```
La sortie te dit si elle est active, depuis quand, sous quel PID, et les dernières lignes de log.
Pour les logs complets :
``` journalctl -u mon-service.service ```
Et pour suivre en direct :
``` journalctl -u mon-service.service -f ```
Après chaque modification d'un fichier d'unité, il faut recharger systemd :
``` systemctl daemon-reload ```
Oublier cette commande est l'erreur la plus fréquente. Le fichier change sur le disque, systemd continue d'utiliser l'ancienne version en mémoire. Tu perds vingt minutes à chercher un bug qui n'existe pas.
Ce que ça coûte
systemd fait beaucoup. Trop, diront certains.Il gère les services, les logs, le réseau, les sessions utilisateur, les conteneurs, les montages. C'est un bloc monolithique là où l'ancien monde empilait des outils séparés.
Le prix de cette intégration, c'est la complexité. Une unité mal écrite peut bloquer le boot. Un `Requires=` mal placé peut empêcher un service critique de démarrer. Le format est lisible, mais il a ses pièges, et la documentation est dense.
L'alternative existe. OpenRC, runit, s6 font le travail différemment, avec des scripts shell et une philosophie Unix plus classique. Sur un serveur minimal ou un conteneur, ces outils ont des arguments solides.
Mais sur une distribution grand public en 2025, systemd est là. Debian, Ubuntu, Fedora, RHEL, Arch, openSUSE l'ont adopté. Refuser systemd, c'est souvent refuser la distribution elle-même.
Alors autant apprendre à écrire une unité correcte. C'est une compétence qui sert longtemps.
Le vrai critère
Tout dépend de ce que tu administres.Si tu gères une machine ou deux, que tu déploies des services standards, et que tu veux des logs centralisés sans effort, systemd te simplifie la vie. Les unités sont un format que tu apprends une fois et que tu réutilises partout.
Si tu construis des conteneurs minimaux, si tu veux un init qui fait une seule chose, ou si tu as une raison précise de préférer des scripts shell explicites, les alternatives tiennent la route. Elles demandent plus de travail au départ et t'en rendent moins ensuite.
La question n'est pas de savoir lequel est le meilleur. Elle est de savoir combien de temps tu veux passer à comprendre ton init, et combien de temps tu veux qu'il te fasse gagner.
Le fichier d'unité que tu écris aujourd'hui, tu le reliras dans deux ans. Écris-le pour cette personne-là.