systemd · linux · devops
systemd: A Pocket Companion
Run your app as a real service — started on boot, restarted on crash, with logs you can actually read.
A process you start by hand dies when you log out. systemd is how Linux runs things properly: as services that start on boot, restart if they crash, and log to one place you can query. This guide turns a plain script into a managed service, then replaces a cron job with a timer — the ops layer beneath everything you deploy.
$ sudo systemctl status nginx # is it running? when did it start? recent logs?
The model: units
systemd manages units — mostly .service units (long-running processes) and .timer
units (scheduled triggers). You control them with systemctl and read their logs with
journalctl.
$ systemctl status <name> # state + recent log lines
$ systemctl start <name> # start now
$ systemctl stop <name> # stop now
$ systemctl restart <name> # stop + start
$ systemctl enable <name> # start automatically on boot
$ systemctl disable <name> # don't start on boot
$ systemctl enable --now <name> # enable *and* start in one go
enable ≠ start
start runs it right now; enable makes it run on every boot. They’re independent —
you usually want both, which is what enable --now does.
Turning a script into a service
Say you have an app started by /opt/myapp/run.sh. Create
/etc/systemd/system/myapp.service:
[Unit]
Description=My App
After=network.target
[Service]
ExecStart=/opt/myapp/run.sh
WorkingDirectory=/opt/myapp
User=appuser
Restart=on-failure
RestartSec=3
Environment=PORT=8000
[Install]
WantedBy=multi-user.target
Three sections: [Unit] is metadata and ordering (After= says start us once the
network is up), [Service] is how to run it, and [Install] is when to enable it.
$ sudo systemctl daemon-reload # tell systemd to re-read unit files
$ sudo systemctl enable --now myapp # start it + run on boot
$ systemctl status myapp # confirm it's active
Edit a unit? daemon-reload first
systemd caches unit files. After creating or editing one, run
sudo systemctl daemon-reload before restart, or your changes won’t take effect.
Restart policy: surviving crashes
The line that earns systemd its keep:
Restart=on-failure # restart only on non-zero exit / signal
RestartSec=3 # wait 3s between restarts
Restart=always restarts even on clean exits; on-failure restarts only on crashes —
usually what you want for a web app. systemd also backs off if a service crash-loops, so a
broken deploy doesn’t hammer the box.
Reading logs: journalctl
All service output goes to the journal — no more hunting through /var/log:
$ journalctl -u myapp # all logs for this unit
$ journalctl -u myapp -f # follow (tail -f)
$ journalctl -u myapp -n 100 # last 100 lines
$ journalctl -u myapp --since "10 min ago"
$ journalctl -u myapp -p err # only errors and worse
$ journalctl -b # logs since the last boot
journalctl -u myapp -f is the one you’ll live in while debugging a deploy.
Timers: a better cron
systemd timers replace cron jobs — with logs, dependencies, and the ability to catch up on
missed runs. Pair a .service (what to do) with a .timer (when):
/etc/systemd/system/backup.service:
[Service]
Type=oneshot
ExecStart=/opt/backup.sh
/etc/systemd/system/backup.timer:
[Timer]
OnCalendar=*-*-* 03:00:00 # every day at 3am
Persistent=true # run on next boot if a scheduled run was missed
[Install]
WantedBy=timers.target
$ sudo systemctl enable --now backup.timer
$ systemctl list-timers # what's scheduled, and when it next fires
Persistent=true is the win over cron: if the machine was off at 3am, the job runs once
it’s back — cron would just skip it.
When it breaks
- Service won’t start?
systemctl status myappshows the exit code;journalctl -u myapp -n 50shows why. - Edited the unit, nothing changed?
daemon-reload, thenrestart. - Crash-looping? Check the logs, then
systemctl reset-failed myappafter you fix it. - Wrong working dir / user? Those are common silent failures — set
WorkingDirectoryandUserexplicitly.
Pocket cheat-sheet
| Do | Command |
|---|---|
| Status + recent logs | systemctl status x |
| Start / stop / restart | systemctl start\|stop\|restart x |
| Enable on boot (+ start) | systemctl enable --now x |
| Re-read unit files | systemctl daemon-reload |
| Follow logs | journalctl -u x -f |
| Errors only / since boot | journalctl -u x -p err / journalctl -b |
| List scheduled timers | systemctl list-timers |
Wrap any long-running process in a unit file and it becomes something you can trust:
started on boot, restarted on failure, logged in one place. man systemd.service and
man systemd.timer are the full reference.
Get new field notes by email
Occasional, practical write-ups on building and shipping software. No spam — unsubscribe anytime.