SSD Nodes Learn
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-07-19

Créer un service systemd sur un VPS

Un service systemd garde votre programme actif : démarrage au boot, redémarrage au crash, journalisation. Comment en écrire un, ajouter un timer et le durcir.

Ce qu'est un service systemd, et pourquoi vous en voulez un

Un service systemd est un petit fichier texte qui indique à votre serveur comment exécuter un programme : le démarrer au boot, le redémarrer s'il plante, et envoyer sa sortie au journal système. C'est tout son rôle. Un programme que vous lancez à la main dans une session SSH meurt dès que vous vous déconnectez ou que le serveur redémarre. Un programme encapsulé dans un service systemd continue de tourner, parce que c'est le serveur lui-même qui le possède, et non votre shell.

systemd est le système d'init d'Ubuntu, Debian, Fedora et de la plupart des serveurs Linux modernes. C'est le premier processus à démarrer et celui qui supervise tout le reste. Quand vous écrivez un fichier de service, vous confiez votre programme à ce superviseur. Ce guide montre la plus petite unité qui fonctionne, les trois sections que possède chaque unité, comment l'activer et lire ses logs, comment l'exécuter selon un planning avec un timer, et comment la verrouiller pour qu'elle tourne avec le moins de privilèges possible.

Le plus petit service qui fonctionne

Un fichier de service réside dans /etc/systemd/system/, se termine par .service et ne nécessite que quelques lignes. Créez-en un pour un programme situé à /usr/local/bin/myapp :

sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My application

[Service]
ExecStart=/usr/local/bin/myapp

[Install]
WantedBy=multi-user.target

C'est une unité complète et fonctionnelle. ExecStart est la commande à exécuter. WantedBy=multi-user.target signifie démarrer ceci une fois que le serveur atteint le fonctionnement multi-utilisateur normal, ce qui est ce qui le fait remonter au boot. Tout le reste n'est que raffinement.

Les trois sections, et à quoi sert chacune

Chaque fichier d'unité est divisé en sections entre crochets. Un service en utilise trois.

[Unit] décrit le service et ses relations. Les deux lignes que vous utiliserez le plus :

[Unit]
Description=My application
After=network-online.target
Wants=network-online.target

Description est l'étiquette lisible que vous voyez dans systemctl status. After=network-online.target indique à systemd de ne pas démarrer votre programme tant que le réseau n'est pas en place, ce qui compte pour tout ce qui ouvre un port ou établit une connexion sortante.

[Service] définit comment le programme s'exécute. C'est là que va la plupart de vos réglages :

