Run programs automatically at server startup (systemd and cron)
How applications, game servers and scripts start by themselves after every reboot: with a systemd service unit as today's standard, or alternatively through a cron entry with @reboot.
Bringing every service back up by hand after each server reboot costs time and eventually goes wrong. Linux ships with its own mechanism for exactly that. In this guide we show you how to run your own applications, game servers, bots or scripts automatically at server startup.
We cover two approaches: a systemd service unit as the solution used today, and the older route through a cron entry with @reboot. Both work independently of the distribution, so on Debian, Ubuntu, AlmaLinux and Rocky Linux alike.
systemd or cron: which approach fits?
systemd is the standard for services on every current Linux distribution. A cron entry with @reboot does start your application as well, but it cannot do anything beyond that.
| Feature | systemd unit | cron with @reboot |
|---|---|---|
| Starts at boot | yes | yes |
| Restart after a crash | yes, automatically | no |
| Check the status | yes, via systemctl | no |
| Log of the output | yes, in the journal | only if you redirect it yourself |
| Start order can be controlled | yes, after the network for example | only roughly, via sleep |
For anything that is meant to run permanently, the systemd unit is the better choice. The cron entry stays a quick stopgap for simple one-off commands.
Requirements
- Root access to your VPS or root server over SSH.
- A start script or program that already runs cleanly from the command line. Test that by hand first, before you automate it.
- The absolute path to that script. Relative paths do not work reliably during system startup.
Creating a systemd service unit
Your own units live in /etc/systemd/system/ and end in .service. Create the file:
nano /etc/systemd/system/meinedienst.service
A simple skeleton that works right away looks like this:
[Unit]
Description=Mein eigener Dienst
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=meinbenutzer
WorkingDirectory=/opt/meinedienst
ExecStart=/bin/bash /opt/meinedienst/start.sh
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
What the individual lines mean
- Description: The plain text name that
systemctl statusdisplays. - After and Wants: The service only starts once the network is available. That removes the need for the
sleeptrick you know from cron. - Type=simple: The program keeps running in the foreground. If your start script forks into the background by itself, you need
Type=forkingand aPIDFileentry instead. - User: The user the service runs as. Never let applications run as root without a reason.
- WorkingDirectory: Replaces the
cdfrom a start script. - Restart=on-failure: systemd restarts the service after a crash. With
Restart=alwaysit does so even when the program exits normally. - WantedBy=multi-user.target: Makes sure the service comes up with the regular system startup.
Enabling and testing the service
After every change to a unit file, systemd has to read it in again. Then you enable the service for system startup and start it right away:
systemctl daemon-reload
systemctl enable --now meinedienst.service
To see whether everything is running:
systemctl status meinedienst.service
The output of the application ends up in the journal. The following command lets you follow it live, which is the fastest way to track down a problem:
journalctl -u meinedienst.service -f
To stop it, restart it or disable it permanently:
systemctl stop meinedienst.service
systemctl restart meinedienst.service
systemctl disable meinedienst.service
Example: starting a TeamSpeak 3 server automatically
The start script shipped with TeamSpeak sends itself into the background and creates a PID file. That is why Type=forking is the correct setting here:
[Unit]
Description=TeamSpeak 3 Server
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
User=teamspeak
WorkingDirectory=/home/teamspeak
ExecStart=/home/teamspeak/ts3server_startscript.sh start
ExecStop=/home/teamspeak/ts3server_startscript.sh stop
PIDFile=/home/teamspeak/ts3server.pid
Restart=on-failure
RestartSec=15
[Install]
WantedBy=multi-user.target
Adjust the user name and the directory to match your installation. What matters is that the user you specify really owns the directory.
Example: game server or bot via a start script
For Minecraft, ARK, Arma 3, LinuxGSM servers or a bot you wrote yourself, the simple variant is usually enough. The service runs in the foreground, systemd takes care of monitoring and restarts:
[Unit]
Description=Gameserver
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=gameserver
WorkingDirectory=/home/gameserver/server
ExecStart=/bin/bash /home/gameserver/server/start.sh
Restart=always
RestartSec=15
[Install]
WantedBy=multi-user.target
If several services start at the same time and slow each other down, spread the start out with different values for RestartSec, or extend After= with the name of the unit that has to run first.
Alternative: autostart through cron with @reboot
If you only want to run a single command at boot and do not need any monitoring, a cron entry is enough. First set the editor, then open the crontab:
export VISUAL=nano; crontab -e
Add a line there with @reboot:
@reboot sleep 60 && cd /<PATH-TO-SCRIPT>/ && bash <YOUR-SCRIPT>.sh
The @reboot makes sure the command is executed once at every system startup. The sleep 60 part waits 60 seconds, so that the network and the database are ready beforehand. The command then changes into the directory of your script and starts it.
An example for a voice server that is supposed to run under its own user:
@reboot sleep 45 && cd /home/<TEAMSPEAK-DIRECTORY>/ && sudo -u <TEAMSPEAK-USER> bash ts3server_startscript.sh start
If you add several lines, give each one a different sleep value (at least 45), so that not all services start at the same moment. The value is given in seconds.
Common mistakes
- The service does not start although it works by hand: Usually an absolute path is missing. Inside a unit and in cron, the PATH environment variable is set very sparsely, so always write
/bin/bash /voller/pfad/skript.sh. - "Unit not found" right after creating it: You forgot
systemctl daemon-reload. - The service exits again immediately: The program forks into the background. Use
Type=forkingwith a matchingPIDFile, or start the program in foreground mode. - Permission errors at startup: The user given in
User=is not allowed to access the directory. Check the owner withls -la. - Nothing is running after the boot: The service was started but never enabled.
systemctl enableis the step that registers it permanently.
And finally the best test of all: reboot the server once with reboot and then check with systemctl status whether all services are running as expected.
Frequently asked questions
Which is better: systemd or cron with @reboot?
Where do I put my own systemd unit?
My service runs by hand but does not start at boot. Why?
How do I see the output of my service?
Do I still need screen or tmux for a game server?
2024-2026 KernelHost GmbH. All rights reserved. This guide is protected by copyright. Republishing it on other websites, in whole, in part or in edited form, is not permitted without our written consent. Quoting with a source credit and a link is expressly welcome.

