SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor · Updated 2026-08-29

How to run program as systemd service for VPS

Make your program run forever on your VPS. Learn how to write a .service file, start am on boot, and check logs with journalctl. We go show you how to secure am properly too.

Wetin be systemd service, and why you need am

Systemd service na small text file wey dey tell your server how to run program: make e start when server boot, restart am if e crash, and send wetin e dey output go system log. Na dat one be the full work. Program wey you start by hand inside SSH session go die the moment you log out or the server reboot. Program wey you wrap inside systemd service go continue to run, because na the server itself dey own am, no be your shell.

systemd na the init system for Ubuntu, Debian, Fedora, and most modern Linux servers. E be the first process wey dey start and the one wey dey supervise everything else. Dat one no always be true, and how systemd replace the init scripts wey come before am dey worth to read once you know wetin unit file dey do. When you write service file, you dey hand your program give dat supervisor. Dis guide go show the smallest unit wey dey work, the three sections wey every unit get, how to turn am on and read im logs, how to run am on schedule with timer, and how to lock am down so e go run with small privilege as possible.

Di smallest service wey dey work

Service file dey live inside /etc/systemd/system/, e dey end with .service, and e just need small 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

Dat one na complete, working unit. ExecStart na di command wey you go run. WantedBy=multi-user.target mean say make di service start once di server reach normal multi-user operation, na dat one dey make am start wen you boot di system. Every oda tin na just refinement.

Di three sections, and wetin each one dey do

Every unit file get sections wey dey inside square brackets. One service dey use three.

[Unit] dey explain di service and how e take relate with oda things. Di two lines wey you go use pass:

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

Description na di human label wey you go see for systemctl status. After=network-online.target dey tell systemd make e no start your program until network don ready, dis one dey important for anytin wey dey bind port or connect go outside.

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

[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 di program as user wey no get special privilege instead of root, and dis one na di most important line for safety. No Type= line dey here, so systemd go just use simple and assume say di ExecStart process go stay for foreground; if di program dey fork go background, you need di correct Type= for how e take start, if not, di unit go show say e active even though di real daemon don disappear. Restart=on-failure and RestartSec=5 get dia own section for down, because na dem be di reason why most pipo dey write service.

[Install] na wetin dey happen when you enable di service:

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target na wetin dey link di service make e start wit system boot when you run systemctl enable. If [Install] section no dey, you fit start di service wit your hand, but e no go start by itself after you reboot di system.

Turn am on and watch am

After you write or edit any unit file, reload systemd make e read the change, then enable and start the service for one time:

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

daemon-reload na the step wey people dey forget: systemd dey cache unit files, so if you edit am, e no go work until you reload. enable --now go enable the service for boot and start am sharp-sharp. 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 want see. To read wetin the program dey output, ask the journal for just this unit:

sudo journalctl -u myapp.service -f

The -f dey follow new lines as dem dey show, just like tail -f. Anything wey your program write go standard output or standard error go land here, you no need do any extra logging setup.

Restart wen e fail, di reason why you dey here

Di main benefit of service na say systemd go restart your program wen e die. Na two lines dey do am:

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure go restart di program if e exit wit non-zero code or if e die sake of crash signal like SIGKILL or SIGSEGV. If di program exit well, or if you stop am wit SIGTERM, SIGINT, SIGHUP, or SIGPIPE, e no go restart. RestartSec=5 go wait five seconds between each try, so if di program crash quick-quick, e no go just dey spin for one tight loop. Confirm am by killing di process and watch as systemd bring am back. Use SIGKILL: because di default SIGTERM na clean stop, so on-failure no go restart di service:

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

Within five seconds, di status go show new Main PID and active (running) again. Na dat one be di whole feature, and na why service better pass make you just leave program dey run for tmux or screen.

Run am as user wey no get special power, and make am strong

If program get flaw and e dey run as root, attacker fit do anything for your server. Make you run am as im own user, and give systemd some directives wey go lock am inside. First, create system account wey no fit login and no get home directory:

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

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

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

Every line dey remove wetin the program no need. NoNewPrivileges=true go stop the process make e no fit gain new privileges, even if e try use setuid binary. PrivateTmp=true go give am private /tmp wey no other process fit see. ProtectSystem=strict go make the whole filesystem read-only, except for some paths wey you go name with ReadWritePaths=. ProtectHome=true go hide /home from am completely. This na the same "least-privilege" logic as when you put service behind firewall: give am only wetin e need. If you don read the guide about how to close IPv6 firewall gap for VPS, this one na the part wey you go do for inside the host. For service wey dey face internet, join this hardening with Fail2ban for front of SSH and default-deny firewall.

Instead of make you dey type all this one by hand and come forget directive, generate complete, hardened unit and copy am out:

Toolsystemd service and timer generator

Timers: di modern way to do cron

Systemd timer dey run service base on schedule, and e be di modern replacement for cron job. Timer get two files: one .service wey dey do di work, and one .timer wey dey talk when e go run. Make we say you want backup by 3am every day. Di service go do di task one time then e go stop:

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

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

Type=oneshot dey tell systemd say di program go run, finish, and close, instead of make e stay open for background. Di timer dey 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 mean 3am every day. You fit test any calendar expression wit systemd-analyze calendar "*-*-* 03:00:00", e go confirm say di syntax correct and e go show you di next time wey e go run. Persistent=true go run any job wey you miss as soon as di server come back up if di server bin off by 3am, someting wey cron no fit do. No forget say you dey enable timer wit timers.target, no be multi-user.target. Na di timer you go enable, no be di service:

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

list-timers go show all di timers wit dia next and last run time, so you fit see quick-quick when your job go run next. Di generator wey dey up so dey build di pair .service and .timer for you when you turn on timer mode. If you compare am wit cron line, timer dey give you real logs inside journal, di same hardening directives like any oda service, and di catch-up feature wey Persistent=true dey provide for jobs wey you miss. Cron still dey okay for simple jobs; but timer na better tool once di job dey important.

FAQ

Wetin be the difference between systemd service and cron job?

One service dey keep program wey dey run for long time alive: e dey start when system boot, e dey restart if e fail, and e dey log everything go journal. Cron job dey run short command base on schedule then e go close. If you want scheduling but you still want journal logs, hardening, and make e run missed tasks, use systemd timer. E dey pair .timer schedule with oneshot service and e dey replace cron for most server tasks.

Where I suppose put my systemd service file?

Put your own units inside /etc/systemd/system/, make the name end with .service. That directory na for units wey administrator add, and e get priority pass units wey packages dey bring inside /lib/systemd/system/. After you create or edit file for there, run sudo systemctl daemon-reload so systemd go see the new changes.

How I go make service restart if e crash?

Add Restart=on-failure and RestartSec=5 go the [Service] section, then run sudo systemctl daemon-reload and restart the service. systemd go relaunch the program if e exit with non-zero code or if e die from crash signal, e go wait five seconds between each try. Test am with sudo systemctl kill -s SIGKILL myapp.service, SIGTERM, the default signal, na clean stop so e no go trigger on-failure, and watch systemctl status make e 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, then add User=myapp go the [Service] section. Add NoNewPrivileges=true, PrivateTmp=true, and ProtectSystem=strict so the process go run with only the access wey e need. To run as unprivileged user na the most important change wey you fit do to make your service safe.

Why my service no gree start?

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