L’issue s’ouvre déjà remplie avec le titre et l’adresse de cette page.
systemctl est la commande centrale pour gérer les services sous Linux. Elle liste, démarre, arrête, recharge et diagnostique tout ce que systemd pilote sur un serveur moderne. Cette page couvre le cycle de vie d'un service, les deux façons de lister les services (celle qui manque à la plupart des lecteurs), la création d'une unité et le diagnostic de base d'un service qui refuse de tourner. Public visé : débutant qui découvre systemctl et administrateur qui veut arrêter de se faire piéger par un start trop optimiste.
Toutes les sorties reproduites ici proviennent d'un serveur Debian 13 avec systemd 257 et nginx installé. Les commandes sont identiques sur Ubuntu, Debian et RHEL ; seules quelques sorties d'enable diffèrent, et c'est signalé.
L'essentiel du travail quotidien sur les services tient dans cinq verbes. Chacun agit immédiatement, sur le système tel qu'il tourne en ce moment, et aucun ne survit à un redémarrage de la machine : l'activation au boot est une notion séparée. Toutes ces commandes modifient l'état du système, elles demandent donc les privilèges root via sudo, sauf status qui se contente de lire.
Fenêtre de terminal
sudosystemctlstartnginx# démarrer maintenant
sudosystemctlstopnginx# arrêter maintenant
sudosystemctlrestartnginx# arrêter puis redémarrer
sudosystemctlreloadnginx# relire la configuration sans couper
systemctlstatusnginx# où en est-on ?
La bonne habitude consiste à enchaîner l'action et sa vérification dans la même ligne de commande. Le shell n'exécute la seconde partie que si la première a réussi, et is-active répond en un mot :
Fenêtre de terminal
systemctlstartnginx && systemctlis-activenginx
active
Le suffixe .service est facultatif : sans suffixe, systemctl ajoute .service par défaut, donc systemctl start nginx et systemctl start nginx.service sont strictement équivalents. Le suffixe devient indispensable pour viser une autre unité du même nom, un timer ou un socket : systemctl status logrotate affiche le service, et seul systemctl status logrotate.timer montre la planification.
reload demande au service de relire sa configuration pendant qu'il continue de servir les requêtes en cours. restart tue le processus puis en lance un nouveau : les connexions en cours tombent. Sur un serveur web en production, la différence se voit dans les logs des clients.
systemctl status est la fiche d'identité d'un service : d'où vient son fichier de configuration, s'il tourne, depuis quand, avec quel PID, et les dernières lignes de son journal. C'est le premier réflexe devant un service suspect.
Fenêtre de terminal
systemctlstatusnginx
● nginx.service - A high performance web server and a reverse proxy server
├─81 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
├─82 "nginx: worker process"
├─83 "nginx: worker process"
(... un processus worker par cœur, 17 tâches au total ...)
Jul 12 16:54:11 srv01 systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Jul 12 16:54:11 srv01 systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.
Chaque ligne répond à une question précise, et les trois premières suffisent le plus souvent :
Ligne
Ce qu'elle vous dit
Loaded
Chemin du fichier unité utilisé, et si le service est activé au boot (enabled / disabled / masked)
Active
État courant et durée : active (running), inactive (dead), failed
Process
Les commandes déjà terminées et leur code de sortie (status=0/SUCCESS ou une erreur)
Main PID
Le PID du processus principal ; s'il est absent, rien ne tourne
CGroup
L'arborescence réelle des processus rattachés au service
Glissez pour voir
La ligne Loaded est celle qu'on oublie de lire. Elle indique le chemin exact du fichier unité chargé, ce qui évite de passer dix minutes à éditer un fichier que systemd n'utilise pas.
Dans un script, un status complet est inexploitable : il faut une réponse courte et un code de retour fiable. Trois commandes le font, et elles s'utilisent aussi bien à la main que dans une automatisation Ansible ou un script de déploiement.
Fenêtre de terminal
systemctlis-activenginx# active | inactive | failed | activating
C'est la question la plus posée sur systemctl, et la réponse est piégeuse : il existe deux listes de services, elles ne contiennent pas les mêmes choses, et la commande qu'on trouve partout (systemctl list-units) est justement celle qui cache une partie des services de la machine.
La distinction tient en une phrase : list-units liste les unités chargées en mémoire par systemd ; list-unit-files liste les fichiers d'unité installés sur le disque. Un service installé par un paquet mais jamais démarré existe bien sur le disque, mais systemd ne l'a pas chargé, donc il n'apparaît pas dans list-units. Sur le serveur de démonstration, l'écart est spectaculaire : 14 services chargés contre 119 fichiers d'unité installés.
systemctl list-units --type=service répond à la question « qu'est-ce que systemd manipule en ce moment ? ». C'est la vue runtime, celle qu'on veut quand on inspecte un serveur vivant.
Fenêtre de terminal
systemctllist-units--type=service
UNIT LOAD ACTIVE SUB DESCRIPTION
ldconfig.service loaded active exited Rebuild Dynamic Linker Cache
nginx.service loaded active running A high performance web server and a reverse proxy server
systemd-journal-catalog-update.service loaded active exited Rebuild Journal Catalog
systemd-journal-flush.service loaded active exited Flush Journal to Persistent Storage
systemd-journald.service loaded active running Journal Service
systemd-remount-fs.service loaded active exited Remount Root and Kernel File Systems
systemd-sysctl.service loaded active exited Apply Kernel Variables
systemd-sysusers.service loaded active exited Create System Users
systemd-tmpfiles-setup-dev-early.service loaded active exited Create Static Device Nodes in /dev gracefully
systemd-tmpfiles-setup-dev.service loaded active exited Create Static Device Nodes in /dev
systemd-tmpfiles-setup.service loaded active exited Create System Files and Directories
systemd-update-done.service loaded active exited Update is Completed
systemd-user-sessions.service loaded active exited Permit User Sessions
14 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.
Trois colonnes portent l'information utile. LOAD dit si le fichier unité a été lu correctement, ACTIVE donne l'état général et SUB le détail : un service active (exited) a terminé sa tâche avec succès et ne laisse aucun processus derrière lui, ce qui est normal pour les tâches de démarrage. Le point noir en début de ligne signale les unités qui demandent votre attention, ici systemd-modules-load.service en failed.
Les deux dernières lignes de la sortie sont un mode d'emploi que systemd vous donne gratuitement : --all pour voir aussi les unités chargées mais inactives, list-unit-files pour voir tout ce qui est installé.
systemctl list-unit-files --type=service répond à la question « qu'est-ce qui est installé sur cette machine, et est-ce que ça démarrera au boot ? ». C'est la vue disque, celle qu'on veut pour un audit ou pour trouver un service qu'on n'a jamais démarré.
La sortie complète compte 119 lignes ; l'extrait ci-dessus montre un exemplaire de chaque état rencontré. La colonne STATE est le cœur de l'affaire : elle dit ce que systemd fera de ce service au prochain démarrage. La colonne PRESET indique ce que la distribution recommandait par défaut, ce qui permet de repérer d'un coup d'œil les services dont l'état a été modifié à la main.
STATE
Ce que ça signifie
enabled
Démarrera au boot : un lien symbolique existe dans un répertoire .wants
disabled
Ne démarrera pas au boot, mais reste démarrable à la main
static
Aucune section [Install] : ne peut pas être activé, il est tiré par une dépendance
masked
Lié vers /dev/null : impossible à démarrer, même manuellement
alias
Autre nom d'un service existant (sur Debian, kmod.service est un lien vers systemd-modules-load.service)
enabled-runtime
Activé jusqu'au prochain reboot seulement, l'activation est en /run et non en /etc
indirect
Activable, mais l'activation réelle passe par un autre service du même groupe
generated
Fabriqué à la volée par un générateur, typiquement à partir d'un vieux script SysV
Glissez pour voir
L'état static est celui qui surprend le plus. Un systemctl enable systemd-tmpfiles-clean.service ne fera rien, non pas à cause d'une erreur de votre part, mais parce que ce service n'a pas vocation à être activé : il est déclenché par d'autres unités. La commande le confirme en un mot.
Lister les 119 unités d'un serveur n'a aucun intérêt opérationnel. Ce qu'on veut, c'est filtrer : quels services tournent, lesquels sont tombés, lesquels démarreront au prochain boot. Le filtre --state= s'applique aux deux commandes, avec des valeurs différentes puisqu'elles ne parlent pas de la même chose.
Les services qui tournent vraiment, processus vivant à l'appui :
Fenêtre de terminal
systemctllist-units--type=service--state=running
UNIT LOAD ACTIVE SUB DESCRIPTION
nginx.service loaded active running A high performance web server and a reverse proxy server
systemd-journald.service loaded active running Journal Service
2 loaded units listed.
Les services qui démarreront au prochain boot, la commande à connaître pour un audit de serveur :
Deux options complètent la panoplie. --all ajoute les unités chargées mais inactives, ce qui fait passer la liste de 14 à 65 entrées sur la machine de démonstration : c'est là que réapparaissent les services arrêtés. --no-legend supprime l'en-tête et la légende, ce qui rend la sortie exploitable dans un script.
Un service traverse plusieurs états au cours de sa vie, et systemctl status les affiche sur la ligne Active. Les connaître évite les faux diagnostics : un service active (exited) n'est pas cassé, il a simplement fini son travail.
État
Signification
active (running)
Le service tourne, un processus est vivant
active (exited)
La tâche s'est terminée avec succès, aucun processus ne subsiste (Type=oneshot)
inactive (dead)
Le service est arrêté, sans erreur
failed
Le service a échoué, le journal contient la raison
activating
Le démarrage est en cours
deactivating
L'arrêt est en cours
masked
Le service est bloqué, un start refusera de faire quoi que ce soit
C'est la confusion de départ de tout débutant, et elle produit des serveurs qui tournent parfaitement jusqu'au premier redémarrage. Les deux notions sont indépendantes : l'une parle du présent, l'autre du prochain boot.
Concept
Question posée
Commande
active
Le service tourne-t-il en ce moment ?
systemctl is-active nginx
enabled
Démarrera-t-il tout seul au prochain boot ?
systemctl is-enabled nginx
Glissez pour voir
Un service peut être enabled et inactive : il est arrêté maintenant, il repartira au reboot. Il peut être active et disabled : il tourne, mais il ne survivra pas au redémarrage. Ce dernier cas est le scénario classique du service installé un vendredi soir et disparu le lundi matin.
Le schéma sépare les deux axes : à gauche l'état maintenant, que changent start et stop ; à droite l'état au prochain boot, que changent enable, disable et mask. Aucune flèche ne relie les deux colonnes, et c'est tout le propos.
Deux commandes suffisent au quotidien. enable crée le lien symbolique qui fera démarrer le service au boot, enable --now fait les deux d'un coup, et disableretire le lien.
Fenêtre de terminal
sudosystemctlenablenginx# au prochain boot
sudosystemctlenable--nownginx# au prochain boot ET maintenant
sudosystemctldisablenginx# ne démarrera plus au boot
systemctlis-enablednginx# vérification
La sortie d'enable dépend de la distribution, et c'est déroutant la première fois. Sur RHEL, systemd affiche le lien symbolique créé. Sur Debian et Ubuntu, un paquet qui embarque encore un script SysV (c'est le cas de nginx) déclenche d'abord une synchronisation, et le message parle de SysV sans mentionner le moindre lien :
Synchronizing state of nginx.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Dans tous les cas, la seule vérification qui compte est systemctl is-enabled nginx, qui doit répondre enabled. Ne vous fiez pas au message affiché, fiez-vous à l'état.
disable empêche le démarrage automatique, mais laisse la porte ouverte à un systemctl start manuel, y compris déclenché par une dépendance d'un autre service. mask ferme la porte : systemd crée un lien vers /dev/null, et plus rien ne peut lancer le service.
Fenêtre de terminal
sudosystemctlmasknginx
Created symlink '/etc/systemd/system/nginx.service' → '/dev/null'.
Toute tentative de démarrage échoue alors avec un message sans ambiguïté, et c'est exactement le comportement recherché pour un service qui ne doit jamais tourner sur un serveur, comme Bluetooth, Avahi ou CUPS :
Failed to start nginx.service: Unit nginx.service is masked.
Le retour en arrière se fait avec sudo systemctl unmask nginx, qui supprime le lien vers /dev/null et rend le service démarrable. Un systemctl is-enabled nginx confirme la sortie de l'étatmasked.
mask a une limite qui surprend : il ne s'applique qu'à une unité livrée par un paquet, rangée dans /usr/lib/systemd/system/. Pour une unité que vous avez écrite vous-même dans /etc/systemd/system/, le lien vers /dev/null prendrait la place de votre fichier, et systemctl refuse avec Failed to mask unit: File '/etc/systemd/system/mon-app.service' already exists et un code de retour 1 (mesuré sur Debian 13 et AlmaLinux 10). Pour neutraliser une telle unité, disable --now l'arrête et la retire du boot ; seule la suppression du fichier, suivie d'un daemon-reload, empêche tout démarrage.
Faire tourner une application métier comme un service systemd apporte trois choses qu'un nohup ne donnera jamais : le redémarrage automatique en cas de crash, le démarrage au boot et les logs centralisés dans le journal. Le fichier d'unité vit dans /etc/systemd/system/ (le répertoire réservé à l'administrateur, prioritaire sur les unités livrées par les paquets).
Un fichier d'unité se lit en trois blocs, et chacun répond à une question différente : [Unit] décrit le service et ses dépendances, [Service] dit comment lancer le processus, [Install] dit quand l'activer au boot. Retenez le piège qui en découle : sans section [Install], le service est static et systemctl enable n'aura aucun effet.
Créer le fichier d'unité avec l'éditeur intégré, qui gère le chemin et les droits pour vous :
Fenêtre de terminal
sudosystemctledit--full--forcemon-app.service
Décrire le service, en nommant explicitement l'utilisateur non privilégié qui l'exécutera :
Activer et démarrer le service. systemctl edit a déjà rechargé la configuration de systemd à la fermeture de l'éditeur ; daemon-reload reste indispensable dès que le fichier est écrit autrement, par un cp ou un outil de déploiement :
Fenêtre de terminal
sudosystemctldaemon-reload# inutile après systemctl edit, indispensable après un cp
sudosystemctlenable--nowmon-app.service
Vérifier réellement que le service tient debout, en laissant passer quelques secondes :
Fenêtre de terminal
sleep3 && systemctlis-activemon-app.service
La sortie doit afficher active. Si elle affiche failed, journalctl -xeu mon-app.service donne la raison.
Oublier daemon-reload après avoir modifié un fichier d'unité sans passer par systemctl edit est l'erreur la plus fréquente : systemd continue d'utiliser la version en mémoire, et vos changements semblent sans effet.
La directive Type= dit à systemd comment savoir que le service est démarré. C'est le réglage le plus mal compris de tout systemd, et le mauvais choix produit des diagnostics faux.
Type
Comportement
Quand l'utiliser
simple
Le processus lancé par ExecStart est le service ; systemd le considère démarré dès le fork
Le défaut quand ExecStart= est présent, à éviter au profit de exec
exec
Comme simple, mais systemd attend que l'exec() réussisse
Le bon défaut pour un binaire au premier plan (systemd 240 et plus)
forking
Le processus se détache, systemd suit le processus fils
Daemons traditionnels avec un PIDFile=
oneshot
Exécute une commande puis se termine
Scripts de maintenance, tâches ponctuelles
notify
Le service prévient systemd qu'il est prêt via sd_notify()
Applications compatibles, idéal pour l'ordre de démarrage
idle
Attend que les autres jobs soient finis
Cas rares, affichage console
Glissez pour voir
Le piège : Type=simple ne vérifie pas que votre binaire existe
Voici le comportement que presque personne n'explique, et qui coûte des heures de débogage. Avec Type=simple, systemd considère le service démarré dès qu'il a forké, donc avant même d'avoir tenté d'exécuter le binaire. La documentation officielle l'écrit noir sur blanc : systemctl startsignale un succès même si le binaire du service ne peut pas être lancé. Résultat : la commande rend la main avec un code de retour 0, et le service est déjà mort. Lancé dans la foulée, is-activepeut encore répondre active : systemd bascule l'unité en failed seulement après avoir récolté la mort du processus fils, une fenêtre de quelques millisecondes.
Deux unités identiques, un seul détail qui change, et deux comportements opposés. La première déclare Type=simple, la seconde Type=exec, et toutes deux pointent vers un binaire qui n'existe pas :
[Service]
Type=simple
ExecStart=/opt/demo/absent
Fenêtre de terminal
systemctlstartt-simple; echo"code retour de start = $?"; systemctlis-activet-simple
code retour de start = 0
active
Le démarrage est un succès pour le shell, et le service se déclare actif. Avec Type=exec, systemd attend le résultat de l'exec() et refuse de mentir :
Fenêtre de terminal
systemctlstartt-exec; echo"code retour de start = $?"; systemctlis-activet-exec
Job for t-exec.service failed because the control process exited with error code.
See "systemctl status t-exec.service" and "journalctl -xeu t-exec.service" for details.
code retour de start = 1
failed
La conséquence est très concrète. Un script de déploiement qui teste $? après un systemctl start se croit en succès alors que l'application est morte, et le pipeline passe au vert. Un systemctl status lancé une seconde trop tôt affiche un active (running) parfaitement mensonger, avec un Main PID entre doubles parenthèses qui trahit un processus qui n'a jamais réussi son exec() :
Un service qui meurt à trois heures du matin doit se relever tout seul. La directive Restart= gère ce cas, et les directives StartLimit* empêchent la boucle de redémarrage infinie qui masquerait un vrai problème.
Directive
Valeur
Effet
Restart
on-failure
Redémarre si le code de sortie est non nul, ou après un signal, un timeout ou le watchdog
Restart
always
Redémarre toujours, sauf après un systemctl stop explicite
Restart
no
Ne redémarre jamais (le défaut)
RestartSec
5s
Attend 5 secondes entre deux tentatives
StartLimitBurst
3
Section [Unit] : au-delà de 3 tentatives dans la fenêtre, systemd abandonne
StartLimitIntervalSec
60s
Section [Unit] : la fenêtre de comptage des tentatives
Glissez pour voir
Restart=on-failure couplé à RestartSec=5s est le réglage que le manuel systemd.service(5)recommande pour un service de longue durée. Les deux directives StartLimit* vivent dans [Unit], pas dans [Service] : placé au mauvais endroit, StartLimitIntervalSec= est ignoré avec un simple avertissement (Unknown key 'StartLimitIntervalSec' in section [Service], ignoring, mesuré sur systemd 257 et 239), et votre fenêtre de comptage n'existe pas. Restart=always a un effet de bord vicieux : il relance aussi un service dont la configuration est cassée, ce qui remplit le journal et retarde le diagnostic.
Quand un service est en failed, la cause est presque toujours écrite noir sur blanc dans le journal. Encore faut-il la lire au bon endroit. Ce workflow en quatre temps résout la majorité des cas.
Lire le statut, qui affiche déjà les dernières lignes de journal :
Fenêtre de terminal
systemctlstatusmon-app.service
Ouvrir le journal complet de l'unité, avec les explications de systemd :
Fenêtre de terminal
journalctl-xeumon-app.service
L'option -x ajoute les explications contextuelles, -e saute à la fin, -u filtre sur l'unité.
Repérer le code d'échec. Sur un service dont l'ExecStart= pointe vers un fichier absent, le journal est explicite :
demo.service: Main process exited, code=exited, status=203/EXEC
demo.service: Failed with result 'exit-code'.
Le code 203/EXEC est de loin le plus fréquent : l'exécutable est introuvable, non exécutable, ou son chemin comporte une faute de frappe. Un ls -l sur le chemin de l'ExecStart= tranche en une seconde.
Corriger, recharger, relancer, dans cet ordre :
Fenêtre de terminal
sudosystemctldaemon-reload
sudosystemctlrestartmon-app.service
sleep3 && systemctlis-activemon-app.service
Les codes d'échec les plus courants forment un vocabulaire à connaître par cœur : 203/EXEC pour un chemin invalide, 217/USER pour un User= qui n'existe pas, 200/CHDIR pour un WorkingDirectory= absent, 226/NAMESPACE pour une directive de durcissement trop stricte. systemd-analyze exit-status les liste tous.
Un service accessible depuis le réseau ne devrait pas pouvoir lire /home, charger un module noyau ou obtenir de nouveaux privilèges. systemd fournit des directives de sandboxing qui s'ajoutent à la section [Service] sans modifier une ligne de l'application. Ces quatre-là couvrent déjà l'essentiel du risque et cassent rarement quoi que ce soit :
[Service]
# interdit toute élévation de privilèges (suid)
NoNewPrivileges=true
# tout le système de fichiers en lecture seule, sauf le répertoire de données
ProtectSystem=strict
ReadWritePaths=/var/lib/mon-app
# /home, /root et /run/user invisibles
ProtectHome=true
# un /tmp isolé, invisible des autres services
PrivateTmp=true
Les commentaires sont sur leur propre ligne, et ce n'est pas une question de style : systemd n'accepte aucun commentaire en fin de ligne. Écrite NoNewPrivileges=true # interdit…, la directive est rejetée (Failed to parse NoNewPrivileges=…, ignoring) puis ignorée, et le service tourne sans la protection que vous croyez avoir posée. Mesuré sur les quatre directives ci-dessus, sur systemd 257 et 239 : systemd-analyze verify affiche l'erreur, le démarrage du service ne dit rien.
La commande systemd-analyze security mon-app.service note le service de 0 à 10 (plus le score est bas, mieux c'est) et liste les protections manquantes une par une. C'est la façon la plus rapide de mesurer l'effet d'une directive : on note avant, on ajoute, on note après.
Écrire une unité en lisant un guide et la faire tenir debout sur un système réel sont deux exercices distincts. Dans une VM jetable, vous rédigez un fichier .service complet, le chargez avec daemon-reload, l'activez avec enable --now et contrôlez son état réel avec is-active et le journal.
list-units liste ce qui est chargé en mémoire, list-unit-files ce qui est installé sur le disque. Un service installé mais jamais démarré n'apparaît pas dans list-units sans --all : 14 unités chargées contre 119 fichiers installés sur le serveur de démonstration.
Pour un audit, la bonne commande est systemctl list-unit-files --type=service --state=enabled : elle dit ce qui démarrera au prochain boot.
enabled et active sont indépendants : l'un parle du prochain boot, l'autre de maintenant.
Type=simple ment. Sur un binaire absent, systemctl start renvoie 0 alors que le service est déjà mort en 203/EXEC (et is-active peut répondre active pendant un court instant). Déclarez Type=exec, ou vérifiez l'état quelques secondes après le démarrage.
203/EXEC est le code d'échec le plus fréquent : chemin d'ExecStart= invalide ou fichier non exécutable.
Après toute modification d'un fichier d'unité hors systemctl edit, daemon-reload est obligatoire, sinon systemd garde sa version en cache.
reload relit la configuration sans couper les connexions, restart tue le processus : préférez reload quand l'unité déclare un ExecReload=.
mask est plus strict que disable : il rend un service de paquet impossible à démarrer, même par une dépendance.
Aucun commentaire en fin de ligne dans un fichier d'unité : la directive est ignorée. systemd-analyze verify le signale.
Vérifiez que l'essentiel de ce guide est acquis. Les questions portent
uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
6 questions
6 min.
70% requis
Informations
Le chronomètre démarre au clic sur Démarrer
Questions à choix multiples, vrai/faux et réponses courtes
Vous pouvez naviguer entre les questions
Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
⏱️--:--
0 / 6
Vérification
(0/0)
--:--
0%
0Correctes
0À revoir
0:00Temps
-Niveau
Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
systemd est le système d'initialisation et de gestion de services utilisé par la majorité des distributions Linux modernes (Ubuntu, Debian, RHEL, Fedora, Arch...).
# Vérifier le PID 1
ps -p 1 -o comm=
# Doit afficher : systemd
# Version
systemctl --version
Voici les commandes systemctl les plus utilisées au quotidien :
Gestion d'un service
# Démarrer un service
sudo systemctl start nginx.service
# Arrêter un service
sudo systemctl stop nginx.service
# Redémarrer (arrêt + démarrage)
sudo systemctl restart nginx.service
# Recharger la configuration (sans arrêt)
sudo systemctl reload nginx.service
# Vérifier l'état
systemctl status nginx.service
Démarrage automatique
# Activer au boot
sudo systemctl enable nginx.service
# Désactiver au boot
sudo systemctl disable nginx.service
# Activer ET démarrer immédiatement
sudo systemctl enable --now nginx.service
Lister et rechercher
# Services chargés en mémoire (pas ceux jamais démarrés)
systemctl list-units --type=service
# Tous les services installés sur le disque
systemctl list-unit-files --type=service
# Services actifs uniquement
systemctl list-units --type=service --state=active
# Services en échec
systemctl list-units --type=service --state=failed
# Rechercher un service
systemctl list-unit-files | grep docker
# Recharger la configuration
sudo systemctl daemon-reload
# Activer au démarrage
sudo systemctl enable mon-app.service
# Démarrer
sudo systemctl start mon-app.service
# Vérifier
systemctl status mon-app.service
Sections expliquées
Section
Rôle
[Unit]
Métadonnées, description, dépendances
[Service]
Comment exécuter le service
[Install]
Quand démarrer (target)
Ces commandes ont des rôles très différents :
Comparaison
Commande
Action
Effet immédiat
Au reboot
start
Démarre le service
Oui
Non
stop
Arrête le service
Oui
Non
restart
Arrête puis redémarre
Oui
Non
reload
Recharge la config
Oui (sans interruption)
Non
enable
Active au boot
Non
Oui
disable
Désactive au boot
Non
Oui
Cas d'usage
# Je veux démarrer un service maintenant
sudo systemctl start nginx
# Je veux qu'il démarre automatiquement au boot
sudo systemctl enable nginx
# Je veux les deux en même temps
sudo systemctl enable --now nginx
# J'ai modifié la config de nginx, je veux recharger sans couper le service
sudo systemctl reload nginx
# Le service est planté, je veux le relancer
sudo systemctl restart nginx
Attention
reload ne fonctionne que si le service le supporte (nginx, Apache...). Sinon, utilisez restart.
Quand un service échoue, voici la méthode de diagnostic :
Étape 1 : Vérifier l'état
systemctl status mon-service.service
Cherchez :
Active: failed (rouge)
Le code de sortie (status=1/FAILURE)
Les dernières lignes de log
Étape 2 : Consulter les logs complets
# Logs du service
journalctl -u mon-service.service
# Logs récents uniquement
journalctl -u mon-service.service -n 50
# Suivre en temps réel
journalctl -u mon-service.service -f
# Uniquement les erreurs
journalctl -u mon-service.service -p err
# Exécuter la commande ExecStart à la main
sudo -u www-data /opt/mon-app/venv/bin/python app.py
Erreurs fréquentes
Symptôme
Cause probable
code=exited, status=203/EXEC
Chemin ExecStart invalide
code=exited, status=217/USER
Utilisateur inexistant
code=exited, status=200/CHDIR
WorkingDirectory inexistant
code=killed, signal=KILL
OOM Killer (mémoire)
journalctl est l'outil de consultation des logs systemd.
Commandes essentielles
# Logs d'un service spécifique
journalctl -u nginx.service
# Les 100 dernières lignes
journalctl -u nginx.service -n 100
# Suivre en temps réel (comme tail -f)
journalctl -u nginx.service -f
# Depuis le dernier boot
journalctl -u nginx.service -b
# Depuis une date
journalctl -u nginx.service --since "2026-01-08 10:00"
# Entre deux dates
journalctl -u nginx.service --since yesterday --until today
Un target systemd est un groupe de services qui définit un état du système (équivalent des runlevels SysVinit).
Targets courants
Target
Équivalent
Description
poweroff.target
runlevel 0
Arrêt du système
rescue.target
runlevel 1
Mode maintenance (mono-utilisateur)
multi-user.target
runlevel 3
Mode multi-utilisateur (sans GUI)
graphical.target
runlevel 5
Mode graphique (avec GUI)
reboot.target
runlevel 6
Redémarrage
Commandes
# Voir le target actuel
systemctl get-default
# Changer le target par défaut (serveur sans GUI)
sudo systemctl set-default multi-user.target
# Changer le target par défaut (desktop avec GUI)
sudo systemctl set-default graphical.target
# Basculer immédiatement vers un target
sudo systemctl isolate rescue.target
Dans un fichier .service
[Install]
WantedBy=multi-user.target
Signifie : "Ce service sera démarré quand le système atteint le mode multi-utilisateur."
Masquer un service (mask) est plus strict que le désactiver (disable).
Différence mask vs disable
Action
Effet
disable
Le service ne démarre pas automatiquement au boot, mais peut être démarré manuellement
mask
Le service est complètement bloqué, impossible de le démarrer même manuellement
Commandes
# Masquer un service
sudo systemctl mask dangerous-service.service
# Vérifier (affiche "masked")
systemctl status dangerous-service.service
# Essayer de démarrer (échouera)
sudo systemctl start dangerous-service.service
# Erreur: Unit dangerous-service.service is masked.
# Démasquer
sudo systemctl unmask dangerous-service.service
Cas d'usage
Empêcher un service vulnérable de démarrer accidentellement
Désactiver des services par défaut non souhaités (cups, avahi...)
Conflits entre services incompatibles
Comment ça marche
mask crée un lien symbolique vers /dev/null, rendant le service inutilisable.
systemd utilise les cgroups pour limiter les ressources des services.
Dans le fichier .service
[Service]
# Limite mémoire (max 512 Mo)
MemoryMax=512M
# Ralentissement (throttling) au-delà de 400 Mo
MemoryHigh=400M
# Limite CPU (50% d'un cœur)
CPUQuota=50%
# Nombre de processus max
TasksMax=100
# Poids I/O (de 1 à 10000, 100 par défaut)
IOWeight=200
Appliquer temporairement (sans modifier le fichier)
# Limiter la mémoire à 256 Mo
sudo systemctl set-property mon-service.service MemoryMax=256M
# Limiter le CPU à 25%
sudo systemctl set-property mon-service.service CPUQuota=25%
Vérifier les limites actuelles
systemctl show mon-service.service | grep -E 'Memory|CPU|Tasks'
Exemple complet (service gourmand)
[Service]
ExecStart=/opt/app/run.sh
# Limites strictes
MemoryMax=1G
MemoryHigh=800M
# 2 cœurs au maximum
CPUQuota=200%
TasksMax=50
# Si le noyau tue un processus (OOM), arrêter toute l'unité
OOMPolicy=kill
Pas de commentaire en fin de ligne dans une unité : MemoryHigh=400M # ... est rejeté puis ignoré. systemd-analyze verify le signale.
systemd offre des options de durcissement pour isoler les services.
Options de sécurité recommandées
[Service]
# Exécuter en tant qu'utilisateur non-root
User=www-data
Group=www-data
# Empêcher l'élévation de privilèges
NoNewPrivileges=true
# Système de fichiers en lecture seule
ProtectSystem=strict
ProtectHome=true
# Répertoire /tmp privé
PrivateTmp=true
# Réglages du noyau, modules et cgroups en lecture seule ou inaccessibles
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
# Filtrer les appels système
SystemCallFilter=@system-service
SystemCallArchitectures=native
# Réseau restreint (si pas besoin)
PrivateNetwork=true
# Capabilities limitées
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
Analyser la sécurité d'un service
# Score de sécurité (0-10, plus bas = mieux)
systemd-analyze security mon-service.service
systemd peut redémarrer automatiquement les services en échec.
Configuration dans le fichier .service
[Unit]
# Au plus 5 démarrages par minute, sinon abandon
StartLimitBurst=5
StartLimitIntervalSec=60
[Service]
ExecStart=/opt/app/run
# Redémarrer en cas d'échec
Restart=on-failure
# Délai entre deux redémarrages
RestartSec=5
Les directives StartLimit* vont dans [Unit] : placé dans [Service], StartLimitIntervalSec= est ignoré avec un avertissement.
Options de Restart
Valeur
Redémarre si...
no
Jamais (défaut)
on-success
Code de sortie = 0
on-failure
Code ≠ 0, signal, timeout
on-abnormal
Signal, timeout, watchdog
on-abort
Signal non intercepté par le programme
always
Toujours (sauf systemctl stop)
Exemple robuste
[Unit]
# Au plus 3 démarrages en 5 minutes
StartLimitBurst=3
StartLimitIntervalSec=300
[Service]
ExecStart=/opt/api/server
Restart=always
RestartSec=10
# Watchdog : le programme doit signaler sa vie toutes les 30 s (sd_notify)
WatchdogSec=30
Vérifier le comportement
# Tuer le processus pour tester le redémarrage
sudo kill -9 $(pgrep -f mon-app)
systemctl status mon-app.service
Les timers systemd remplacent avantageusement cron pour les tâches planifiées.
# /etc/systemd/system/backup.timer
[Unit]
Description=Timer pour backup quotidien
[Timer]
# Tous les jours à 3h du matin
OnCalendar=*-*-* 03:00:00
# Rattraper si le système était éteint
Persistent=true
# Ajouter un délai aléatoire (évite surcharge simultanée)
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
# Tous les jours à minuit
OnCalendar=daily
# Toutes les heures
OnCalendar=hourly
# Tous les lundis à 8h
OnCalendar=Mon *-*-* 08:00:00
# Toutes les 15 minutes
OnCalendar=*:0/15
Lister les timers actifs
systemctl list-timers
Le Type définit comment systemd détecte que le service est "prêt".
Types principaux
Type
Comportement
Exemple
simple
Le processus ExecStart est le service, prêt dès le fork
Défaut historique
exec
Comme simple, mais attend la réussite de l'exec()
Le bon choix pour un binaire au premier plan
forking
Le processus se fork, le parent quitte
Apache, Nginx traditionnel
oneshot
Exécute une fois et termine
Scripts de maintenance
notify
Le service signale quand il est prêt
dockerd
dbus
Prêt quand nom D-Bus acquis
Services desktop
idle
Attend que les jobs soient finis
Login prompts
Exemples
exec (à préférer à simple, qui est le défaut) : un binaire introuvable fait échouer systemctl start, là où simple rend 0 :
systemd gère les dépendances via des directives dans la section [Unit].
Directives principales
Directive
Signification
After=
Démarrer après ce service (ordre)
Before=
Démarrer avant ce service
Requires=
Dépendance forte : un arrêt explicite de l'autre arrête celui-ci ; son échec au démarrage ne bloque celui-ci qu'avec After=
Wants=
Dépendance souple (si l'autre échoue, continue)
BindsTo=
Dépendance très forte : si l'autre s'arrête ou disparaît, celui-ci s'arrête
Conflicts=
Ne peut pas tourner en même temps
Exemples
API qui a besoin de la base de données :
[Unit]
Description=Mon API
After=network.target postgresql.service
Requires=postgresql.service
# PostgreSQL démarre avant l'API ; si son démarrage échoue, l'API ne démarre pas.
# Un crash ultérieur de PostgreSQL n'arrête pas l'API : il faudrait BindsTo=.
Service optionnel (Redis comme cache) :
[Unit]
Description=Mon API avec cache optionnel
After=network.target redis.service
Wants=redis.service
# Si Redis n'est pas là, l'API démarre quand même
Services incompatibles :
[Unit]
Description=Apache
Conflicts=nginx.service
# Apache et Nginx ne peuvent pas tourner ensemble
Visualiser les dépendances
# Dépendances d'un service
systemctl list-dependencies nginx.service
# Dépendances inversées (qui dépend de...)
systemctl list-dependencies --reverse network.target