Le service systemd ne démarre pas — les cinq causes, et le log qui les nomme
Presque jamais mystérieux : systemctl status, les 50 dernières lignes du journal — et l'une des cinq causes est nommée noir sur blanc. Les correctifs, ensuite ? De l'appariement de motifs.
■ espace · voie prose
systemctl status + journalctl -u <unit> -n 50 --no-pager — une des cinq causes est nommée dans le log.
203/EXEC = chemin, shebang ou bit d'exécution — systemd lance ExecStart= sans shell.
Échec au boot uniquement ? Une course d'ordonnancement — ajoutez After=/Wants= et une garde ExecStartPre=.
LES DEUX COMMANDES
systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager
status vous donne l'état — inactive (dead), failed (exit-code), activating (auto-restart) — et les dernières lignes de log. Le journal donne l'histoire complète : stdout, stderr, et les doléances de systemd à propos de l'unité. Échec avant, et vous voulez celui run-là ? Ajoutez -b pour le boot courant, ou --since today. Une dernière paire à connaître :
systemctl list-units --failed # every red unit on the box
systemctl reset-failed myapp.service # clear the failed state after the fix
verify: systemctl list-units --failed → 0 unités chargées listées
LES CINQ CAUSES
Après que le log a nommé le symptôme, la cause est presque toujours une de celles-ci — vérifiez d'abord la plus probable :
| Le log dit | Cause probable | Correctif |
|---|---|---|
status=203/EXEC | Mauvais ExecStart=chemin ou interpréteur manquant | Chemin absolu, chmod +x, vérifiez le shebang |
status=203/EXEC sur un script | Fins de ligne CRLF ou mauvais shebang | dos2unix script.sh, réparez la première ligne |
status=1/FAILURE, aucun log d'app | Répertoire de travail ou var d'env manquante | Réglez WorkingDirectory=, ajoutez Environment= |
Permission denied | L'utilisateur ne peut lire des fichiers ni lier le port | Corrigez la possession ; les ports sous 1024 exigent AmbientCapabilities=CAP_NET_BIND_SERVICE ou root |
Unit is masked | Quelqu'un a lancé systemctl mask | systemctl unmask myapp.service |
| L'édition du fichier d'unité « ne fait rien » | Daemon pas rechargé | systemctl daemon-reload |
LA FAMILLE 203/EXEC
Celle qui bouffe les après-midis. Systemd n'utilise pas votre shell pour lancer ExecStart= — pas d'expansion de ~, pas de PATH lookup, pas de cd. Regardez les mauvaises lignes devenir justes :
Un script qui tourne très bien depuis votre terminal mais échoue avec 203 sous systemd, c'est presque toujours une de ces quatre : non exécutable, fins CRLF, shebang vers nulle part, ou chemin relatif.
MARCHE À LA MAIN, MEURT AU BOOT
L'échec au boot, c'est de l'ordonnancement, pas de la syntaxe. La course classique : votre service attrape le réseau, un disque monté ou une base de données avant que ça existe. Déclarez les dépendances au lieu d'espérer :
[Unit]
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.toml
After= ordonne le démarrage ; Wants= fait que systemd remonte réellement la dépendance. network-online.target ne marche que si le service network-wait est activé sur votre distro — vérifiez systemctl is-enabled NetworkManager-wait-online.service (ou l'équivalent systemd-networkd). La garde ExecStartPre= échoue bruyamment avec un message lisible au lieu d'une trace d'appel.
verify: systemctl is-enabled NetworkManager-wait-online.service → enabled
La deuxième variante : démarre au boot puis meurt aussitôt. Cherchez ce que votre environnement interactif avait et pas le boot — PATH différentes, un virtualenv, un HOME. Systemd démarre les unités avec un environnement dénudé ; tout ce que votre shell fournit implicitement doit être épelé :
[Service]
Environment="PATH=/opt/app/venv/bin:/usr/bin"
User=appuser
WorkingDirectory=/opt/app
Pour les vrais mystères, lancez la commande exacte ExecStart= comme l'utilisateur de l'unité, dans un shell — la plupart des différences d'environnement font surface dans les dix premières secondes.
RIEN DANS LE JOURNAL ?
StandardOutput=etStandardError=dans l'unité — ils doivent êtrejournal(défaut) oujournal+console. Quelqu'un les a peut-être mis surnull.- L'app écrit dans un fichier au lieu du stdout. Systemd ne capture que stdout/stderr ; les écrivains de fichiers de log contournent complètement le journal. Pointez l'app vers stdout ou lisez le fichier.
- Des limites de stockage ont jeté des vieilles lignes :
journalctl --disk-usage, etSystemMaxUse=dans/etc/systemd/journald.confsi le journal s'étouffe.
REDÉMARRER APRÈS UN CRASH
La politique par défaut est Restart=no : un service crashé reste mort, et vous l'apprenez par un utilisateur. Réglez ça service par service :
[Service]
Restart=on-failure
RestartSec=5
verify: systemctl show myapp.service --property=Restart → Restart=on-failure
| Réglage | Re-démarre quand |
|---|---|
no (défaut) | Jamais |
on-failure | Code de sortie non-nul, signal, timeout |
always | Toute sortie, même propre |
on-watchdog | Timeout du watchdog seulement |
Jumelez Restart=on-failure avec une garde de cadence de démarrage pour qu'une boucle de crash ne martèle pas la boîte : StartLimitIntervalSec= et StartLimitBurst= dans la section [Unit]. Cinq échecs en soixante secondes devraient pager un humain, pas faire tourner un CPU à vide. Et si vous câblez ça comme un job programmé plutôt qu'un daemon, les timers offrent le logging journal et l'ordonnancement de dépendances gratuitement — pesez d'abord cron contre un timer.
LA CHECK-LIST DE 60 SECONDES
systemctl status <unit>— lisez l'état et les dernières lignes.journalctl -u <unit> -n 50 --no-pager— trouvez la vraie erreur.203/EXEC? Corrigez chemin, shebang, permissions.Permission denied? Corrigez l'utilisateur et la possession des fichiers.- Échec au boot uniquement ? Ajoutez
After=/Wants=network-online.targetet une gardeExecStartPre=. - Changé un fichier d'unité ?
systemctl daemon-reload && systemctl restart <unit>. - Ajoutez
Restart=on-failurepour que le prochain crash s'annonce au lieu de se cacher.
La plupart des moments « systemd c'est compliqué » se réduisent à l'erreur était dans le journal depuis le début. Lisez-le avant d'éditer le fichier d'unité, pas après.
fin des cinq causes · rien n'a été téléchargé · tout est local