Chapitre 3.5 — systemd & services
⏱️ TL;DR — systemd est le chef d’orchestre de Linux moderne : le premier processus lancé au démarrage (PID 1), celui qui démarre, surveille et redémarre tous les services. Un service, c’est un programme qui tourne en permanence (Nginx, Postgres, ton app Next.js) — et qu’on veut voir redémarrer tout seul au boot et après un crash. On le pilote avec
systemctl:start,stop,restart,reload,status, et surtoutenable(démarrage automatique au boot — le piège classique est de l’oublier). Les logs se lisent avecjournalctl -u <service>(et-fpour suivre en direct). Pour transformer ton app en service, tu écris un petit fichier texte, un unit (/etc/systemd/system/monapp.service), avec où et sous quel utilisateur la lancer, etRestart=alwayspour qu’elle ressuscite après un crash. C’est la brique qui fera tourner FormaCampus en Partie 9.
🎯 Objectifs
- Comprendre le rôle de systemd et ce qu’est un service (une unit).
- Piloter un service avec
systemctl:start,stop,restart,reload,status. - Ne jamais oublier
enable(démarrage au boot) — la différence vitale avecstart. - Lire les logs d’un service avec
journalctl(-u,-f,--since,-e). - Écrire un unit file complet pour faire tourner ta propre app en service.
systemd : le chef d’orchestre
Au démarrage du serveur, le noyau Linux lance un seul processus : le PID 1. Sur les distributions modernes (Ubuntu, Debian, la plupart), ce PID 1 est systemd. C’est lui l’ancêtre de tous les processus (revoir l’arbre du Chapitre 3.4) et le gestionnaire de services du système : il décide quoi lancer au démarrage, dans quel ordre, surveille que tout tourne, et relance ce qui tombe.
Chaque chose que systemd gère est une unit. La plus courante est le service (fichiers .service) : un programme de longue durée. Nginx, Postgres, SSH, ton app : tous sont des services gérés par systemd. C’est systemd qui les fait survivre à ta déconnexion et redémarrer au reboot — exactement ce qu’un & ou un nohup ne savent pas faire proprement.
💡 Réflexe — Dès qu’un programme doit tourner en continu en production, la question n’est pas « comment le lancer ? » mais « comment en faire un service ? ». Un service lancé à la main dans un terminal est une bombe à retardement : il meurt à la première déconnexion, au premier crash, au premier reboot. La réponse de prod, c’est toujours systemd.
systemctl : piloter les services
systemctl est la commande unique pour tout piloter. Les verbes essentiels :
sudo systemctl status nginx # ÉTAT du service : actif ? depuis quand ? PID ? 3 dernières lignes de log
sudo systemctl start nginx # le DÉMARRER maintenant
sudo systemctl stop nginx # l'ARRÊTER maintenant
sudo systemctl restart nginx # l'arrêter puis le redémarrer (recharge tout)
sudo systemctl reload nginx # RECHARGER sa config SANS l'arrêter (pas de coupure de service)status est ton tableau de bord : il te dit si le service est active (running) ou failed, depuis combien de temps, son PID, et affiche les dernières lignes de log — souvent assez pour comprendre un problème du premier coup.
💡 Réflexe —
reloadplutôt querestartquand c’est possible.reloaddemande au service de relire sa config sans s’arrêter : aucune coupure.restartle tue et le relance : bref instant d’indisponibilité + toutes les connexions en cours sont perdues. Pour Nginx après un changement de config,reloadest la norme ;restartse réserve aux cas oùreloadne suffit pas. (Note : tous les services ne savent pas sereload—statuset la doc du service te le disent.)
enable : le piège qui casse au reboot
Voici la distinction que tout le monde confond au début, et qui provoque des pannes mystérieuses :
startdémarre le service maintenant, pour cette session. Mais au prochain reboot, il ne repartira PAS.enablel’inscrit pour démarrer automatiquement à chaque boot. Mais ne le démarre pas tout de suite.
sudo systemctl enable nginx # démarrage AUTOMATIQUE au boot (mais pas maintenant)
sudo systemctl start nginx # démarrage MAINTENANT (mais pas au prochain reboot)
sudo systemctl enable --now nginx # LES DEUX d'un coup : active au boot ET démarre tout de suite
sudo systemctl disable nginx # retire du démarrage automatique
sudo systemctl is-enabled nginx # vérifier : "enabled" ou "disabled" ?⚠️ Piège — Oublier
enable. Scénario classique et cruel : tu installes ton app, tu lastart, tout marche, tu es content. Des semaines plus tard, une mise à jour ou une coupure de courant redémarre le serveur… et ton app ne revient pas, parce qu’elle n’était questart, jamaisenable. Le site est down « sans raison ». Le réflexe qui évite ça :systemctl enable --nowpour tout service de prod, puis teste avec unsudo rebootque tout remonte bien. Unsystemctl is-enabledsur tes services critiques fait partie de la checklist de mise en prod.
journalctl : lire les logs
systemd collecte automatiquement la sortie de chaque service (ce que le programme écrit) dans un journal centralisé, le journald. On le consulte avec journalctl. L’option reine est -u (unit), pour cibler un service :
journalctl -u nginx # tous les logs du service nginx (paginés)
journalctl -u nginx -e # saute directement à la FIN (-e = end, les plus récents)
journalctl -u nginx -f # SUIT le log en DIRECT (-f = follow), comme tail -f
journalctl -u nginx --since "10 min ago" # depuis 10 minutes
journalctl -u nginx --since today # depuis ce matin
journalctl -u nginx --since "2026-07-15 09:00" # depuis une date/heure préciseLe couple status + journalctl est ta méthode de diagnostic universelle pour un service qui déconne : systemctl status <service> pour l’état et un aperçu, journalctl -u <service> -e pour le détail des erreurs, -f pour observer en temps réel pendant que tu reproduis le problème.
🐚 Au terminal — La séquence « mon service ne démarre pas » :
sudo systemctl status monapp # est-il failed ? qu'affiche l'aperçu de log ?
journalctl -u monapp -e # le détail de l'erreur (dernières entrées)
journalctl -u monapp -f # suivre en direct pendant qu'on tente un restart
sudo systemctl restart monapp # relancer, en observant le journal en parallèle📚 La doc —
man systemctl,man journalctl, etman systemd.service(le format d’un unit file, section par section). systemd est vaste ; on n’en couvre ici que le cœur indispensable au déploiement. Pour les timers (l’équivalent moderne de cron, croisés avec le renouvellement Let’s Encrypt en Partie 8) et les options fines, la doc systemd fait autorité.
Écrire un unit : transformer ton app en service
Voici le cœur pratique du chapitre : rendre ta propre application gérée par systemd. Un service est décrit par un fichier texte, l’unit file, rangé dans /etc/systemd/system/ et nommé <nom>.service. Il se compose de trois sections : [Unit] (métadonnées et dépendances), [Service] (comment lancer le programme), [Install] (comment l’activer au boot).
Exemple complet et commenté pour une app Next.js en production :
[Unit]
Description=FormaCampus - front Next.js
After=network.target
# After : ne démarre ce service qu'une fois le réseau prêt.
[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/var/www/formacampus
EnvironmentFile=/var/www/formacampus/.env.production
ExecStart=/usr/bin/node /var/www/formacampus/server.js
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.targetDécortiquons les directives qui comptent, dans [Service] :
ExecStart: la commande qui lance ton app. Utilise des chemins absolus (/usr/bin/node, pasnode) — systemd n’a pas le mêmePATHque ton shell.UseretGroup: l’utilisateur sous lequel tourne le service. Jamais root pour une app web : icideploy. C’est le moindre privilège des Chapitres 3.1 et 3.2 appliqué aux services.WorkingDirectory: le dossier depuis lequel l’app s’exécute (comme si tu avais faitcddedans avant).EnvironmentFile: le fichier.envd’où charger les variables d’environnement (secrets,NODE_ENV=production, port…). On ne met pas les secrets en clair dans l’unit ; on pointe vers un fichier aux droits restreints (rappel : 600/640, Chapitre 3.3).Restart=always: la directive magique. Si l’app crashe, systemd la relance automatiquement. AvecRestartSec=3, il attend 3 secondes entre deux tentatives (pour ne pas boucler frénétiquement).
Et dans [Install] :
WantedBy=multi-user.target: dit à quel moment du démarrage rattacher le service quand on l’enable.multi-user.target= « le système est prêt, en mode serveur multi-utilisateurs » — la cible standard pour un service de fond.
⚠️ Piège — Modifier un unit sans faire
daemon-reload. systemd met en cache la définition des services. Si tu édites/etc/systemd/system/monapp.servicepuis fais directementsystemctl restart monapp, systemd relance l’ancienne version — tes changements sont ignorés, et tu tournes en rond à te demander pourquoi. Après toute édition d’un unit, il faut recharger la config de systemd :
sudo systemctl daemon-reload # systemd relit les fichiers d'unit (OBLIGATOIRE après édition)
sudo systemctl restart monapp # puis on appliqueLa séquence complète, de A à Z
Mettre un nouveau service en production, dans l’ordre :
# 1. Créer/éditer l'unit
sudo nano /etc/systemd/system/formacampus-front.service # (coller l'unit ci-dessus)
# 2. Recharger la config de systemd (car on a touché à un unit)
sudo systemctl daemon-reload
# 3. Activer au boot ET démarrer tout de suite
sudo systemctl enable --now formacampus-front
# 4. Vérifier que tout va bien
sudo systemctl status formacampus-front # doit être "active (running)"
journalctl -u formacampus-front -e # jeter un œil aux logs de démarrage🧭 Sur FormaCampus — C’est ici que tout converge. L’équipe fait tourner deux services systemd :
formacampus-front.service(le front Next.js) etformacampus-api.service(l’API Symfony), tous deux enUser=deploy, avecRestart=alwayset unEnvironmentFilepointant vers un.enven 640. Chacun estenable --now, puis l’équipe reboote volontairement le VPS pour vérifier que les deux remontent seuls. Nginx (Partie 7) relaiera ensuite vers ces deux services locaux. Le jour où l’API crashe à 3 h du matin,Restart=alwaysla relance en 3 secondes, etjournalctl -u formacampus-apigarde la trace de l’incident pour le lendemain. Fini les apps lancées à la main qui meurent au premier hoquet : c’est la définition même de la prod. On construit tout ça en détail en Partie 9.
🔒 Sécurité — Un unit bien écrit est aussi une surface d’attaque réduite :
User=deploy(jamais root), un.envaux droits restreints référencé parEnvironmentFileplutôt que des secrets en clair, et — pour aller plus loin — les options de durcissement de systemd (NoNewPrivileges=,ProtectSystem=,PrivateTmp=…) qui cloisonnent encore le service. On les cite ; leur détail relève du durcissement (Partie 5) et de la docman systemd.exec. Le réflexe minimal, dès maintenant : jamaisUser=rootpour une app.
✏️ Exercices
Exercice 1 — Le service qui ne revient pas. Un collègue a installé une app, l’a lancée avec systemctl start monapp, et tout marchait. Après un reboot du serveur, le site est down. Quel est le problème, comment le confirmer, et comment le corriger définitivement ?
✅ Solution
Le problème : le service a été start mais jamais enable. start ne démarre que pour la session courante ; au reboot, systemd ne le relance pas car il n’est pas inscrit au démarrage automatique.
Confirmer :
sudo systemctl is-enabled monapp # affichera "disabled"Corriger définitivement :
sudo systemctl enable --now monapp # active au boot ET démarre maintenant
sudo systemctl is-enabled monapp # doit maintenant afficher "enabled"Bonus : refaire un sudo reboot pour vérifier que le service remonte bien tout seul.
Exercice 2 — Écris un unit. Écris un unit systemd minimal mais complet pour une API Node qui se lance avec /usr/bin/node /var/www/api/server.js, tourne sous l’utilisateur deploy, charge son environnement depuis /var/www/api/.env, et redémarre automatiquement en cas de crash.
✅ Solution
[Unit]
Description=API Node de production
After=network.target
[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/var/www/api
EnvironmentFile=/var/www/api/.env
ExecStart=/usr/bin/node /var/www/api/server.js
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.targetÀ déposer dans /etc/systemd/system/api.service, puis : sudo systemctl daemon-reload (car on a créé un unit), et enfin sudo systemctl enable --now api. Les points clés : User=deploy (pas root), Restart=always (résilience au crash), EnvironmentFile (secrets hors de l’unit), WantedBy=multi-user.target (activable au boot).
🧠 Quiz de révision
1. Quelle est la différence entre systemctl start et systemctl enable ?
start démarre le service maintenant, mais il ne repartira pas au prochain reboot. enable l’inscrit pour démarrer automatiquement au boot, mais ne le lance pas tout de suite. Pour les deux d’un coup : systemctl enable --now <service>. Oublier enable est le piège classique qui fait qu’un service ne revient pas après un redémarrage.
2. Comment lit-on les logs d’un service précis, en direct ?
Avec journalctl -u <service> -f : -u cible l’unit (le service voulu), -f suit le journal en temps réel (comme tail -f). Pour aller directement aux dernières entrées sans suivre : -e. Pour filtrer par date : --since.
3. À quoi sert Restart=always dans un unit, et User= ?
Restart=always demande à systemd de relancer automatiquement le service s’il crashe — c’est ce qui rend une app résiliente en prod. User= fixe l’utilisateur sous lequel le service tourne : on met un compte non privilégié (ex. deploy), jamais root, pour limiter les dégâts en cas de compromission (moindre privilège).
4. Pourquoi faut-il faire daemon-reload après avoir édité un unit ?
Parce que systemd met en cache la définition des services. Sans systemctl daemon-reload, un restart relancerait l’ancienne version de l’unit et ignorerait tes modifications. La règle : après toute édition d’un fichier d’unit, sudo systemctl daemon-reload avant de (re)démarrer.
5. Que fait WantedBy=multi-user.target dans la section [Install] ?
Il indique à quel moment du démarrage rattacher le service quand on l’enable. multi-user.target correspond au système prêt en mode serveur (multi-utilisateurs, sans interface graphique) : c’est la cible standard pour un service de fond. C’est cette ligne qui rend le service activable au boot.
Fin de la Partie 3. Tu sais désormais qui habite la machine, qui a le droit de quoi, et comment faire tourner tes apps comme de vrais services. Direction l’accès distant sécurisé : Partie 4 — SSH : accès & durcissement.