[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.toml
WorkingDirectory=/opt/myapp
User=myapp
Restart=on-failure
RestartSec=5
Environment=LOG_LEVEL=info

User=myapp exécute le programme sous un compte non privilégié au lieu de root, ce qui est la ligne la plus importante pour la sécurité. Restart=on-failure et RestartSec=5 ont leur propre section plus bas, parce qu'ils sont la raison pour laquelle la plupart des gens écrivent un service tout court.

[Install] définit ce qui se passe quand vous activez le service :

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target est ce qui rattache le service au boot quand vous lancez systemctl enable. Sans section [Install], un service peut être démarré à la main mais ne remontera pas de lui-même après un redémarrage.

Activez-le et surveillez-le

Après avoir écrit ou modifié un fichier d'unité, rechargez systemd pour qu'il lise le changement, puis activez et démarrez le service en une seule étape :

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

daemon-reload est l'étape que les gens oublient : systemd met en cache les fichiers d'unité, donc une modification ne fait rien tant que vous ne rechargez pas. enable --now à la fois active le service au boot et le démarre immédiatement. Vérifiez :

sudo systemctl status myapp.service
* myapp.service - My application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
     Active: active (running) since Wed 2026-07-15 22:40:11 UTC; 3s ago
   Main PID: 4123 (myapp)

Active: active (running) et enabled sont ce que vous voulez. Pour lire la sortie du programme, demandez au journal uniquement cette unité :

sudo journalctl -u myapp.service -f

Le -f suit les nouvelles lignes à mesure qu'elles arrivent, comme tail -f. Tout ce que votre programme écrit sur la sortie standard ou l'erreur standard atterrit ici, sans aucune configuration de journalisation de votre part.

Redémarrage après échec, la raison de votre présence ici

Le principal intérêt d'un service, c'est que systemd redémarre votre programme quand il meurt. Deux lignes suffisent :

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure redémarre le programme quand il se termine avec un code non nul ou meurt à cause d'un signal de crash comme SIGKILL ou SIGSEGV. Une sortie propre, ou un arrêt par SIGTERM, SIGINT, SIGHUP ou SIGPIPE, ne le déclenche pas. RestartSec=5 attend cinq secondes entre les tentatives, pour qu'un programme qui plante instantanément ne tourne pas en boucle serrée. Confirmez-le en tuant le processus et en regardant systemd le relancer. Utilisez SIGKILL : le SIGTERM par défaut compte comme un arrêt propre, donc on-failure ne redémarrerait pas le service :

sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.service

En moins de cinq secondes, le statut affiche un nouveau Main PID et à nouveau active (running). C'est toute la fonctionnalité, et c'est pourquoi un service vaut mieux que laisser un programme tourner dans tmux ou screen.

Exécutez-le sous un utilisateur non privilégié, et durcissez-le

Un service qui tourne en root peut tout faire à votre serveur si le programme est un jour exploité. Exécutez-le sous son propre utilisateur, et donnez à systemd quelques directives qui le clôturent. Créez d'abord un compte système sans connexion et sans répertoire personnel :

sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp

Ensuite, définissez User=myapp et ajoutez des lignes de durcissement à [Service] :

[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

Chaque ligne retire quelque chose dont le programme n'a pas besoin. NoNewPrivileges=true empêche le processus d'obtenir de nouveaux privilèges, même via un binaire setuid. PrivateTmp=true lui donne un /tmp privé qu'aucun autre processus ne peut voir. ProtectSystem=strict rend tout le système de fichiers en lecture seule, sauf quelques chemins que vous nommez avec ReadWritePaths=. ProtectHome=true lui cache /home entièrement. C'est la même logique de moindre privilège que placer un service derrière un pare-feu : ne lui donnez que ce dont il a besoin. Si vous avez lu le guide sur combler la faille du pare-feu IPv6 sur un VPS, ceci est la moitié côté hôte de la même idée. Pour un service exposé à Internet, combinez ce durcissement avec Fail2ban devant SSH et un pare-feu par défaut interdit.

Plutôt que de taper tout cela à la main et de mal vous souvenir d'une directive, générez une unité complète et durcie, puis copiez-la :

ToolGénérateur de service et de timer systemd

Les timers : le cron moderne

Un timer systemd exécute un service selon un planning, et c'est le remplaçant moderne d'une tâche cron. Un timer, ce sont deux fichiers : un .service qui fait le travail, et un .timer qui dit quand. Supposons que vous vouliez une sauvegarde à 3 h chaque jour. Le service fait la tâche une fois puis se termine :

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

Type=oneshot indique à systemd que le programme s'exécute, se termine et en a fini, plutôt que de rester résident. Le timer le planifie :

# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

OnCalendar=*-*-* 03:00:00 signifie 3 h chaque jour. Testez n'importe quelle expression de calendrier avec systemd-analyze calendar "*-*-* 03:00:00", qui confirme qu'elle est analysée et affiche les prochaines fois où elle se déclenchera. Persistent=true exécute une tâche manquée dès que le serveur revient s'il était éteint à 3 h, ce que cron ne sait pas faire. Remarquez qu'un timer s'active via timers.target, et non multi-user.target. Activez le timer, pas le service :

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers

list-timers affiche chaque timer avec sa prochaine et sa dernière exécution, pour que vous voyiez d'un coup d'œil quand votre tâche se déclenche ensuite. Le générateur ci-dessus construit la paire .service et .timer pour vous quand vous activez le mode timer. Face à une ligne cron, un timer vous donne de vrais logs dans le journal, les mêmes directives de durcissement que n'importe quel service, et le rattrapage des exécutions manquées que fournit Persistent=true. Cron reste très bien pour une tâche simple ; un timer est le meilleur outil dès que la tâche compte.

FAQ

Quelle est la différence entre un service systemd et une tâche cron ?

Un service maintient en vie un programme de longue durée : il démarre au boot, redémarre après un échec et écrit dans le journal. Une tâche cron exécute une commande courte selon un planning puis se termine. Quand vous voulez la planification mais aussi des logs dans le journal, du durcissement et le rattrapage des exécutions manquées, utilisez un timer systemd, qui associe un planning .timer à un service oneshot et remplace cron pour la plupart des tâches serveur.

Où placer mon fichier de service systemd ?

Placez vos propres unités dans /etc/systemd/system/, avec un nom se terminant par .service. Ce répertoire est destiné aux unités que l'administrateur ajoute, et il a priorité sur les unités fournies par les paquets dans /lib/systemd/system/. Après avoir créé ou modifié un fichier là, lancez sudo systemctl daemon-reload pour que systemd prenne en compte le changement.

Comment faire redémarrer un service s'il plante ?

Ajoutez Restart=on-failure et RestartSec=5 à la section [Service], puis lancez sudo systemctl daemon-reload et redémarrez le service. systemd relance le programme quand il se termine avec un code non nul ou meurt à cause d'un signal de crash, en attendant cinq secondes entre les tentatives. Testez avec sudo systemctl kill -s SIGKILL myapp.service (SIGTERM, le signal par défaut, compte comme un arrêt propre et ne déclenche pas on-failure) et regardez systemctl status afficher un nouveau PID en quelques secondes.

Comment exécuter un service systemd sous un utilisateur non root ?

Créez un compte système avec sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp, puis ajoutez User=myapp à la section [Service]. Ajoutez NoNewPrivileges=true, PrivateTmp=true et ProtectSystem=strict pour que le processus tourne avec le moins d'accès nécessaire. Tourner sous un utilisateur non privilégié est le changement le plus important que vous puissiez apporter à la sécurité d'un service.

Pourquoi mon service n'a-t-il pas démarré ?

Lancez systemctl status myapp.service pour le résumé et journalctl -u myapp.service pour la sortie complète. Les causes les plus fréquentes sont un mauvais chemin dans ExecStart, un WorkingDirectory manquant, une erreur de permission parce que User= ne peut pas lire un fichier, ou un sudo systemctl daemon-reload oublié après une modification. Le journal affiche le message d'erreur propre au programme, qui nomme généralement le problème directement.