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é.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Lister les services avec
list-unitsetlist-unit-files, et comprendre pourquoi les deux listes ne se ressemblent pas - Piloter le cycle de vie :
start,stop,restart,reload,status - Lire un statut ligne par ligne et interroger un service avec
is-active,is-enabled,is-failed - Créer une unité de service personnalisée sans tomber dans le piège de
Type=simple - Diagnostiquer un échec avec
journalctlet le code203/EXEC
Les cinq commandes du quotidien
Section intitulée « Les cinq commandes du quotidien »Quatre-vingt-dix pour cent du travail 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.
sudo systemctl start nginx # démarrer maintenantsudo systemctl stop nginx # arrêter maintenantsudo systemctl restart nginx # arrêter puis redémarrersudo systemctl reload nginx # relire la configuration sans coupersystemctl status nginx # 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 :
systemctl start nginx && systemctl is-active nginxactiveLe suffixe .service est facultatif : systemctl start nginx et systemctl start nginx.service sont strictement équivalents. Il devient obligatoire dès qu'un timer ou un socket porte le même nom que le service, ce qui arrive souvent.
Recharger sans couper le trafic
Section intitulée « Recharger sans couper le trafic »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 reload nginx && systemctl is-active nginxactiveLire un statut ligne par ligne
Section intitulée « Lire un statut ligne par ligne »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.
systemctl status nginx● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled) Active: active (running) since Sun 2026-07-12 16:54:11 UTC; 31s ago Invocation: 78240b0f696a4bc29a331a580ba0cdd5 Docs: man:nginx(8) Process: 76 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Process: 80 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Main PID: 81 (nginx) Tasks: 17 (limit: 8616) Memory: 10.9M (peak: 11.7M) CPU: 29ms CGroup: /system.slice/nginx.service ├─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 |
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.
Les réponses en un mot
Section intitulée « Les réponses en un mot »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.
systemctl is-active nginx # active | inactive | failed | activatingsystemctl is-enabled nginx # enabled | disabled | static | maskedsystemctl is-failed demo # failed si l'unité est en échecAprès avoir remis à zéro un service en échec avec reset-failed, is-failed répond par l'état courant :
systemctl reset-failed demo && systemctl is-failed demoinactiveLister les services : list-units ou list-unit-files ?
Section intitulée « Lister les services : list-units ou list-unit-files ? »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.
Les services chargés en mémoire
Section intitulée « Les services chargés en mémoire »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.
systemctl list-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-modules-load.service loaded failed failed Load Kernel Modules 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é.
Les services installés sur le disque
Section intitulée « Les services installés sur le disque »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é.
systemctl list-unit-files --type=serviceUNIT FILE STATE PRESETapt-daily-upgrade.service static -apt-daily.service static -autovt@.service alias -console-getty.service masked disableddebug-shell.service disabled disabledgetty@.service enabled enablednginx.service enabled enabledsystemd-networkd.service disabled enabledsystemd-pstore.service enabled enabledsystemd-remount-fs.service enabled-runtime enabledsystemd-udevd.service masked enabledx11-common.service masked enabled
119 unit files listed.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 (kmod.service pointe vers un autre 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 |
L'état static est celui qui surprend le plus. Un systemctl enable systemd-tmpfiles-clean.service échouera, 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.
systemctl is-enabled systemd-tmpfiles-clean.servicestaticLes listings qui servent vraiment
Section intitulée « Les listings qui servent vraiment »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 :
systemctl list-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 :
systemctl list-unit-files --type=service --state=enabledUNIT FILE STATE PRESETgetty@.service enabled enablednginx.service enabled enabledsystemd-pstore.service enabled enabled
3 unit files listed.Les services en échec, le premier réflexe quand on prend la main sur une machine dont on ne sait rien :
systemctl list-units --state=failed UNIT LOAD ACTIVE SUB DESCRIPTION● demo.service loaded failed failed Demo qui echoue● systemd-modules-load.service loaded failed failed Load Kernel Modules
2 loaded units listed.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.
systemctl list-units --type=service --no-legend | wc -l14Le tableau qui évite l'erreur
Section intitulée « Le tableau qui évite l'erreur »Retenez la question que chaque commande pose vraiment. C'est le seul moyen de ne plus hésiter devant un serveur inconnu.
| Vous voulez savoir | Commande | Attention |
|---|---|---|
| Ce qui tourne en ce moment | systemctl list-units --type=service --state=running | Ne montre pas les services installés mais jamais lancés |
| Tout ce que systemd a chargé | systemctl list-units --type=service | Ajoutez --all pour inclure les unités inactives |
| Ce qui est installé sur la machine | systemctl list-unit-files --type=service | La liste la plus longue, c'est la vue disque |
| Ce qui démarrera au prochain boot | systemctl list-unit-files --type=service --state=enabled | La bonne commande pour un audit |
| Ce qui est tombé | systemctl list-units --state=failed | Le premier réflexe de diagnostic |
| Trouver un service par son nom | systemctl list-unit-files | grep docker | Cherchez dans les unit files, pas dans les units chargées |
Les états d'un service
Section intitulée « Les états d'un service »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 |
enabled n'est pas active
Section intitulée « enabled n'est pas active »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 |
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.
Activer un service au démarrage
Section intitulée « Activer un service au démarrage »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 disable retire le lien.
sudo systemctl enable nginx # au prochain bootsudo systemctl enable --now nginx # au prochain boot ET maintenantsudo systemctl disable nginx # ne démarrera plus au bootsystemctl is-enabled nginx # vérificationLa 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.Executing: /usr/lib/systemd/systemd-sysv-install enable nginxLe disable, lui, montre bien la suppression du lien, ce qui confirme la mécanique sous-jacente :
Removed '/etc/systemd/system/multi-user.target.wants/nginx.service'.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.
Masquer un service pour de bon
Section intitulée « Masquer un service pour de bon »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.
sudo systemctl mask nginxCreated 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'état masked.
Créer une unité de service
Section intitulée « Créer une unité de service »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 signalé en bas du schéma : 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 sudo systemctl edit --full --force mon-app.service -
Décrire le service, en nommant explicitement l'utilisateur non privilégié qui l'exécutera :
[Unit]Description=API de facturation interneAfter=network-online.target postgresql.serviceWants=network-online.target[Service]Type=execUser=appuserGroup=appuserWorkingDirectory=/opt/mon-appExecStart=/opt/mon-app/bin/api --config /etc/mon-app/api.tomlExecReload=/bin/kill -HUP $MAINPIDRestart=on-failureRestartSec=5sNoNewPrivileges=true[Install]WantedBy=multi-user.target -
Recharger systemd pour qu'il relise le fichier, puis démarrer :
Fenêtre de terminal sudo systemctl daemon-reloadsudo systemctl enable --now mon-app.service -
Vérifier réellement que le service tient debout, en laissant passer quelques secondes :
Fenêtre de terminal sleep 3 && systemctl is-active mon-app.serviceLa sortie doit afficher
active. Si elle affichefailed,journalctl -xeu mon-app.servicedonne la raison.
Oublier daemon-reload après avoir modifié un fichier d'unité est l'erreur la plus fréquente : systemd continue d'utiliser la version qu'il a en mémoire, et vos changements semblent sans effet.
Les trois sections
Section intitulée « Les trois sections »Un fichier d'unité se lit toujours de la même façon, ce qui rend n'importe quelle unité inconnue déchiffrable en trente secondes.
| Section | Rôle |
|---|---|
| [Unit] | Description, dépendances (After=, Wants=, Requires=), ordre de démarrage |
| [Service] | Le type, la commande, l'utilisateur, la politique de redémarrage |
| [Install] | Ce qui se passe à l'enable : la cible qui tirera ce service au boot |
Une unité sans section [Install] est justement une unité static : elle ne peut pas être activée, seulement tirée par une dépendance.
Choisir le bon Type
Section intitulée « Choisir le bon Type »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 historique, à é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 |
Le piège : Type=simple ne vérifie pas que votre binaire existe
Section intitulée « 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. Résultat : systemctl start rend la main avec un code de retour 0, is-active répond active, et le service est déjà mort.
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=simpleExecStart=/opt/demo/absentsystemctl start t-simple; echo "code retour de start = $?"; systemctl is-active t-simplecode retour de start = 0activeLe 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 :
systemctl start t-exec; echo "code retour de start = $?"; systemctl is-active t-execJob 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 = 1failedLa 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() :
● demo.service - Demo qui echoue Loaded: loaded (/etc/systemd/system/demo.service; disabled; preset: enabled) Active: active (running) since Sun 2026-07-12 16:54:43 UTC; 11ms ago Main PID: 551 ((app.py))Redémarrer automatiquement en cas de crash
Section intitulée « Redémarrer automatiquement en cas de crash »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 ou un timeout |
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 | Au-delà de 3 tentatives dans la fenêtre, systemd abandonne |
StartLimitIntervalSec | 60s | La fenêtre de comptage des tentatives |
Restart=on-failure couplé à RestartSec=5s est le réglage par défaut raisonnable pour une application métier. 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.
Diagnostiquer un service qui échoue
Section intitulée « Diagnostiquer un service qui échoue »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 systemctl status mon-app.service -
Ouvrir le journal complet de l'unité, avec les explications de systemd :
Fenêtre de terminal journalctl -xeu mon-app.serviceL'option
-xajoute les explications contextuelles,-esaute à la fin,-ufiltre 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/EXECdemo.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 -lsur le chemin de l'ExecStart=tranche en une seconde. -
Corriger, recharger, relancer, dans cet ordre :
Fenêtre de terminal sudo systemctl daemon-reloadsudo systemctl restart mon-app.servicesleep 3 && systemctl is-active mon-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.
Durcir un service exposé
Section intitulée « Durcir un service exposé »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]NoNewPrivileges=true # interdit toute élévation de privilèges (suid)ProtectSystem=strict # tout le système de fichiers en lecture seuleReadWritePaths=/var/lib/mon-appProtectHome=true # /home, /root et /run/user invisiblesPrivateTmp=true # un /tmp isolé, invisible des autres servicesLa 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.
Dépannage
Section intitulée « Dépannage »Les symptômes ci-dessous couvrent la quasi-totalité des appels au support sur un service systemd. La colonne de droite donne l'action, pas une piste.
| Symptôme | Cause probable | Solution |
|---|---|---|
systemctl start réussit mais le service est mort | Type=simple ne vérifie pas l'exec() | Passer en Type=exec, ou vérifier avec sleep 3 && systemctl is-active <service> |
Service failed avec 203/EXEC | Binaire absent, chemin erroné ou non exécutable | ls -l sur le chemin de l'ExecStart=, puis chmod +x si besoin |
Le service ne figure pas dans list-units | Il est installé mais jamais chargé | systemctl list-unit-files --type=service | grep <nom> |
| Modification du fichier d'unité sans effet | systemd utilise sa version en cache | sudo systemctl daemon-reload puis restart |
start répond Unit is masked | Service masqué vers /dev/null | sudo systemctl unmask <service> |
enable répond unit is static | Aucune section [Install] dans l'unité | Normal : le service est tiré par une dépendance, pas par un enable |
| Le service tourne mais disparaît après reboot | Jamais activé au boot | sudo systemctl enable <service> puis vérifier is-enabled |
| Permission denied dans le journal | User= sans droits sur les fichiers | Vérifier le propriétaire des fichiers et les directives de durcissement |
Mettre en pratique
Section intitulée « Mettre en pratique »É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.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »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
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
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »list-unitsliste ce qui est chargé en mémoire,list-unit-filesce qui est installé sur le disque. Un service installé mais jamais démarré n'apparaît pas danslist-unitssans--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. enabledetactivesont indépendants : l'un parle du prochain boot, l'autre de maintenant.Type=simplement. Sur un binaire absent,systemctl startrenvoie 0 etis-activerépondactivealors que le service est déjà mort en203/EXEC. DéclarezType=exec, ou vérifiez l'état quelques secondes après le démarrage.203/EXECest 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é,
daemon-reloadest obligatoire, sinon systemd garde sa version en cache. reloadrelit la configuration sans couper les connexions,restarttue le processus : préférezreloadquand l'unité déclare unExecReload=.maskest plus strict quedisable: il rend le service impossible à démarrer, même par une dépendance.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Activer un service au démarrage : targets,
WantedBy, presets et test de persistance après reboot. - Un service ne démarre pas : codes de sortie,
reset-failedet redémarrage en boucle. - Un service actif qui ne répond pas : quand
systemctlafficheactive (running)et que le service est pourtant muet. - Durcir un service systemd avec le sandboxing : le jeu complet des directives de protection et le score
systemd-analyze security. - Planifier une tâche avec les timers systemd : l'alternative à cron, avec journalisation intégrée.
Questions fréquentes
Section intitulée « Questions fréquentes »Pourquoi systemd ?
| Aspect | SysVinit (ancien) | systemd (moderne) |
|---|---|---|
| Démarrage | Séquentiel (lent) | Parallèle (rapide) |
| Dépendances | Scripts manuels | Automatiques |
| Journalisation | Fichiers dispersés | Centralisée (journalctl) |
| Surveillance | Cron/scripts | Intégrée (watchdog) |
| Isolation | Limitée | cgroups, namespaces |
Composants principaux
- systemctl : gestion des services
- journalctl : consultation des logs
- systemd-analyze : analyse du démarrage
- Unités : services, timers, sockets, targets...
Vérifier si systemd est actif
# Vérifier le PID 1
ps -p 1 -o comm=
# Doit afficher : systemd
# Version
systemctl --version
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
# Lister tous les services
systemctl list-units --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
Après modification d'un fichier .service
sudo systemctl daemon-reload
sudo systemctl restart mon-service.service
.service dans /etc/systemd/system/.Étape 1 : Créer le fichier de service
sudo nano /etc/systemd/system/mon-app.service
Étape 2 : Structure du fichier
[Unit]
Description=Mon Application Python
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/mon-app
ExecStart=/opt/mon-app/venv/bin/python app.py
Restart=on-failure
RestartSec=5
# Sécurité
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Étape 3 : Activer et démarrer
# 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) |
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, Postfix...). Sinon, utilisez restart.É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
Étape 3 : Vérifier la syntaxe du fichier service
sudo systemd-analyze verify /etc/systemd/system/mon-service.service
Étape 4 : Tester manuellement
# 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) |
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
Filtrer par niveau de gravité
# Erreurs uniquement
journalctl -u nginx.service -p err
# Erreurs et warnings
journalctl -u nginx.service -p warning
# Niveaux : emerg, alert, crit, err, warning, notice, info, debug
Format de sortie
# Format JSON (pour parsing)
journalctl -u nginx.service -o json
# Format court (une ligne par entrée)
journalctl -u nginx.service -o short
# Avec horodatage précis
journalctl -u nginx.service -o short-precise
Gestion de l'espace disque
# Taille des logs
journalctl --disk-usage
# Nettoyer (garder 7 jours)
sudo journalctl --vacuum-time=7d
# Nettoyer (garder 500 Mo max)
sudo journalctl --vacuum-size=500M
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."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.Dans le fichier .service
[Service]
# Limite mémoire (max 512 Mo)
MemoryMax=512M
MemoryHigh=400M # Throttling au-delà
# Limite CPU (50% d'un cœur)
CPUQuota=50%
# Nombre de processus max
TasksMax=100
# Priorité I/O (entre 100 et 10000)
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
CPUQuota=200% # 2 cœurs max
TasksMax=50
# Tuer si dépassement mémoire
OOMPolicy=kill
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
# Masquer /proc et /sys
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
Exemple minimal sécurisé
[Unit]
Description=API Sécurisée
After=network.target
[Service]
Type=simple
User=api-user
ExecStart=/opt/api/run
Restart=on-failure
# Durcissement
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/api
[Install]
WantedBy=multi-user.target
Temps total de démarrage
systemd-analyze
# Startup finished in 3.5s (kernel) + 12.4s (userspace) = 15.9s
Services les plus lents
systemd-analyze blame
# 8.123s apt-daily-upgrade.service
# 4.567s docker.service
# 2.345s NetworkManager-wait-online.service
# ...
Graphique de démarrage (SVG)
systemd-analyze plot > boot.svg
firefox boot.svg # Visualiser
Chaîne critique (dépendances)
systemd-analyze critical-chain
# graphical.target @15.9s
# └─multi-user.target @15.9s
# └─docker.service @11.3s +4.5s
# └─network.target @11.2s
Optimisations courantes
| Service lent | Solution |
|---|---|
apt-daily-upgrade.service |
Décaler avec timer |
NetworkManager-wait-online |
Désactiver si IP statique |
docker.service |
Démarrer à la demande (socket) |
snapd.service |
Désactiver si non utilisé |
Configuration dans le fichier .service
[Service]
ExecStart=/opt/app/run
# Redémarrer en cas d'échec
Restart=on-failure
# Délai entre les redémarrages (éviter boucle infinie)
RestartSec=5
# Nombre max de redémarrages par intervalle
StartLimitBurst=5
StartLimitIntervalSec=60
# → Max 5 redémarrages par minute, sinon abandon
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-catchable (SIGKILL) |
always |
Toujours (sauf systemctl stop) |
Exemple robuste
[Service]
ExecStart=/opt/api/server
Restart=always
RestartSec=10
# Attendre 30s avant de considérer le service comme "démarré"
# (évite les redémarrages si crash immédiat)
StartLimitBurst=3
StartLimitIntervalSec=300
# Watchdog : tuer si pas de réponse en 30s
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
Créer un timer
1. Le service (ce qui sera exécuté)# /etc/systemd/system/backup.service
[Unit]
Description=Backup quotidien
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backup
2. Le timer (quand exécuter)# /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
Activer le timer
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
Syntaxe OnCalendar
# 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
Types principaux
| Type | Comportement | Exemple |
|---|---|---|
simple |
Le processus ExecStart est le service | Scripts, apps Node.js |
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 | systemd-notify, Docker |
dbus |
Prêt quand nom D-Bus acquis | Services desktop |
idle |
Attend que les jobs soient finis | Login prompts |
Exemples
simple (défaut, le plus courant) :[Service]
Type=simple
ExecStart=/usr/bin/node /opt/app/server.js
forking (daemons traditionnels) :[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -HUP $MAINPID
oneshot (scripts ponctuels) :[Service]
Type=oneshot
ExecStart=/opt/scripts/cleanup.sh
RemainAfterExit=yes # Garder l'état "active"
notify (signal de disponibilité) :[Service]
Type=notify
ExecStart=/usr/bin/docker daemon
[Unit].Directives principales
| Directive | Signification |
|---|---|
After= |
Démarrer après ce service (ordre) |
Before= |
Démarrer avant ce service |
Requires= |
Dépendance forte (si l'autre plante, celui-ci aussi) |
Wants= |
Dépendance souple (si l'autre plante, continue) |
BindsTo= |
Dépendance très forte (arrêt synchronisé) |
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
# Si PostgreSQL plante, l'API est arrêtée aussi
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