SSD Nodes Learn
How to do am Matt ConnorBy Matt Connor · Updated 2026-07-24

how to run program as systemd service for VPS

Learn how to create a systemd service file for your VPS. We go show you how to make your program start on boot, restart if e crash, and use system timers.

Wetin systemd service be, and why you need am

systemd service na small text file wey dey tell your server how to run program: how to start am when server boot, how to restart am if e crash, and how to send its output go system log. Na that one be the work. If you start program manually for SSH session, e go die as soon as you log out or server reboot. But if you wrap program inside systemd service, e go keep running, because server itself dey control am instead of your shell.

systemd na the init system for Ubuntu, Debian, Fedora, and most modern Linux servers. Na him be the first process wey dey start and na him dey supervise everything else. When you write service file, you dey hand your program give that supervisor. This guide go show you the smallest unit wey dey work, the three sections wey every unit get, how to turn am on and how to read its logs, how to run am for schedule with timer, and how to lock am down so e go run with small privilege as possible.

The smallest service wey dey work

Service file dey live for /etc/systemd/system/, e dey end with .service, and e no need many lines. Create one for program wey dey /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.target

Na complete, working unit be that. ExecStart na the command to run. WantedBy=multi-user.target mean say make e start once server reach normal multi-user operation, na wetin make e start when boot dey happen. Everything else na just to fine-tune am.

The three sections, and wetin each one dey do

Every unit file dey divide into sections inside square brackets. Service dey use three.

[Unit] dey describe the service and how e dey connect with other things. The two lines wey you go use pass na:

[Unit]
Description=My application
After=network-online.target
Wants=network-online.target

Description na the human label wey you go see for systemctl status. After=network-online.target dey tell systemd say make e no start your program until network don ready, wetin important for anything wey dey bind port or dey make outbound connection.

[Service] na how the program dey run. Na here most of your settings dey go:

[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.toml
WorkingDirectory=/opt/myapp
User=myapp
Restart=on-failure
RestartSec=5
Environment=LOG_LEVEL=info

User=myapp dey run the program as unprivileged account instead of root, and na the most important line for safety. Restart=on-failure and RestartSec=5 get their own section below, because na dem be reason why most people dey write service at all.

[Install] na wetin go happen when you enable the service:

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target na wetin dey link the service to boot when you run systemctl enable. If you no get [Install] section, you fit start service with hand, but e no go start by itself after reboot.

Turn am on and watch am

After you don write or edit any unit file, reload systemd so e go see the change, then enable and start the service for one single step:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

daemon-reload na the step many people dey forget: systemd dey cache unit files, so edit no go do anything until you reload. enable --now dey enable the service for boot and start am immediately. Check am:

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) and enabled na wetin you dey find. To read wetin the program dey output, ask the journal for only this unit:

sudo journalctl -u myapp.service -f

The -f dey follow new lines as dem dey come, just like tail -f. Anything your program write to standard output or standard error go land here, without you need to set up any logging.

Restart on failure, the reason you are here

The main benefit of service na say systemd go restart your program when e die. Two lines dey do am:

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure go restart the program when e exit with non-zero code or when crash signal like SIGKILL or SIGSEGV kill am. If e exit clean, or if SIGTERM, SIGINT, SIGHUP, or SIGPIPE stop am, e no go trigger restart. RestartSec=5 go wait five seconds between tries, so program wey dey crash instantly no go enter tight loop. Confirm am by kill the process and watch how systemd go bring am back. Use SIGKILL: because default SIGTERM count as clean stop, on-failure no go restart the service:

sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.service

Within five seconds, the status go show new Main PID and active (running) again. Na that one be the whole feature, and na why service better than to just leave program running for tmux or screen.

Run am as unprivileged user, and harden am

If service dey run as root, e fit do anything to your server if hacker exploit the program. Run am as its own user, and give systemd some directives wey go fence am. First, create system account wey no get login and no get home:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp

Then set User=myapp and add hardening lines to [Service]:

[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

Every line dey remove something wey the program no need. NoNewPrivileges=true stop the process from getting new privileges, even through setuid binary. PrivateTmp=true give am private /tmp wey no other process fit see. ProtectSystem=strict make the whole filesystem read-only except for some paths wey you name with ReadWritePaths=. ProtectHome=true hide /home from am completely. This na the same least-privilege logic as putting service behind firewall: give am only wetin e need. If you don read the guide on how to close IPv6 firewall gap on a VPS, this na the on-host part of that same idea. For service wey dey face internet, use this hardening together with Fail2ban for SSH and default-deny firewall.

Instead of to type all this manually and mistake one directive, generate complete, hardened unit and copy am out:

Toolsystemd service and timer generator

Timers: the modern cron

systemd timer dey run service for schedule, and na him be the modern replacement for cron job. Timer na two files: .service wey dey do the work, and .timer wey dey talk when e go happen. Say you want backup for 3am every day. Service go do the task once and exit:

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

Type=oneshot dey tell systemd say program run, finish, and done, instead of to stay resident. Timer go schedule am:

# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

OnCalendar=*-*-* 03:00:00 means 3am every day. You fit test any calendar expression with systemd-analyze calendar "*-*-* 03:00:00", wey go confirm if e parse well and print the next times e go fire. Persistent=true dey run missed job as soon as server come back if e no dey at 3am, something cron no fit do. Notice say timer dey enable through timers.target, no be multi-user.target. Enable the timer, no be the service:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers

list-timers dey show every timer with its next and last run, so you fit see at a glance when your job go fire next. The generator wey dey above dey build the paired .service and .timer for you when you turn on timer mode. Compared to cron line, timer dey give you real logs for journal, the same hardening directives as any service, and the missed-run catch-up wey Persistent=true dey provide. Cron still dey okay for simple job; timer na better tool once the job important.

FAQ

Wetin be difference between systemd service and cron job?

Service dey keep program dey run for long: e dey start when computer boot, e dey restart if e fail, and e dey log everything for journal. Cron job dey run short command for certain time and den e dey exit. If you want scheduling but you also want journal logs, hardening, and make e catch up for missed runs, use systemd timer. E dey pair .timer schedule with oneshot service and e dey replace cron for most server tasks.

Where I go put my systemd service file?

Put your own units for /etc/systemd/system/, and make the name end with .service. That directory na for units wey administrator add, and e get priority over units wey packages carry inside /lib/systemd/system/. After you create or edit file for there, run sudo systemctl daemon-reload make systemd pick the change.

How I go make service restart if e crash?

Add Restart=on-failure and RestartSec=5 for [Service] section, den run sudo systemctl daemon-reload and restart the service. systemd go relaunch the program when e exit with non-zero code or when e die from crash signal, e dey wait five seconds between tries. Test am with sudo systemctl kill -s SIGKILL myapp.service — SIGTERM, wey na default signal, e dey count as clean stop and e no dey trigger on-failure — and watch systemctl status show new PID within few seconds.

How I go run systemd service as non-root user?

Create system account with sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp, den add User=myapp for [Service] section. Add NoNewPrivileges=true, PrivateTmp=true, and ProtectSystem=strict make the process run with small access wey e need. Running as unprivileged user na the most important single change wey you fit make for service safety.

Why my service fail to start?

Run systemctl status myapp.service for summary and journalctl -u myapp.service for full output. Most common reasons na wrong path for ExecStart, missing WorkingDirectory, permission error because User= no fit read file, or when person forget sudo systemctl daemon-reload after edit. The journal go show the program error message, and e dey usually name the problem directly.