Credit : Logo officiel
Les bases de systemd : gerer ses services Linux
Les bases de systemd : gerer ses services Linux
Vendredi 23h, un client m'appelle : son site WordPress repond plus. Je me connecte en SSH au VPS Debian 12, je tape systemctl status nginx et la, le verdict tombe en deux secondes : Active: failed (Result: exit-code). Trois minutes plus tard apres avoir lu les logs avec journalctl -u nginx -n 50, je vois le probleme (un fichier de config corrompu apres un apt upgrade mal gere). Sans systemd, j'aurais cherche dans dix repertoires de logs differents.
C'est exactement pour ca que je commence chaque diagnostic par systemctl et journalctl. En 2026, sur Debian 12, Ubuntu 24.04, Rocky Linux 9 et a peu pres toutes les distros serveur, c'est systemd qui tient les rennes : Nginx, PHP 8.3-FPM, MariaDB, Redis, fail2ban, ton app Node.js, ton agent CrowdSec... tout passe par lui.
systemctl : la commande centrale
Gerer les services au quotidien
Les operations de base que je tape vingt fois par jour :
# Demarrer, arreter, redemarrer
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
# Recharger la config sans couper le service (zero downtime)
systemctl reload nginx
# Activer/desactiver au demarrage du serveur
systemctl enable nginx
systemctl disable nginx
# Combo : activer ET demarrer maintenant
systemctl enable --now nginx
# Verifier l'etat
systemctl status nginx
La difference entre restart et reload est cruciale. reload envoie un signal SIGHUP au processus, qui relit sa config sans tuer les connexions en cours. restart arrete tout puis redemarre, donc tu coupes les visiteurs. Sur un site en production avec du trafic, je fais toujours reload quand le service le supporte (Nginx, PHP-FPM, sshd...).
Sortie typique de systemctl status nginx quand tout va bien :
# nginx.service - A high performance web server and a reverse proxy server
# Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
# Active: active (running) since Wed 2026-05-07 14:22:18 CEST; 3 days ago
# Docs: man:nginx(8)
# Main PID: 1234 (nginx)
# Tasks: 5 (limit: 4915)
# Memory: 12.4M
# CPU: 1min 42.318s
Les infos importantes a regarder : Loaded (le service est-il connu ?), Active (running, failed, inactive ?), Main PID (utile pour strace ou lsof), et Memory/CPU pour reperer une fuite memoire naissante.
Explorer les services en cours
# Lister tous les services actifs
systemctl list-units --type=service --state=active
# Services en echec (toujours bon a verifier apres un reboot)
systemctl list-units --type=service --state=failed
# Services qui ont ete charges mais sont inactifs
systemctl list-units --type=service --state=inactive
# Dependances d'un service
systemctl list-dependencies nginx
# Que depend DE ce service ?
systemctl list-dependencies nginx --reverse
# Reponses booleennes pour scripts
systemctl is-active nginx
systemctl is-enabled nginx
systemctl is-failed nginx
Un truc que je fais systematiquement apres un reboot ou une mise a jour majeure : systemctl --failed. Ca te montre direct si un service a pas reussi a demarrer. Sur un serveur freshly upgrade vers Debian 12, j'ai souvent du php8.2-fpm en failed parce que la config a evolue vers PHP 8.3.
journalctl : lire les logs comme un pro
journalctl c'est l'outil pour consulter les logs systemd. Sous-utilise par la plupart des gens qui continuent a faire tail -f /var/log/syslog. Pourtant journalctl est beaucoup plus puissant.
# Logs d'un service
journalctl -u nginx
journalctl -u php8.3-fpm
# En temps reel (comme tail -f)
journalctl -u nginx -f
# Par date
journalctl -u nginx --since "2026-05-07 08:00" --until "2026-05-07 12:00"
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx --since yesterday
# Dernieres N lignes
journalctl -u nginx -n 50
# Logs du demarrage actuel
journalctl -b
# Logs du boot precedent (utile apres un crash)
journalctl -b -1
# Par priorite (emerg, alert, crit, err, warning, notice, info, debug)
journalctl -p err
journalctl -p warning -u nginx
# Sortie JSON pour parsing
journalctl -u nginx -o json --since "1 hour ago"
# Filtrer par PID ou utilisateur
journalctl _PID=1234
journalctl _UID=33
# Espace utilise par les logs
journalctl --disk-usage
# Nettoyer
journalctl --vacuum-time=7d
journalctl --vacuum-size=500M
Sur un VPS IONOS j'ai eu le cas ou les logs systemd prenaient 4.2 Go a force d'accumuler les erreurs PHP. Un coup de journalctl --vacuum-size=500M et c'etait regle. Pour limiter ca de maniere permanente, edite /etc/systemd/journald.conf :
[Journal]
SystemMaxUse=500M
SystemKeepFree=1G
MaxRetentionSec=2week
ForwardToSyslog=no
Puis systemctl restart systemd-journald. Tes logs seront capes a 500 Mo et retenus 2 semaines max.
Anatomie d'un unit file
Un unit file decrit comment systemd doit gerer un service. Trois sections cles : [Unit], [Service], [Install].
Creer un service personnalise pour une app Node.js
Admettons une app Express qui ecoute sur le port 3000. Cree /etc/systemd/system/mon-app.service :
[Unit]
Description=Mon application Node.js
Documentation=https://github.com/moi/mon-app
After=network.target
Wants=network-online.target
Requires=mariadb.service
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/mon-app
EnvironmentFile=/etc/mon-app/env.conf
ExecStart=/usr/bin/node server.js
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
StartLimitInterval=60
StartLimitBurst=3
StandardOutput=journal
StandardError=journal
SyslogIdentifier=mon-app
# Securite (durcissement)
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/var/www/mon-app/storage
ProtectHome=true
PrivateTmp=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
# Limites de ressources
MemoryMax=512M
CPUQuota=80%
TasksMax=100
[Install]
WantedBy=multi-user.target
Quelques points cles :
Type=simple: le processus reste au premier plan. Pour des forks, utiliseforking. Pour des scripts one-shot,oneshot.Restart=on-failure+RestartSec=5: si l'app crashe, systemd la relance apres 5s. Combinaison gagnante.StartLimitBurst=3: si elle crashe 3 fois enStartLimitInterval=60secondes, systemd abandonne. Evite les boucles de redemarrage qui consomment du CPU pour rien.- Section securite :
ProtectSystem=strictrend tout le systeme de fichiers en lecture seule sauf lesReadWritePathsdeclares. Si ton app est compromise, elle peut pas ecrire ailleurs.
Active et demarre :
systemctl daemon-reload
systemctl enable mon-app
systemctl start mon-app
systemctl status mon-app
Le daemon-reload c'est OBLIGATOIRE apres chaque modif d'un unit file. Oublie pas, sinon systemd continue d'utiliser l'ancienne version en cache et tu vas te demander pourquoi tes changements s'appliquent pas.
Les timers : remplacer cron proprement
Les timers systemd sont une alternative moderne a cron. Mieux integres, journalises, avec gestion des dependances et rattrapage si le serveur etait eteint.
Le service dans /etc/systemd/system/backup.service :
[Unit]
Description=Sauvegarde quotidienne des bases
Wants=mariadb.service
After=mariadb.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=root
Nice=10
IOSchedulingClass=idle
Puis le timer dans /etc/systemd/system/backup.timer :
[Unit]
Description=Lancer la sauvegarde tous les jours a 3h
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=300
AccuracySec=1m
[Install]
WantedBy=timers.target
Active le timer :
systemctl daemon-reload
systemctl enable --now backup.timer
Verifie les timers actifs et leur prochaine execution :
systemctl list-timers --all
# NEXT LEFT LAST PASSED UNIT ACTIVATES
# Thu 2026-05-08 03:00:00 CEST 12h left Wed 2026-05-07 03:00:13 CEST 11h ago backup.timer backup.service
# Thu 2026-05-08 06:30:42 CEST 16h left Wed 2026-05-07 06:30:42 CEST 8h ago apt-daily.timer apt-daily.service
Pourquoi les timers plutot que cron ?
- Journalisation :
journalctl -u backup.servicete donne tout l'historique des executions et leurs sorties. - Persistent=true : si le serveur etait eteint au moment prevu, le timer rattrape l'execution au reboot. Cron oublie.
- RandomizedDelaySec : repartit la charge si tu as plusieurs serveurs (utile pour pas saturer un point de stockage commun).
- Dependances : tu peux specifier qu'un timer doit attendre que MariaDB soit demarre.
- Reutilisable : meme service appele par un timer ou manuellement avec
systemctl start backup.service.
Erreurs courantes et leur fix
Failed to start service.service: Unit not found
Cause : tu as cree le unit file mais oublie le daemon-reload. Ou le fichier a un nom incorrect (doit finir par .service).
Fix :
systemctl daemon-reload
ls -la /etc/systemd/system/mon-app.service
systemctl status mon-app.service
Job for nginx.service failed because the control process exited with error code
Cause : la config Nginx a une erreur de syntaxe. Classique apres un edit a la main.
Fix :
nginx -t
# nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/monsite:42
journalctl -u nginx -n 30
# Lis la stack et corrige la ligne incriminee
start request repeated too quickly
Cause : ton service crashe en boucle, systemd a abandonne apres StartLimitBurst tentatives.
Fix :
systemctl reset-failed mon-app
journalctl -u mon-app -n 100 --no-pager
# Identifie la cause du crash, corrige, puis :
systemctl start mon-app
Failed to connect to bus: No such file or directory
Cause : tu es dans un container ou un chroot sans systemd.
Fix : utilise service nginx restart ou directement les binaires. systemd ne tourne pas dans tous les environnements.
Le service demarre mais s'arrete tout de suite
Cause : Type=simple declare mais le binaire fork (cas typique avec PHP-FPM mal configure) ou inversement.
Fix : verifie le Type= dans le unit file. Pour les daemons qui forkent, utilise Type=forking avec un PIDFile=.
Depannage : la checklist
Quand un service deconne, je suis toujours cette sequence :
# 1. Etat actuel
systemctl status mon-app
# 2. Logs recents
journalctl -u mon-app -n 50 --no-pager
# 3. Logs depuis le dernier demarrage
journalctl -u mon-app -b
# 4. Recharger apres modif
systemctl daemon-reload
# 5. Reset si stuck en failed
systemctl reset-failed mon-app
# 6. Analyser le temps de demarrage du systeme
systemd-analyze
systemd-analyze blame | head -20
systemd-analyze critical-chain
systemd-analyze blame est super pour identifier ce qui ralentit le boot. Sur un serveur que j'ai recupere, j'ai vu que apt-daily.service prenait 2 minutes au demarrage. Apres investigation, c'etait un cache APT corrompu. apt clean && apt update et hop, boot 4x plus rapide.
Cas pratique : monitorer ses services
Un workflow que j'ai mis en place sur tous mes VPS : un check automatise toutes les 5 minutes pour detecter les services en panne et m'alerter par webhook. Cree /usr/local/bin/check-services.sh :
#!/bin/bash
set -euo pipefail
SERVICES=(nginx php8.3-fpm mariadb redis-server fail2ban)
WEBHOOK="https://hooks.slack.com/services/XXX/YYY/ZZZ"
FAILED=()
for s in "${SERVICES[@]}"; do
if ! systemctl is-active --quiet "$s"; then
FAILED+=("$s")
fi
done
if [ ${#FAILED[@]} -gt 0 ]; then
MESSAGE="Services en panne sur $(hostname) : ${FAILED[*]}"
curl -s -X POST -H 'Content-Type: application/json' \
-d "{\"text\":\"$MESSAGE\"}" "$WEBHOOK"
echo "$(date) $MESSAGE" >> /var/log/check-services.log
fi
Planifie-le via un timer systemd (boucle bouclee) :
# /etc/systemd/system/check-services.timer
[Unit]
Description=Check services every 5 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
systemctl enable --now check-services.timer
Bonus : les targets et le boot
systemd organise le demarrage avec des targets (equivalents des runlevels SysV). Les principaux :
multi-user.target: mode serveur sans GUI (l'equivalent du runlevel 3)graphical.target: mode bureau avec interface graphiquerescue.target: mode minimal pour la maintenanceemergency.target: encore plus minimal, juste un shellreboot.target,poweroff.target: redemarrer/eteindre
# Voir le target par defaut
systemctl get-default
# Changer (utile pour desactiver un GUI sur un serveur)
systemctl set-default multi-user.target
# Changer immediatement (sans reboot)
systemctl isolate multi-user.target
# Booter en mode rescue depuis GRUB :
# Ajouter "systemd.unit=rescue.target" a la ligne kernel
C'est rare d'avoir a toucher aux targets sur un serveur de prod, mais utile a connaitre quand un boot deconne.
Pour aller plus loin
- Securiser SSH avec sshd_config : un SSH proprement durci, indispensable avant d'exposer ton serveur.
- Logs Linux : ou chercher et comment lire : journalctl c'est cool, mais y a aussi /var/log a maitriser.
- Automatiser ses backups avec Bash et cron : pour comparer avec l'approche timers presentee ici.
- Hardening Linux : securiser un serveur : pour aller plus loin sur les sandbox systemd et capabilities.
- Configurer iptables sur Linux : completer la securite reseau avec un pare-feu propre.
Le reflexe systemd
Maitriser systemctl et journalctl, c'est gagner 80% du temps de diagnostic sur un serveur Linux moderne. Cree-toi des unit files propres pour tes apps custom, utilise les timers a la place de cron quand tu veux du logging serieux, et durcis chaque service avec les options de sandbox. Plus tu pratiques, plus ces commandes deviennent un reflexe quasi-musculaire. Et c'est exactement comme ca qu'a 23h un vendredi tu retrouves l'origine d'une panne en trois minutes.