SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-28

Programma draaien als systemd service op Linux VPS

Leer hoe u een eigen systemd service schrijft voor uw programma. Zorg dat uw applicatie automatisch start, herstelt na een crash en logt naar journalctl op uw VPS.

Wat een systemd-service is en waarom u deze nodig heeft

Een systemd-service is een klein tekstbestand dat uw server instrueert hoe een programma moet worden uitgevoerd: start het bij het opstarten, herstart het bij een crash en stuur de uitvoer naar het systeemlogboek. Dat is de volledige taak. Een programma dat u handmatig start in een SSH-sessie stopt zodra u uitlogt of de server opnieuw opstart. Een programma dat is verpakt in een systemd-service blijft draaien, omdat de server zelf het beheer voert in plaats van uw shell.

systemd is het init-systeem op Ubuntu, Debian, Fedora en de meeste moderne Linux-servers. Het is het eerste proces dat start en het proces dat toezicht houdt op al het overige. Dat was niet altijd zo, en hoe systemd de init-scripts van voorheen verving is het lezen waard zodra u begrijpt wat een unit-bestand doet. Wanneer u een servicebestand schrijft, draagt u uw programma over aan die supervisor. Deze handleiding toont de kleinst werkende unit, de drie secties die elke unit bevat, hoe u deze inschakelt en de logs leest, hoe u deze volgens een schema uitvoert met een timer, en hoe u deze beveiligt zodat deze met zo min mogelijk rechten wordt uitgevoerd.

De kleinst werkende service

Een servicebestand bevindt zich in /etc/systemd/system/, eindigt op .service en heeft slechts enkele regels nodig. Maak er een aan voor een programma op /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

Dit is een volledige, werkende unit. ExecStart is het uit te voeren commando. WantedBy=multi-user.target betekent dat de service start zodra de server de normale multi-user modus bereikt; dit zorgt ervoor dat de service automatisch opstart bij het booten. Al het overige is verfijning.

De drie secties en waarvoor ze dienen

Elk unit-bestand is onderverdeeld in secties tussen vierkante haken. Een service gebruikt er drie.

[Unit] beschrijft de service en de bijbehorende relaties. De twee regels die u het meest zult gebruiken:

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

Description is het menselijk leesbare label dat u ziet in systemctl status. After=network-online.target vertelt systemd dat het programma pas gestart mag worden als het netwerk actief is; dit is van belang voor alles wat een poort opent of een uitgaande verbinding maakt.

[Service] bepaalt hoe het programma wordt uitgevoerd. Hier plaatst u de meeste instellingen:

[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 voert het programma uit als een account zonder privileges in plaats van als root; dit is de belangrijkste regel voor de veiligheid. Er is hier geen Type=-regel aanwezig, dus systemd valt terug op simple en gaat ervan uit dat het ExecStart-proces op de voorgrond blijft. Een programma dat naar de achtergrond forked, heeft het juiste Type= voor de manier van starten nodig, anders rapporteert de unit dat deze actief is terwijl de eigenlijke daemon al is afgesloten. Restart=on-failure en RestartSec=5 krijgen hieronder een eigen sectie, omdat dit de reden is waarom de meeste mensen een service schrijven.

[Install] bepaalt wat er gebeurt wanneer u de service inschakelt:

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target is wat de service koppelt aan het opstartproces wanneer u systemctl enable uitvoert. Zonder een [Install]-sectie kan een service handmatig worden gestart, maar zal deze niet automatisch opkomen na een reboot.

Inschakelen en monitoren

Nadat u een unit file heeft geschreven of bewerkt, moet u systemd herladen zodat de wijzigingen worden ingelezen. Schakel daarna de service in en start deze in één stap:

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

daemon-reload is de stap die vaak wordt vergeten: systemd slaat unit files op in een cache, waardoor een bewerking pas effect heeft na een herlaadactie. enable --now zorgt er zowel voor dat de service wordt ingeschakeld bij het opstarten als dat deze direct wordt gestart. Controleer de status:

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) en enabled zijn de statussen die u wilt zien. Om de uitvoer van het programma te lezen, vraagt u het journal specifiek voor deze unit op:

sudo journalctl -u myapp.service -f

De -f volgt nieuwe regels zodra deze binnenkomen, vergelijkbaar met tail -f. Alles wat uw programma naar de standaarduitvoer of standaardfout schrijft, komt hier terecht, zonder dat u zelf logging hoeft in te stellen.

Herstarten bij falen, de reden van uw komst

Het belangrijkste voordeel van een service is dat systemd uw programma herstart wanneer het stopt. Twee regels volstaan:

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure herstart het programma wanneer het afsluit met een niet-nul exitcode of crasht door een signaal zoals SIGKILL of SIGSEGV. Een schone afsluiting, of een stop door SIGTERM, SIGINT, SIGHUP of SIGPIPE, activeert dit niet. RestartSec=5 wacht vijf seconden tussen pogingen, zodat een programma dat direct crasht niet in een oneindige lus blijft hangen. Controleer dit door het proces te beëindigen en te zien hoe systemd het terugbrengt. Gebruik SIGKILL: de standaard SIGTERM telt als een schone stop, waardoor on-failure de service niet zou herstarten:

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

Binnen vijf seconden toont de status een nieuwe Main PID en active (running) opnieuw. Dat is de volledige functionaliteit, en het is de reden waarom een service beter is dan een programma laten draaien in tmux of screen.

Draai de service als een gebruiker zonder privileges en verhard de configuratie

