SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-22

Créer un service systemd pour un programme sur VPS

Apprenez à créer un service systemd pour lancer votre programme au démarrage, le relancer après un crash et consulter ses logs avec journalctl, avec un timer et du hardening.

Ce qu’est un service systemd et pourquoi vous en avez besoin

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 se termine de manière inattendue et envoyer sa sortie dans le journal système. C’est tout son rôle. Un programme que vous démarrez manuellement dans une session SSH s’arrête dès que vous vous déconnectez ou que le serveur redémarre. Un programme associé à un service systemd continue de fonctionner, car c’est le serveur lui-même qui le gère, et non votre shell.

systemd est le système d’initialisation d’Ubuntu, Debian, Fedora et de la plupart des serveurs Linux modernes. C’est le premier processus à démarrer et celui qui supervise tous les autres. Cela n’a pas toujours été le cas, et le remplacement des scripts init par systemd mérite d’être lu une fois que vous savez ce que fait un unit file. Lorsque vous écrivez un service file, vous confiez votre programme à ce superviseur. Ce guide présente l’unité fonctionnelle la plus simple, les trois sections que contient chaque unité, la façon de l’activer et de consulter ses journaux, la façon de l’exécuter selon un planning avec un timer et la façon de la verrouiller pour qu’elle s’exécute avec le moins de privilèges possible.

Le service minimal fonctionnel

Un fichier de service se trouve dans /etc/systemd/system/, porte l’extension .service et ne nécessite que quelques lignes. Créez-en un pour un programme situé dans /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

Il s’agit d’une unité complète et fonctionnelle. ExecStart correspond à la commande à exécuter. WantedBy=multi-user.target signifie de démarrer le service lorsque le serveur atteint le fonctionnement multi-utilisateur normal, ce qui permet son lancement au boot. Tout le reste relève du perfectionnement.

Les trois sections et leur rôle

Chaque fichier d’unité est divisé en sections entre crochets. Une unité de service en utilise trois.

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

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

Description est le libellé 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 disponible. C’est important pour tout programme qui écoute sur un port ou établit une connexion sortante.

[Service] définit le mode d’exécution du programme. La plupart de vos paramètres se trouvent ici :

