De betrouwbaarste manier om een gameserver in de lucht te houden, is het proces te beheren met een systemd-service-bestand. Processen die je met screen of nohup op de achtergrond start, verdwijnen wanneer de server herstart, en niemand brengt ze terug als ze crashen. systemd daarentegen start je gameproces automatisch bij het opstarten, herstart het na een crash en maakt alle uitvoer vanaf één plek leesbaar. In dit artikel maken we van een gameproces vanaf nul een systemd-service.
Waarom een systemd-service gebruiken?
Of het nu Metin2, Minecraft, een C++-gamecore of je eigen serverbinary is: het zijn allemaal langlopende achtergrondprocessen. Ze rechtstreeks vanuit een terminal starten levert deze problemen op:
- Het proces sterft zodra de SSH-sessie sluit.
- De game komt na een herstart niet automatisch terug.
- Een crash vereist handmatig ingrijpen.
- Logs zijn verspreid, waardoor het lastig is te volgen wat er gebeurde.
systemd lost dit allemaal op. Het is het standaard init-systeem op moderne Linux-distributies (Debian, Ubuntu, AlmaLinux, Rocky), dus je hoeft niets extra's te installeren.
Maak een aparte gebruiker voor de service
Om veiligheidsredenen wil je het gameproces niet als root draaien. Maak een speciale systeemgebruiker zonder shell aan:
sudo useradd --system --create-home --shell /usr/sbin/nologin gameserver
sudo mkdir -p /opt/game
sudo chown -R gameserver:gameserver /opt/game
Plaats je gamebestanden onder /opt/game en controleer de rechten van de uitvoerbare binary: chmod +x /opt/game/start. Deze gebruiker draait alleen de game en kan niet op het systeem inloggen, wat de impact van een mogelijk beveiligingslek beperkt.
Het systemd-servicebestand schrijven
Servicedefinities staan in bestanden met de extensie .service onder /etc/systemd/system/. Maak /etc/systemd/system/gameserver.service:
[Unit]
Description=Gameserver
After=network.target
[Service]
Type=simple
User=gameserver
Group=gameserver
WorkingDirectory=/opt/game
ExecStart=/opt/game/start
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
Wat de secties betekenen:
- [Unit] —
After=network.targetzorgt ervoor dat de service niet start voordat het netwerk klaar is. - Type=simple — De juiste keuze wanneer het proces op de voorgrond draait (de meeste gameservers doen dat). Als het proces zichzelf daemoniseert, gebruik dan
Type=forking. - WorkingDirectory — Sommige gamecores zoeken configbestanden relatief aan de werkmap; stel dit altijd in.
- ExecStart — Het volledige commando dat wordt uitgevoerd. Gebruik altijd een absoluut pad; systemd gaat niet uit van een
PATH. - Restart=on-failure — Herstart het proces als het met een niet-nul code afsluit.
RestartSec=5wacht 5 seconden voor de herstart. - LimitNOFILE — Verhoogt de limiet voor open bestanden/sockets om veel spelersverbindingen aan te kunnen.
De service inschakelen en starten
Nadat je het bestand hebt opgeslagen, laat systemd de nieuwe definitie inlezen, schakel de service in om bij het opstarten te starten en draai hem:
sudo systemctl daemon-reload
sudo systemctl enable gameserver
sudo systemctl start gameserver
Controleer de status:
sudo systemctl status gameserver
Zie je active (running) in de uitvoer, dan draait de service. Het commando enable koppelt de service aan multi-user.target, zodat de game terugkomt telkens als de server opstart. Om beide in één commando te doen, gebruik je systemctl enable --now gameserver.
Logs bekijken met journalctl
systemd verzamelt de standaarduitvoer en foutuitvoer van het proces in journald, dus je hoeft geen apart logbestand te beheren:
# Toon recente logs
sudo journalctl -u gameserver
# Live volgen (zoals tail)
sudo journalctl -u gameserver -f
# Alleen de logs van vandaag
sudo journalctl -u gameserver --since today
Als je gamecore zijn eigen logbestanden schrijft, blijven die op hun plek; journald legt alleen vast wat naar de console wordt geprint. Om te voorkomen dat de schijf volloopt, kun je een limiet als SystemMaxUse=500M instellen in /etc/systemd/journald.conf.
Netjes afsluiten en veelgemaakte fouten
Veel gameservers hebben bij het stoppen een zachte afsluiting nodig om spelersdata op te slaan. Standaard stuurt systemd SIGTERM; idealiter vangt je proces dit op en sluit het netjes af. Pas de wachttijd aan indien nodig:
[Service]
TimeoutStopSec=30
KillSignal=SIGINT
De meest voorkomende problemen: een verkeerd pad (ExecStart moet absoluut zijn), een rechtenfout (de binary moet van de gebruiker gameserver zijn en uitvoerbaar zijn) en een verkeerd Type (Type=simple gebruiken voor een zelf-forkend proces laat de service voortdurend lijken te "herstarten"). Vergeet niet na elke wijziging sudo systemctl daemon-reload te draaien.
Veelgestelde vragen
Wat moet ik doen na het bewerken van de service?
Telkens als je het .service-bestand wijzigt, draai je eerst sudo systemctl daemon-reload en daarna sudo systemctl restart gameserver. Anders blijft systemd de oude definitie gebruiken.
Kan ik meerdere game-instanties draaien?
Ja. Schrijf hetzelfde sjabloon als een template-unit met de naam gameserver@.service en start instanties zoals systemctl start gameserver@1, gameserver@2. Elke instantie krijgt via de variabele %i zijn eigen poort of configuratie.
Waarom blijft het proces herstarten?
Meestal betekent het dat de binary meteen afsluit. Lees de echte foutmelding met journalctl -u gameserver -f; vaak komt het door een ontbrekend configbestand, een onbeschikbare poort of een verkeerde werkmap.
Wil je je serverinfrastructuur verstevigen? Heb je hulp nodig bij het opzetten van een gameserver, systemd-services of Linux-beheer, neem dan contact met me op.