Programma draaien als systemd service op VPS
Leer hoe u een programmatisch servicebestand schrijft voor Linux. Zorg dat uw software automatisch herstart bij een crash en gebruik systemd timers.
Wat een systemd service is en waarom u er een nodig heeft
Een systemd service is een klein tekstbestand dat uw server instructies geeft over het uitvoeren van een programma: het opstarten bij het booten, het herstarten bij een crash, en het versturen van de output 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 herstart. Een programma dat is ingekapseld in een systemd service blijft draaien, omdat de server het proces beheert 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 alles controleert. Wanneer u een servicebestand schrijft, draagt u uw programma over aan deze supervisor. Deze handleiding behandelt de kleinste werkende eenheid, de drie secties die elke unit bevat, hoe u deze activeert en de logs leest, hoe u deze via een timer op een schema laat draaien, en hoe u de beveiliging maximaliseert zodat het programma met minimale privileges draait.
De kleinste werkende service
Een servicebestand staat in /etc/systemd/system/, heeft de extensie .service en heeft slechts enkele regels nodig. Maak een bestand 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.targetDit is een volledige, werkende eenheid. ExecStart is het commando dat moet worden uitgevoerd. WantedBy=multi-user.target betekent dat de service start zodra de server de normale multi-user modus bereikt, wat ervoor zorgt dat de service bij het opstarten wordt geladen. De rest is voor verfijning.
De drie secties en hun functies
Elk unit file is onderverdeeld in secties tussen vierkante haken. Een service gebruikt er drie.
[Unit] beschrijft de service en de relaties ermee. De twee belangrijkste regels voor u zijn:
[Unit]
Description=My application
After=network-online.target
Wants=network-online.targetDescription is het label voor de gebruiker dat u ziet in systemctl status. After=network-online.target zorgt ervoor dat systemd het programma pas start als het netwerk actief is. Dit is noodzakelijk voor applicaties die een port binden of een outbound verbinding maken.
[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=infoUser=myapp voert het programma uit als een unprivileged account in plaats van root. Dit is de belangrijkste regel voor de veiligheid. Restart=on-failure en RestartSec=5 hebben hun eigen sectie hieronder, omdat dit de reden is waarom de meeste gebruikers een service maken.
[Install] bepaalt wat er gebeurt wanneer u de service inschakelt:
[Install]
WantedBy=multi-user.targetWantedBy=multi-user.target koppelt de service aan het opstartproces wanneer u systemctl enable uitvoert. Zonder een [Install] sectie kan een service handmatig worden gestart, maar start deze niet automatisch na een reboot.
Schakel het in en controleer het
Herlaad systemd nadat u een unit file heeft geschreven of bewerkt, zodat de wijzigingen worden doorgevoerd. Gebruik vervolgens deze stap om de service in één keer in te schakelen en te starten:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.servicedaemon-reload is de stap die vaak wordt vergeten: systemd cachet unit files, dus een bewerking heeft geen effect totdat u herlaadt. enable --now schakelt de service in voor de volgende boot en start deze direct. Controleer dit met:
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 gewenste resultaten. Om de output van het programma te lezen, kunt u de journal vragen om alleen deze unit:
sudo journalctl -u myapp.service -fDe -f volgt nieuwe regels zodra ze binnenkomen, vergelijkbaar met tail -f. Alles wat uw programma naar standard output of standard error schrijft, komt hier terecht zonder dat u een logging-configuratie hoeft in te stellen.
Herstarten bij fouten, de reden dat u hier bent
Het belangrijkste voordeel van een service is dat systemd uw programma herstart wanneer het stopt. De volgende twee regels voeren dit uit:
[Service]
Restart=on-failure
RestartSec=5Restart=on-failure herstart het programma wanneer het afsluit met een non-zero code of crasht door een signaal zoals SIGKILL of SIGSEGV. Een nette afsluiting, of een stop via SIGTERM, SIGINT, SIGHUP of SIGPIPE, activeert dit niet. RestartSec=5 wacht vijf seconden tussen de pogingen. Dit voorkomt dat een programma dat direct crasht in een eindeloze lus terechtkomt. Controleer dit door het proces te killen en te kijken hoe systemd het herstart. Gebruik SIGKILL: de standaard SIGTERM wordt beschouwd als een nette stop, waardoor on-failure de service niet zou herstarten:
sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.serviceBinnen vijf seconden toont de status opnieuw een nieuwe Main PID en active (running). Dit is de volledige functionaliteit, en dit is waarom een service beter is dan een programma dat draait in tmux of screen.
Voer het uit als een onbevoegde gebruiker en verscherp de beveiliging
Een service die als root draait, kan alles doen op uw server als het programma wordt misbruikt. Voer de service uit onder een eigen gebruiker en gebruik systemd-directives om de service te beperken. Maak eerst een systeemaccount aan zonder login en zonder home-directory:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myappStel vervolgens User=myapp in en voeg hardening-regels toe aan [Service]:
[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=trueElke regel verwijdert een functie die het programma niet nodig heeft. NoNewPrivileges=true voorkomt dat het proces nieuwe privileges verkrijgt, zelfs via een setuid-binary. PrivateTmp=true geeft het een private /tmp die voor andere processen onzichtbaar is. ProtectSystem=strict maakt het volledige bestandssysteem read-only, behalve de paden die u opgeeft met ReadWritePaths=. ProtectHome=true verbergt /home volledig voor de service. Dit is hetzelfde principe van 'least privilege' als een service achter een firewall plaatsen: geef alleen wat strikt noodzakelijk is. Als u de handleiding over het dichten van de IPv6-firewallkloof op een VPS heeft gelezen, dan is dit de lokale uitvoering van hetzelfde concept. Gebruik voor een service die verbonden is met het internet deze hardening in combinatie met Fail2ban voor SSH en een default-deny firewall.
In plaats van alles handmatig te typen om fouten in directives te voorkomen, kunt u een volledige, beveiligde unit genereren en kopiëren:
Timers: de moderne cron
Een systemd timer voert een service uit volgens een schema. Het is de moderne vervanger voor een cron job. Een timer bestaat uit twee bestanden: een .service die het werk uitvoert, en een .timer die het tijdstip bepaalt. Stel dat u elke dag om 3:00 uur een backup 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.shType=oneshot vertelt systemd dat het programma wordt uitgevoerd, klaar is en stopt, in plaats van dat het in het geheugen blijft staan. 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.targetOnCalendar=*-*-* 03:00:00 betekent elke dag om 3:00 uur. Test elke kalenderexpressie met systemd-analyze calendar "*-*-* 03:00:00". Dit bevestigt of de expressie correct wordt verwerkt en toont de volgende tijdstippen van uitvoering. Persistent=true voert een gemiste taak direct uit zodra de server weer aan staat als deze om 3:00 uur uitstond. Dit is iets wat cron niet kan. Let op dat een timer wordt ingeschakeld via timers.target en niet via multi-user.target. Schakel de timer in, niet de service:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timerslist-timers toont elke timer met de volgende en laatste uitvoering. Zo ziet u in één oogopslag wanneer uw taak de volgende keer start. De generator hierboven maakt de bijbehorende .service en .timer voor u aan wanneer u timer mode inschakelt. Vergeleken met een cron regel biedt een timer echte logs in de journal, dezelfde hardening directives als elke service, en de mogelijkheid om gemiste runs in te halen via Persistent=true. Cron is nog steeds geschikt voor eenvoudige taken; een timer is het betere hulpmiddel voor belangrijke taken.
FAQ
Wat is het verschil tussen een systemd service en een cron job?
Een service houdt een programma dat langdurig draait actief: deze start bij het opstarten van het systeem, start opnieuw bij een fout en logt naar de journal. Een cron job voert een korte opdracht uit volgens een schema en stopt daarna. Wanneer u een schema nodig heeft, maar ook journal logs, hardening en het inhalen van gemiste runs wilt, gebruik dan een systemd timer. Deze combineert een .timer schema met een oneshot service en vervangt cron voor de meeste servertaken.
Waar plaats ik mijn systemd service file?
Plaats uw eigen units in /etc/systemd/system/, met een naam die eindigt op .service. Deze directory is voor units die de administrator toevoegt en heeft prioriteit boven units die via pakketten in /lib/systemd/system/ worden geleverd. Voer na het maken of bewerken van een bestand sudo systemctl daemon-reload uit, zodat systemd de wijziging detecteert.
Hoe zorg ik dat een service opnieuw start als deze crasht?
Voeg Restart=on-failure en RestartSec=5 toe aan de [Service] sectie, voer vervolgens sudo systemctl daemon-reload uit en start de service opnieuw op. systemd start het programma opnieuw wanneer het afsluit met een non-zero code of crasht door een crash signal, met een pauze van vijf seconden tussen de pogingen. Test dit met sudo systemctl kill -s SIGKILL myapp.service — SIGTERM, het standaard signaal, telt als een nette stop en activeert geen on-failure — en controleer of systemctl status binnen enkele seconden een nieuwe PID laat zien.
Hoe draai ik een systemd service als een non-root user?
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 de minimale benodigde rechten. Het draaien als een unprivileged user is de belangrijkste wijziging die u kunt maken 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 output. De meest voorkomende oorzaken zijn een foutief pad in ExecStart, een ontbrekende WorkingDirectory, een permissiefout omdat User= een bestand niet kan lezen, of een vergeten sudo systemctl daemon-reload na een bewerking. De journal toont de eigen foutmelding van het programma, die het probleem meestal direct benoemt.