[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 avec un compte non privilégié plutôt qu’avec root. C’est la ligne la plus importante pour la sécurité. Il n’y a pas de ligne Type= ici. systemd utilise donc la valeur simple et suppose que le processus ExecStart reste au premier plan. Un programme qui se met en arrière-plan en créant un processus fils nécessite le Type= adapté à son mode de démarrage, sinon l’unité sera indiquée comme active alors que le daemon réel aura déjà quitté. Restart=on-failure et RestartSec=5 ont leur propre section ci-dessous, car ce sont les raisons principales pour lesquelles on crée un service.

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

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target rattache le service au démarrage du système lorsque vous exécutez systemctl enable. Sans section [Install], un service peut être démarré manuellement, mais il ne démarrera pas automatiquement 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 prenne en compte la modification, 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 l’on oublie souvent : systemd met les fichiers d’unité en cache, donc une modification reste sans effet tant que vous n’avez pas rechargé sa configuration. enable --now active le service au démarrage et le démarre immédiatement. Vérifiez son état :

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 correspondent au résultat attendu. Pour consulter la sortie du programme, demandez au journal d’afficher uniquement cette unité :

sudo journalctl -u myapp.service -f

-f suit les nouvelles lignes à mesure qu’elles arrivent, comme tail -f. Tout ce que votre programme écrit sur la sortie standard ou la sortie d’erreur arrive ici, sans configuration de journalisation supplémentaire de votre part.

Redémarrer en cas d’échec : la raison pour laquelle vous êtes ici

Le principal avantage d’un service est que systemd redémarre votre programme lorsqu’il s’arrête brutalement. Deux lignes suffisent :

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure redémarre le programme lorsqu’il se termine avec un code différent de zéro ou lorsqu’il s’arrête à cause d’un signal de crash comme SIGKILL ou SIGSEGV. Une sortie normale, ou un arrêt provoqué par SIGTERM, SIGINT, SIGHUP ou SIGPIPE, ne déclenche pas le redémarrage. RestartSec=5 attend cinq secondes entre les tentatives. Un programme qui plante immédiatement n’effectue donc pas une boucle serrée. Vérifiez ce comportement en tuant le processus et en observant systemd le relancer. Utilisez SIGKILL : SIGTERM, le signal par défaut, est considéré comme un arrêt normal. on-failure ne redémarrerait donc pas le service :

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

Dans les cinq secondes, l’état affiche un nouveau Main PID, puis à nouveau active (running). C’est toute la fonctionnalité. C’est aussi la raison pour laquelle un service est préférable à un programme laissé dans tmux ou screen.

L’exécuter avec un utilisateur non privilégié et le renforcer

Un service exécuté avec root peut faire n’importe quoi sur votre serveur si le programme est exploité. Exécutez-le avec son propre utilisateur et ajoutez quelques directives systemd pour limiter son environnement. Commencez par créer un compte système sans accès à la connexion et sans répertoire personnel :

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

Définissez ensuite User=myapp et ajoutez les lignes de renforcement dans [Service] :

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

Chaque ligne retire une fonctionnalité dont le programme n’a pas besoin. NoNewPrivileges=true empêche définitivement le processus d’obtenir de nouveaux privilèges, même par l’intermédiaire d’un binaire setuid. PrivateTmp=true lui attribue un /tmp privé, invisible pour les autres processus. ProtectSystem=strict rend l’ensemble du système de fichiers accessible en lecture seule, à l’exception des chemins indiqués avec ReadWritePaths=. ProtectHome=true lui masque entièrement /home. C’est le même principe du moindre privilège que pour un service placé derrière un pare-feu : donnez-lui uniquement ce dont il a besoin. Si vous avez consulté le guide sur la fermeture de la faille du pare-feu IPv6 sur un VPS, cette configuration constitue la partie appliquée à l’hôte de la même approche. Pour un service exposé à Internet, associez ce renforcement à Fail2ban devant SSH et à un pare-feu en refus par défaut.

Au lieu de saisir tout cela manuellement et de risquer d’oublier une directive, générez une unité complète et renforcée, puis copiez-la :

Toolsystemd service and timer generator

Timers : le cron moderne

Un timer systemd exécute un service selon un calendrier. Il remplace aujourd’hui les tâches cron. Un timer se compose de deux fichiers : un .service qui effectue le travail et un .timer qui indique quand l’exécuter. Supposons que vous vouliez effectuer une sauvegarde à 3am tous les jours. Le service exécute 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, puis s’arrête, au lieu 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 tous les jours à 3am. Testez toute expression de calendrier avec systemd-analyze calendar "*-*-* 03:00:00". Cette commande vérifie qu’elle est valide et affiche les prochaines heures d’exécution. Persistent=true exécute une tâche manquée dès que le serveur revient en ligne s’il était arrêté à 3am, ce que cron ne sait pas faire. Notez qu’un timer s’active avec timers.target, et non avec 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 tous les timers avec leur prochaine et leur dernière exécution. Vous pouvez ainsi voir immédiatement quand votre tâche sera exécutée. Le générateur ci-dessus crée pour vous les fichiers associés .service et .timer lorsque vous activez le mode timer. Par rapport à une ligne cron, un timer fournit de vrais journaux dans le journal systemd, les mêmes directives de durcissement que n’importe quel service et le rattrapage des exécutions manquées fourni par Persistent=true. Cron reste adapté aux tâches simples. Un timer est préférable dès que la tâche devient importante.

FAQ

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

Un service maintient un programme longuement exécuté : il démarre au boot, redémarre en cas d’échec et écrit ses journaux dans le journal systemd. Une tâche cron exécute une commande courte selon une planification, puis se termine. Si vous voulez une planification avec les journaux du journal systemd, le hardening et le rattrapage des exécutions manquées, utilisez un timer systemd. Il associe une planification .timer à un service oneshot et remplace cron pour la plupart des tâches serveur.

Où dois-je placer mon fichier de service systemd ?

Placez vos propres unités dans /etc/systemd/system/, avec un nom qui se termine par .service. Ce répertoire est réservé aux unités ajoutées par l’administrateur et il est prioritaire sur les unités fournies par les paquets dans /lib/systemd/system/. Après avoir créé ou modifié un fichier à cet emplacement, exécutez sudo systemctl daemon-reload pour que systemd prenne la modification en compte.

Comment redémarrer un service s’il plante ?

Ajoutez Restart=on-failure et RestartSec=5 à la section [Service], puis exécutez sudo systemctl daemon-reload et redémarrez le service. systemd relance le programme lorsqu’il se termine avec un code différent de zéro ou qu’il meurt à la suite d’un signal de crash, en attendant cinq secondes entre les tentatives. Testez-le avec sudo systemctl kill -s SIGKILL myapp.service. SIGTERM, le signal par défaut, correspond à un arrêt normal et ne déclenche pas on-failure. Surveillez ensuite systemctl status : il doit afficher un nouveau PID après quelques secondes.

Comment exécuter un service systemd avec un utilisateur autre que 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 afin que le processus s’exécute avec les accès strictement nécessaires. L’exécution avec un utilisateur non privilégié est la mesure individuelle la plus importante que vous puissiez prendre pour améliorer la sécurité d’un service.

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

Exécutez systemctl status myapp.service pour obtenir le résumé et journalctl -u myapp.service pour afficher la sortie complète. Les causes les plus fréquentes sont un chemin incorrect dans ExecStart, un WorkingDirectory manquant, une erreur de permissions parce que User= ne peut pas lire un fichier, ou l’oubli de sudo systemctl daemon-reload après une modification. Le journal systemd affiche le message d’erreur du programme, qui indique généralement directement le problème.