Guides

How to Keep Any App Running 24/7 with systemd

Turn any script or application into a proper Linux service that starts at boot, restarts after a crash, and survives you closing your SSH session.

Virtualized Team·August 22, 2026·4 min read
How to Keep Any App Running 24/7 with systemd

You start your bot, it works, you close your terminal, and it dies. Or it runs for three days, hits an error, and stays dead until you notice.

systemd fixes both. It is already installed on Ubuntu, Debian, AlmaLinux, and Rocky, and a service file is about ten lines.

Before you start

You need SSH access, a user with sudo, and an application that already runs correctly when you launch it by hand. Get it working in the foreground first, since systemd will not fix a broken app. It will just restart it in a loop.

Note the two things you will need: the full path to the command that starts it, and the directory it needs to run from.

Step 1: Find the full path to your command

systemd does not use your shell's PATH, so node or python3 on their own will not work. Get the absolute path:

which node

That returns something like /usr/bin/node. Do the same for whatever runtime your app uses.

Step 2: Write the service file

sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My application
After=network-online.target

[Service]
Type=simple
User=youruser
WorkingDirectory=/home/youruser/myapp
ExecStart=/usr/bin/node index.js
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

What each line is doing:

  • After=network-online.target: do not start until the network is actually up. Skipping this is why services sometimes fail on boot but start fine by hand.
  • User: run as your normal account, not root. If the app is compromised, this is the difference between a bad day and a rebuild.
  • WorkingDirectory: the folder the app expects to be in. Relative paths inside your code resolve from here.
  • ExecStart: the full path to the command, with its arguments.
  • Restart=always: bring it back whenever it stops, whether it crashed or exited cleanly.
  • RestartSec=10: wait ten seconds between attempts, so a broken app does not spin the CPU restarting hundreds of times a minute.
  • WantedBy=multi-user.target: this is what makes it start at boot.

Step 3: Load and start it

Whenever you add or edit a service file, systemd needs to re-read it:

sudo systemctl daemon-reload

Then enable and start in one command:

sudo systemctl enable --now myapp

enable registers it to start at boot. --now also starts it immediately. Forgetting enable is the single most common mistake here: the app runs perfectly until the first reboot, then quietly does not come back.

Step 4: Check it is running

sudo systemctl status myapp

You want to see active (running) in green. The last few log lines appear underneath, which is usually enough to spot a problem straight away.

Step 5: Read the logs

systemd captures anything your app prints to standard output, so you do not need to set up log files yourself.

Follow the logs live:

sudo journalctl -u myapp -f

See the last hundred lines:

sudo journalctl -u myapp -n 100

See only what happened since the last boot:

sudo journalctl -u myapp -b

The commands you will use

  • sudo systemctl restart myapp: after changing your code
  • sudo systemctl stop myapp: stop it, and keep it stopped
  • sudo systemctl disable myapp: stop it coming back at boot
  • sudo systemctl daemon-reload: after every edit to the service file

That last one catches people out. Editing the .service file and running restart alone will restart the app with the old configuration. Always daemon-reload first.

If it will not start

Run sudo systemctl status myapp and read the error. Nearly all of them are one of these:

status=203/EXEC means systemd could not run the command. The path in ExecStart is wrong, or the file is not executable. Check with which, and use absolute paths for everything.

status=200/CHDIR means the WorkingDirectory does not exist, or your user cannot read it. Check for typos in the path.

It starts, then stops, then starts again in a loop. The app itself is crashing. journalctl -u myapp -n 50 shows why. Usually a missing environment variable, a config file it cannot find, or a port already in use.

It works when you run it manually but not as a service. Almost always environment. Your shell has variables and a PATH that systemd does not. Pass anything the app needs explicitly:

Environment="NODE_ENV=production"
Environment="API_KEY=your-key-here"

For anything secret, use a file instead so the value is not readable in systemctl status:

EnvironmentFile=/home/youruser/myapp/.env

Then chmod 600 that file so only your user can read it.

Why not screen, tmux, or pm2

They work, and plenty of people use them. But a screen session dies with the server, so nothing comes back after a reboot unless you remember to reconnect and start it. pm2 solves that but adds a process manager on top of the one your operating system already ships.

systemd starts at boot, restarts on failure, collects logs, and is already there. For anything that needs to stay up unattended, it is the right tool.