Een eigen systemd-service aanmaken en automatisch laten starten
Het unit-bestand regel voor regel: Type, User, WorkingDirectory, Restart en network-online.target. Met de foutmeldingen woordelijk en het bewijs dat de dienst een herstart echt overleeft.
Een programma dat na een herstart niet uit zichzelf terugkomt, is op een server geen dienst maar een risico. nohup, screen en tmux houden een proces in leven zolang er niets gebeurt. In dit artikel schrijven wij regel voor regel een eigen unit-bestand, tonen wij de foutmeldingen woordelijk en leggen wij uit hoe u er weer uit komt wanneer de dienst in de startcarrousel blijft hangen.
Alle commando's draaien als root. Getest op Debian 13 (systemd 257), Debian 12 (252), Ubuntu 24.04 LTS (255) en Ubuntu 22.04 LTS (249). Waar de vier systemen van elkaar afwijken, staat dat erbij.
Wat een systemd-service doet en wat nohup en screen niet doen
Het verschil zit niet in gemak maar in verantwoordelijkheid. systemd start het proces bij het booten in een vastgelegde volgorde, plaatst het in een eigen cgroup, verzamelt stdout en stderr in het journal, start het na een crash opnieuw op en sluit het bij het afsluiten netjes af met SIGTERM. Die cgroup mist u het pijnlijkst: een via nohup gestart script laat verweesde kindprocessen achter, terwijl een unit haar hele groep opruimt.
Voorbereiding: programma, gebruiker en map
Voordat de unit er komt, moet werken wat zij moet starten. Als voorbeeld dient een script dat naar stdout schrijft. Precies dat is het punt: een dienst onder systemd logt niet zelf naar een bestand, maar schrijft naar stdout, en systemd zet het in het journal.
mkdir -p /opt/kh-demo
cat > /opt/kh-demo/run.sh <<'EOF'
#!/bin/bash
set -euo pipefail
while true; do
echo "kh-demo draait, $(date --iso-8601=seconds), PWD=$PWD, GREETING=${GREETING:-niet ingesteld}"
sleep 10
done
EOF
chmod +x /opt/kh-demo/run.sh
Daarna een eigen systeemgebruiker: --system kent een UID onder 1000 toe en /usr/sbin/nologin verhindert interactief inloggen.
useradd --system --no-create-home --home-dir /opt/kh-demo --shell /usr/sbin/nologin khdemo
id khdemo
chown -R root:khdemo /opt/kh-demo
chmod 750 /opt/kh-demo
De beslissende test vóór het unit-bestand: draait het programma onder deze gebruiker? Wie deze stap overslaat, zit later systemd te debuggen terwijl het probleem in het programma zelf zit.
timeout 3 runuser -u khdemo -- /opt/kh-demo/run.sh; echo "Exitcode $?"
Exitcode 124 is hier het gewenste resultaat: timeout heeft een draaiend programma afgebroken. Elke andere waarde betekent dat het script uit zichzelf is gestopt, en dan ligt de fout niet bij systemd.
Het unit-bestand regel voor regel
Eigen units horen in /etc/systemd/system/, niet in /lib/systemd/system/ of /usr/lib/systemd/system/: die twee mappen zijn van de pakketbeheerder en worden bij de volgende apt upgrade overschreven.
cat > /etc/systemd/system/kh-demo.service <<'EOF'
[Unit]
Description=KernelHost Demo Worker
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=khdemo
Group=khdemo
WorkingDirectory=/opt/kh-demo
EnvironmentFile=-/etc/kh-demo.env
ExecStart=/opt/kh-demo/run.sh
Restart=on-failure
RestartSec=5s
TimeoutStopSec=20s
SyslogIdentifier=kh-demo
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
[Install]
WantedBy=multi-user.target
EOF
Sectie [Unit]
Description= is de tekst die systemctl status toont. Technisch heeft die geen enkel effect, maar hij bepaalt wel of u om drie uur 's nachts begrijpt wat er precies op failed staat. Schrijf een zin, geen los woord.
After= legt alleen de volgorde vast, niet de afhankelijkheid. After=network-online.target betekent: wordt dit target tijdens deze start toch al geactiveerd, wacht er dan op. Het betekent niet dat het target ook daadwerkelijk gestart wordt.
Wants= vraagt het target wel echt aan, en dan als zwakke afhankelijkheid. Mislukt het target, dan start de dienst alsnog. De tegenhanger Requires= zou de dienst mee de fout in trekken. Voor de meeste toepassingen is Wants= de juiste keuze, want een dienst die door een netwerktime-out helemaal niet start, is erger dan een dienst die even in het luchtledige draait en dankzij Restart= vanzelf terugkomt.
Sectie [Service]
User= en Group= bepalen de identiteit van het proces. Zonder deze regels draait de dienst als root. Op Debian en Ubuntu maakt useradd standaard een gelijknamige groep aan, en daarom klopt Group=khdemo.
WorkingDirectory= is de werkmap van het proces. Zonder deze opgave start de dienst in /. Elk programma dat configuratiebestanden, templates of plug-ins relatief ten opzichte van de huidige map zoekt, gaat dan meteen onderuit. Bestaat de opgegeven map niet, dan eindigt de start met 200/CHDIR.
EnvironmentFile= laadt variabelen uit een bestand. Het minteken vóór het pad maakt dat bestand optioneel. Zonder dat minteken mislukt de start zodra het bestand ontbreekt. De inhoud bestaat uit simpele KEY=VALUE-regels, geen shell. Er export voor zetten is verkeerd, en PORT=$BASE_PORT wordt niet uitgewerkt.
ExecStart= heeft een absoluut pad naar het uitvoerbare bestand nodig. Dat is de regel waarop de meesten stuklopen, en daarom krijgt zij verderop een eigen hoofdstuk.
TimeoutStopSec= begrenst hoelang systemd na de SIGTERM wacht voordat SIGKILL volgt. Standaard zijn dat 90 seconden.
SyslogIdentifier= bepaalt de naam waaronder de regels in het journal verschijnen. Zonder deze regel heet de afzender run.sh, en daar kunt u bij het filteren niets mee.
NoNewPrivileges=true verbiedt het proces en al zijn kindprocessen om via setuid-binaries extra rechten te verwerven. PrivateTmp=true geeft de dienst een eigen /tmp. ProtectSystem=full koppelt /usr, /boot en /etc alleen-lezen aan. Het niveau strict gaat verder en verlangt dan StateDirectory= of ReadWritePaths= voor alles wat beschrijfbaar moet blijven.
Sectie [Install]
WantedBy=multi-user.target beantwoordt de vraag wanneer de dienst automatisch moet starten. multi-user.target is het normale bedrijf met meerdere gebruikers zonder grafische schil, dus precies wat een server bereikt. Pas systemctl enable verwerkt deze sectie en legt de symlink aan. Ontbreekt [Install], dan breekt enable af met The unit files have no installation config (WantedBy=, RequiredBy=, Also=, Alias= settings in the [Install] section, and DefaultInstance= for template units). De dienst is dan nog wel met de hand te starten, maar komt na een herstart nooit meer terug.
Type=simple, Type=exec en Type=forking
De Type= beantwoordt precies één vraag: waaraan herkent systemd dat de dienst gestart is?
Bij Type=simple geldt de dienst als gestart zodra het proces is aangemaakt, dus na de fork() en nog voordat het programma wordt uitgevoerd. Dit is de standaard en de juiste keuze voor vrijwel alle moderne programma's die op de voorgrond blijven draaien.
Bij Type=exec wacht systemd bovendien tot execve() geslaagd is. Het praktische verschil is groot: met Type=simple meldt systemctl start succes, zelfs wanneer het binary helemaal niet bestaat, en verschijnt de fout pas daarna in het journal. Met Type=exec mislukt de start in precies dat geval meteen. Beschikbaar sinds systemd 240 en dus op alle vier de distributies.
Bij Type=forking gaat systemd ervan uit dat het programma zichzelf naar de achtergrond stuurt: het gestarte proces eindigt, een kindproces draait verder, en pas dat einde geldt als startsignaal. Dat is klassiek gedrag van een Unix-daemon. Wie dit type gebruikt, heeft er in de regel ook PIDFile= met een absoluut pad bij nodig, want anders gokt systemd welk van de overgebleven processen het hoofdproces is.
Het advies is duidelijk: gebruik Type=forking alleen wanneer het programma zich er absoluut niet van laat weerhouden. Bijna elke software heeft er een schakelaar voor, vaak --foreground, -D FOREGROUND, --no-daemon of daemon off; in de configuratie. Op de voorgrond plus Type=simple is de unit korter, belandt de logging in het journal en werkt Restart= betrouwbaar.
De klassieke verkeerde combinatie: een programma dat zichzelf naar de achtergrond stuurt onder Type=simple. systemd ziet het startproces eindigen, houdt de dienst voor beëindigd en maakt de kindprocessen mee af. Het symptoom is Active: inactive (dead) direct na een systemctl start die geen enkele fout meldde. In het omgekeerde geval, een voorgrondprogramma onder Type=forking, wacht systemd op een einde dat nooit komt: Job for foo.service failed because a timeout was exceeded, na 90 seconden.
After=network-online.target correct inrichten
Hier stopt bijna elke handleiding te vroeg. network.target betekent alleen dat het netwerkbeheer gestart is, niet dat er een IP-adres geconfigureerd is. Wie een bereikbaar adres nodig heeft, bijvoorbeeld omdat het programma aan een vast IP bindt, moet bij network-online.target zijn.
Dat target wordt echter niet vanzelf bereikt. Het vereist dat een passende wachtdienst geactiveerd is, en die verschilt per netwerkbeheer:
systemctl list-unit-files 'systemd-networkd-wait-online.service' 'NetworkManager-wait-online.service' 'ifupdown-wait-online.service' --no-pager
Op Ubuntu Server 22.04 en 24.04 loopt het netwerk via netplan met systemd-networkd. Daar is systemd-networkd-wait-online.service actief en heeft network-online.target een echte betekenis. Op een klassieke Debian-installatie met ifupdown bestaat ifupdown-wait-online.service wel, maar is die niet geactiveerd. Het target geldt dan onmiddellijk als bereikt, en de wachttijd waarop u rekent vindt gewoon niet plaats.
Op Debian 12 en 13 met ifupdown activeert u de wachtdienst zo nodig eenmalig:
systemctl enable ifupdown-wait-online.service
Twee wachtdiensten tegelijk activeren is geen goed idee, want dan wacht elk van beide op zijn eigen netwerkbeheer en loopt er onvermijdelijk één in de time-out van 90 seconden. Dat is de meest voorkomende oorzaak van een server die plotseling anderhalve minuut langer over het booten doet. Robuuster dan welke volgorde ook is trouwens een programma dat een ontbrekende verbinding bij de start gewoon verdraagt, in combinatie met Restart=on-failure.
Restart, RestartSec en de startrem
Restart=on-failure herstart bij een exitcode ongelijk aan nul, bij een signaal zoals SIGSEGV en bij een watchdog-time-out, maar niet na exit 0 en niet na een handmatige systemctl stop. Restart=always herstart bovendien na een nette afsluiting, en daarmee bouwt u het snelst een oneindige lus voor uzelf.
Daartegen heeft systemd een ingebouwde rem: standaard zijn vijf startpogingen binnen tien seconden toegestaan (StartLimitBurst=5, StartLimitIntervalSec=10s). Daarna geeft systemd het op en meldt:
kh-demo.service: Start request repeated too quickly.
kh-demo.service: Failed with result 'exit-code'.
Failed to start kh-demo.service - KernelHost Demo Worker.
De dienst blijft dan in de toestand failed staan en reageert ook niet meer op systemctl start, totdat de teller wordt teruggezet:
systemctl reset-failed kh-demo.service
Twee details kosten hier geregeld tijd. Ten eerste horen StartLimitBurst= en StartLimitIntervalSec= in de sectie [Unit] thuis, niet in [Service]. In de verkeerde sectie worden ze genegeerd, met een waarschuwing in het journal maar zonder foutmelding. Ten tweede tellen handmatige aanroepen van systemctl restart gewoon mee: wie tijdens het debuggen vijf keer herstart, activeert de rem zelf.
Vuistregel: StartLimitIntervalSec groter dan RestartSec maal StartLimitBurst.
Op Debian 13 en Ubuntu 24.04 bestaan daarnaast RestartSteps= en RestartMaxDelaySec= (vanaf systemd 254) voor exponentieel oplopende wachttijden. Op Debian 12 en Ubuntu 22.04 bestaan deze directives niet en worden ze genegeerd.
Activeren, starten en aantonen dat het draait
Vóór de eerste start loont een syntaxcontrole. Die vindt typefouten in namen van directives, verkeerd geschreven secties en ontbrekende programma's, zonder ook maar iets te starten:
systemd-analyze verify /etc/systemd/system/kh-demo.service
Geen uitvoer betekent: alles in orde. Een typefout als WorkingDirectiry= levert, afhankelijk van de systemd-versie, Unknown key name 'WorkingDirectiry' in section 'Service', ignoring of Unknown key 'WorkingDirectiry' in section [Service], ignoring op. Hier ontstaan de stille fouten: systemd negeert onbekende sleutels, de dienst start wel, maar gedraagt zich anders dan verwacht.
systemctl daemon-reload
daemon-reload leest de unit-bestanden opnieuw in, maar herstart niets. Wie de reload vergeet, krijgt bij het volgende systemctl status: Warning: The unit file, source configuration file or drop-ins of kh-demo.service changed on disk. Run 'systemctl daemon-reload' to reload units. Onthoud de volgorde: eerst daemon-reload, dan restart.
systemctl enable kh-demo.service
systemctl start kh-demo.service
Beide stappen tegelijk gaan met systemctl enable --now kh-demo.service.
Nu het deel dat de meeste handleidingen overslaan: het bewijs dat het echt gewerkt heeft en niet alleen dat er geen foutmelding kwam. Vier onafhankelijke aanwijzingen.
Ten eerste: de symlink bestaat. Die symlink is het complete mechanisme achter enable. Ontbreekt hij, dan start de dienst na de herstart niet, wat status op dat moment ook beweert.
ls -l /etc/systemd/system/multi-user.target.wants/kh-demo.service
systemctl is-enabled kh-demo.service
Ten tweede: de toestand. Doorslaggevend is wat tussen haakjes op de regel Active: staat. active (running) betekent dat er een proces draait. active (exited) betekent dat het programma klaar is en er niemand meer over is. Voor een permanent draaiende dienst is dat een fout, ook al staat er groen naast.
systemctl status kh-demo.service --no-pager
systemctl is-active kh-demo.service
Ten derde: het journal. Niet alleen of er regels binnenkomen, maar ook of het de juiste regels zijn.
journalctl -u kh-demo.service -n 20 --no-pager
Live meelezen: journalctl -u kh-demo.service -f. Alleen de laatste start: -b. Een tijdvak: --since "-1h". Meer daarover in het artikel journalctl: logs onder systemd analyseren.
Ten vierde: de werkzame configuratie. Niet het bestand, maar wat systemd ervan gemaakt heeft. Dat verschil telt zodra er drop-ins in het spel zijn.
systemctl show kh-demo.service -p ExecStart -p User -p WorkingDirectory -p Restart -p MainPID
systemctl cat kh-demo.service
Het sluitende bewijs blijft een echte herstart. Daarna laat dit commando zien of er iets is blijven liggen:
systemctl list-units --type=service --state=failed --no-pager
De meest voorkomende fouten, woordelijk geciteerd
systemd meldt startfouten via eigen exitcodes boven de 200. Het getal in de status vertelt al waar u moet zoeken.
| Code | Betekenis | Oorzaak |
|---|---|---|
| 200/CHDIR | EXIT_CHDIR | WorkingDirectory= bestaat niet of is voor de gebruiker niet toegankelijk |
| 203/EXEC | EXIT_EXEC | ExecStart= niet gevonden, niet uitvoerbaar of verkeerde interpreter |
| 216/GROUP | EXIT_GROUP | Group= bestaat niet |
| 217/USER | EXIT_USER | User= bestaat niet |
| 219/CGROUP | EXIT_CGROUP | cgroup kon niet worden aangemaakt |
| 238/STATE_DIRECTORY | EXIT_STATE_DIRECTORY | StateDirectory= bestaat al, met de verkeerde eigenaar |
203/EXEC: het verkeerde ExecStart-pad
In het journal staat daarover een regel in de vorm kh-demo.service: Failed at step EXEC spawning /opt/kh-demo/run.sh: No such file or directory, gevolgd door Main process exited, code=exited, status=203/EXEC.
No such file or directory is misleidend, want die melding heeft drie oorzaken. Ten eerste: het bestand bestaat werkelijk niet, meestal door een typefout of doordat het binary in /usr/local/bin staat in plaats van /usr/bin. Ten tweede: de executable-bit ontbreekt, en dan meldt het journal Permission denied. Ten derde, het gemeenste geval: het bestand is wel uitvoerbaar, maar de shebang-regel wijst naar het niets. Een script met #!/usr/bin/python loopt op Debian 12 en nieuwer precies zo vast, omdat daar alleen /usr/bin/python3 bestaat. De kernel meldt dat de interpreter ontbreekt, en systemd geeft dat door alsof het script ontbreekt.
De controle kost drie seconden:
ls -l /opt/kh-demo/run.sh
head -n 1 /opt/kh-demo/run.sh
Relatieve paden en PATH
ExecStart=node server.js werkt niet, ook niet met een ingestelde WorkingDirectory=. De fout bij het inlezen luidt Neither a valid executable name nor an absolute path. Alleen het eerste item moet absoluut zijn, de argumenten daarna mogen relatief blijven. Juist is ExecStart=/usr/bin/node server.js, waarbij server.js ten opzichte van de WorkingDirectory wordt opgelost. Het juiste pad levert command -v:
command -v bash
Pas op met versiebeheerders: onder nvm levert dat een pad als /root/.nvm/versions/node/v22.14.0/bin/node op, dat voor de dienstgebruiker helemaal niet bestaat. Runtimes voor diensten installeert u systeembreed, bijvoorbeeld via NodeSource of Adoptium Temurin.
Ontbrekende omgevingsvariabelen
De klassieker: bij interactief inloggen draait het programma, als dienst niet. De reden is dat systemd geen login-shell start. Noch /etc/profile, noch ~/.bashrc, noch ~/.profile wordt gelezen, en alles wat daar met export is gezet, ontbreekt in de dienst. De PATH van een systeemdienst is een vast minimaal pad, meestal /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin.
Variabelen horen daarom in de unit zelf of in een eigen bestand:
cat > /etc/kh-demo.env <<'EOF'
GREETING=hallo
LANG=nl_NL.UTF-8
EOF
chmod 640 /etc/kh-demo.env
chown root:khdemo /etc/kh-demo.env
systemctl restart kh-demo.service
journalctl -u kh-demo.service -n 5 --no-pager
Staat er in de uitvoer nu GREETING=hallo in plaats van GREETING=niet ingesteld, dan is het bestand aangekomen. Dat is ook rechtstreeks te controleren:
systemctl show kh-demo.service -p Environment -p EnvironmentFiles
Drie valkuilen in zulke bestanden: commandosubstitutie met $(...) vindt niet plaats, een letterlijk bedoeld $ moet als $$ geschreven worden, en aanhalingstekens belanden in de waarde, waar ze niet thuishoren.
Unit not found
Failed to start kh-demo.service: Unit kh-demo.service not found. betekent bijna altijd een van drie dingen: het bestand staat in de verkeerde map, het heeft de verkeerde extensie (.services in plaats van .service) of de daemon-reload ontbreekt. Met ls -l /etc/systemd/system/ zijn de eerste twee gevallen meteen duidelijk.
Verschillen tussen Debian 13, Debian 12, Ubuntu 24.04 en 22.04
De basis, dus [Unit], [Service], [Install], Type=simple, Restart=, User= en WorkingDirectory=, is op alle vier de systemen identiek. De verschillen zitten in de randgevallen.
Beschikbare directives. Ubuntu 22.04 komt met systemd 249, Debian 12 met systemd 252, Ubuntu 24.04 met systemd 255 en Debian 13 met systemd 257. Alles vanaf 253 ontbreekt op de twee oudere systemen: Type=notify-reload (253) en verder RestartSteps=, RestartMaxDelaySec= en RestartMode=direct (254). Ze leveren geen foutmelding op, maar worden genegeerd. Zo ontstaan units die op het ene systeem doen wat u wilt en op het andere schijnbaar zonder reden iets anders.
systemctl --version
Netwerk. Ubuntu Server gebruikt netplan met systemd-networkd, Debian in de standaardinstallatie ifupdown. Daardoor zegt network-online.target op Ubuntu zonder verder toedoen iets, en op Debian pas na het activeren van ifupdown-wait-online.service.
Paden van interpreters en runtimes. Hier ontstaan de meeste 203/EXEC-fouten bij het overzetten van een unit tussen systemen. Node.js zit bij Debian 13 in versie 20.19, bij Debian 12 in 18.20, bij Ubuntu 24.04 in 18.19 en bij Ubuntu 22.04 in 12.22. PHP is 8.4 op Debian 13, 8.2 op Debian 12, 8.3 op Ubuntu 24.04 en 8.1 op Ubuntu 22.04. Bij Java is de sprong het grootst: Debian 13 levert alleen openjdk-21-jre-headless, Debian 12 alleen openjdk-17-jre-headless, terwijl Ubuntu 24.04 en 22.04 juist 8, 11, 17 en 21 aanbieden. Wie ExecStart=/usr/lib/jvm/java-17-openjdk-amd64/bin/java van Debian 12 naar Debian 13 kopieert, krijgt gegarandeerd 203/EXEC.
Databases. Debian levert geen mysql-server, daar draait altijd MariaDB. Een unit met After=mysql.service wacht op Debian dus op een dienst die niet bestaat en start zonder enige vertraging, stil en zonder waarschuwing. Juist is daar After=mariadb.service.
Wijzigen, terugdraaien, opruimen
Eigen units wijzigt u rechtstreeks in het bestand, gevolgd door een daemon-reload. Voor units die uit een pakket komen, gebruikt u drop-ins, zodat de volgende update uw aanpassing niet opruimt:
systemctl edit kh-demo.service
Dat maakt /etc/systemd/system/kh-demo.service.d/override.conf aan, waarin alleen de gewijzigde directives staan. Bijzonder geval: lijstdirectives zoals ExecStart= worden niet vervangen maar aangevuld. Wie ze wil overschrijven, moet de lijst eerst leegmaken:
ExecStart=
ExecStart=/opt/kh-demo/run.sh --verbose
systemctl revert kh-demo.service verwijdert alle drop-ins weer. Het voorbeeld volledig terugbouwen gaat in precies deze volgorde:
systemctl disable --now kh-demo.service
rm -f /etc/systemd/system/kh-demo.service
rm -rf /etc/systemd/system/kh-demo.service.d
systemctl daemon-reload
systemctl reset-failed
Let op bij de laatste stap: systemctl reset-failed zonder argument zet de fouttoestand van alle units terug, niet alleen die van de voorbeelddienst. Op een systeem waar nog andere diensten op failed staan, verdwijnen daarmee ook hun meldingen uit systemctl list-units --state=failed. Wie alleen hier wil opruimen, gebruikt de gerichte vorm systemctl reset-failed kh-demo.service uit de sectie hierboven.
Wie het bestand verwijdert zonder eerst disable aan te roepen, laat een dode symlink achter in /etc/systemd/system/multi-user.target.wants/, die bij elke daemon-reload als waarschuwing opduikt. Opruimen doet u die met find /etc/systemd/system -xtype l -delete. Die aanroep verwijdert echter elke dode symlink onder /etc/systemd/system, dus ook de resten van andere diensten die u misschien nog nodig heeft. Veiliger is eerst een blik zonder -delete, of meteen de gerichte variant find /etc/systemd/system -xtype l -name 'kh-demo*' -delete. Optioneel nog de gebruiker en de bestanden:
userdel khdemo
rm -rf /opt/kh-demo /etc/kh-demo.env
Een absoluut pad in ExecStart, het passende Type, een eigen gebruiker, een ingestelde WorkingDirectory en variabelen via EnvironmentFile=: wie dat op orde heeft, houdt een dienst over die een herstart overleeft en die bij een storing verklapt wat er aan de hand was.
Veelgestelde vragen
Moet ik na elke wijziging aan het unit-bestand systemctl daemon-reload uitvoeren?
Wat betekent status=203/EXEC?
Waarom start mijn dienst na een herstart niet, terwijl systemctl start wel werkt?
Type=simple of Type=forking?
Waarom vindt mijn dienst zijn omgevingsvariabelen niet?
De dienst reageert niet meer op systemctl start, wat nu?
2026 KernelHost GmbH. Alle rechten voorbehouden. Deze handleiding is auteursrechtelijk beschermd. Publicatie op andere websites, geheel, gedeeltelijk of in bewerkte vorm, is zonder onze schriftelijke toestemming niet toegestaan. Citeren met bronvermelding en link is uitdrukkelijk welkom.