Een service die als root draait, kan uw server volledig overnemen als het programma ooit wordt gecompromitteerd. Draai de service daarom onder een eigen gebruiker en voeg enkele systemd-richtlijnen toe om de service te isoleren. Maak eerst een systeemaccount aan zonder inlogmogelijkheid en zonder home-directory:

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

Stel vervolgens User=myapp in en voeg de verhardingsregels toe aan [Service]:

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

Elke regel verwijdert functionaliteit die het programma niet nodig heeft. NoNewPrivileges=true voorkomt dat het proces nieuwe privileges verkrijgt, zelfs niet via een setuid-binary. PrivateTmp=true geeft het proces een eigen /tmp die voor andere processen onzichtbaar is. ProtectSystem=strict maakt het volledige bestandssysteem alleen-lezen, met uitzondering van de paden die u opgeeft met ReadWritePaths=. ProtectHome=true verbergt /home volledig voor het proces. Dit volgt hetzelfde principe van minimale privileges als het plaatsen van een service achter een firewall: geef de service alleen toegang tot wat strikt noodzakelijk is. Als u de handleiding over het dichten van het IPv6-firewallgat op een VPS heeft gelezen, dan is dit de tegenhanger op host-niveau. Voor een service die direct verbonden is met het internet, combineert u deze verharding met Fail2ban voor SSH en een default-deny firewall.

In plaats van dit alles handmatig in te typen en het risico te lopen een richtlijn te vergeten, kunt u een volledige, verharde unit genereren en deze kopiëren:

Toolsystemd service and timer generator

Timers: de moderne cron

Een systemd timer voert een service uit volgens een schema en is de moderne vervanger voor een cron job. Een timer bestaat uit twee bestanden: een .service die het werk uitvoert en een .timer die bepaalt wanneer dit gebeurt. Stel dat u elke dag om 03:00 uur een back-up wilt maken. De service voert de taak eenmalig uit en stopt daarna:

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

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

Type=oneshot geeft aan systemd door dat het programma wordt uitgevoerd, voltooid en afgesloten, in plaats van resident in het geheugen te blijven. De timer plant de uitvoering:

# /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 staat voor 03:00 uur elke dag. Test elke kalender-expressie met systemd-analyze calendar "*-*-* 03:00:00"; dit bevestigt of de syntax correct is en toont de volgende tijdstippen waarop de taak wordt geactiveerd. Persistent=true voert een gemiste taak direct uit zodra de server weer online is als deze om 03:00 uur uitstond, iets wat cron niet kan. Let op dat een timer wordt ingeschakeld via timers.target, niet via multi-user.target. Schakel de timer in, niet de service:

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

list-timers toont elke timer met de volgende en laatste uitvoering, zodat u in één oogopslag ziet wanneer uw taak de volgende keer wordt gestart. De generator hierboven maakt de gekoppelde .service en .timer voor u aan wanneer u de timer-modus inschakelt. In vergelijking met een cron-regel biedt een timer u echte logs in de journal, dezelfde hardening-richtlijnen als elke andere service en de inhaalfunctie voor gemiste taken die Persistent=true biedt. Cron is prima voor een eenvoudige taak; een timer is de betere tool zodra de taak belangrijk is.

FAQ

Wat is het verschil tussen een systemd-service en een cron-job?

Een service houdt een langlopend programma actief: het start bij het opstarten, herstart bij een fout en logt naar de journal. Een cron-job voert een kort commando uit volgens een schema en stopt daarna. Wanneer u de planning nodig heeft, maar ook journal-logs, hardening en inhaalmogelijkheden voor gemiste taken wilt, gebruik dan een systemd-timer. Deze koppelt een .timer-schema aan een oneshot-service en vervangt cron voor de meeste servertaken.

Waar plaats ik mijn systemd-servicebestand?

Plaats uw eigen units in /etc/systemd/system/, met een naam die eindigt op .service. Die map is bedoeld voor units die door de beheerder worden toegevoegd en heeft voorrang op units die door pakketten in /lib/systemd/system/ worden geleverd. Voer na het aanmaken of bewerken van een bestand sudo systemctl daemon-reload uit, zodat systemd de wijziging verwerkt.

Hoe zorg ik ervoor dat een service herstart als deze crasht?

Voeg Restart=on-failure en RestartSec=5 toe aan de [Service]-sectie, voer daarna sudo systemctl daemon-reload uit en herstart de service. systemd start het programma opnieuw op wanneer het afsluit met een exitcode die niet nul is of sterft door een crash-signaal, waarbij het vijf seconden wacht tussen de pogingen. Test dit met sudo systemctl kill -s SIGKILL myapp.service; SIGTERM, het standaard signaal, telt als een nette stop en activeert on-failure niet. Controleer of systemctl status binnen enkele seconden een nieuw PID toont.

Hoe draai ik een systemd-service als een niet-rootgebruiker?

Maak een systeemaccount aan met sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp en voeg vervolgens User=myapp toe aan de [Service]-sectie. Voeg NoNewPrivileges=true, PrivateTmp=true en ProtectSystem=strict toe zodat het proces draait met zo min mogelijk toegangsrechten. Draaien als een gebruiker zonder privileges is de belangrijkste wijziging die u kunt doorvoeren voor de veiligheid van een service.

Waarom is mijn service niet gestart?

Voer systemctl status myapp.service uit voor de samenvatting en journalctl -u myapp.service voor de volledige uitvoer. De meest voorkomende oorzaken zijn een onjuist pad in ExecStart, een ontbrekende WorkingDirectory, een rechtenfout omdat User= een bestand niet kan lezen, of een vergeten sudo systemctl daemon-reload na een bewerking. De journal toont de foutmelding van het programma zelf, die het probleem meestal direct benoemt.