Write a systemd service unit for your own app
Turn any long-running program into a proper service: create a unit file, run it as an unprivileged user, restart it on failure and start it at boot.
Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?
If you start an app with nohup ./myapp & or inside screen, it will not come back after a reboot or a crash, and its output goes nowhere useful. A small systemd unit file fixes all three problems. This guide uses an app at /opt/myapp/bin/myapp that listens on port 8080; replace names and paths with your own.
1. Create a user for the app
Services should not run as root unless they really need to. Create a system account with no login shell:
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
# Debian/Ubuntu and RHEL/Rocky/Alma
sudo useradd --system --home-dir /opt/myapp --shell /usr/sbin/nologin appuser
sudo chown -R appuser:appuser /opt/myapp
2. Write the unit file
Your own units go in /etc/systemd/system/. The file name becomes the service name.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My example app
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
EnvironmentFile=-/etc/myapp/myapp.env
ExecStart=/opt/myapp/bin/myapp --port 8080
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
What each line does:
After=/Wants=network-online.target: start after the network is configured, useful when the app connects to a database on another host at startup.Type=exec: the program stays in the foreground and is the service. The default when onlyExecStart=is set issimple; the systemd manual recommendsexecfor long-running services because it reports a failure if the binary cannot be started at all. Only useforkingfor old daemons that put themselves into the background.User=/Group=: run as the account from step 1.EnvironmentFile=: readKEY=valuelines from a file. The leading-makes the file optional. Keep secrets here withchmod 600, not in the unit file.ExecStart=: the full path to the program and its arguments. systemd does not run a shell, so pipes and&&do not work here.Restart=on-failurewithRestartSec=5: restart after a crash or non-zero exit, waiting 5 seconds. The default isRestart=no.WantedBy=multi-user.target: whatsystemctl enablehooks into so the app starts at boot.
3. Load, start and enable it
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo systemd-analyze verify /etc/systemd/system/myapp.service # catches typos
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
systemctl status myapp
If the status shows active (running), you are done. If it shows failed, read the logs:
journalctl -u myapp -n 50 --no-pager
Common first-run problems: a wrong path in ExecStart= (use an absolute path), the app user cannot read its config or write its data directory, or the app forks into the background by itself (then it needs a foreground flag, or Type=forking).
4. Test the restart behavior
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo systemctl kill --signal=SIGKILL myapp
sleep 6
systemctl status myapp # should be running again with a new PID
5. Optional: sandbox the service
systemd can restrict what the service may touch. Add these lines to [Service] one at a time and test after each, because a too-strict setting shows up as “permission denied” errors in the app:
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
StateDirectory=myapp
ReadWritePaths=/opt/myapp/data
ProtectSystem=strictmakes the whole filesystem read-only for the service, except/dev,/procand/sys;ReadWritePaths=re-opens the paths it must write.StateDirectory=myappcreates/var/lib/myappowned by the service user.ProtectHome=yeshides/home,/rootand/run/user.
systemd-analyze security myapp rates the unit’s sandboxing and lists settings you could still add.
Changing it later
After editing the unit file, always run sudo systemctl daemon-reload and then sudo systemctl restart myapp. To adjust a unit that came from a package instead, use sudo systemctl edit name, which writes only your changes to a drop-in file.
Official documentation
Did this work for you?
No votes yet. Be the first.
If it did not work, the report form below tells the editors what went wrong.
See also
- 0164 systemd services: systemctl and journalctl basics systemd starts and supervises services on most Linux servers. systemctl starts, stops and enables them; journalctl shows their logs. Beginner · Explains an idea · Linux Article Services & systemd 15 Oct 2026
- 0166 Replace a cron job with a systemd timer A systemd timer runs a service on a schedule like cron, but logs every run to the journal, can catch up missed… Intermediate · Step by step · Linux Article Services & systemd 15 Oct 2026
- 0167 Fix: Job for nginx.service failed because the control process exited with error code systemctl prints this when a service's start command exits with an error. The real reason is in journalctl; for nginx it is… Intermediate · Fixes an error · Linux Article Networking & firewalls · Services & systemd 15 Oct 2026
- 0168 Server health check script: disk, memory and failed units A dependency-free bash script that checks disk and inode usage, available memory, load, failed systemd units and pending reboots, and exits non-zero… Intermediate · Cheat sheet · Linux Script Disks, LVM & filesystems · Services & systemd 15 Oct 2026
- 0255 Build a monitoring stack with Prometheus, node_exporter and Grafana on one Linux server Install Prometheus and node_exporter as systemd services, scrape and query host metrics with PromQL, then add Grafana with a dashboard and an… Intermediate · Guided project · Linux Article Prometheus & Grafana · Services & systemd 15 Oct 2026
- 0265 Turn a one-liner into a robust bash script: strict mode, options, logging, traps, locking and a schedule Grow a find-and-gzip one-liner step by step into a dependable bash script with strict mode, getopts, usage text, logging, error traps, dry-run,… Intermediate · Guided project · Linux, macOS Article Services & systemd · Shell scripting 15 Oct 2026
Repos in the wild
- systemd: the Linux system and service manager systemd/systemd The source of systemd, the init system and service manager on most Linux distributions, together with journald, networkd, resolved, timers and the systemctl tool. GitHub
History
Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.
- Dominik +3,955 chars