systemd Basics: Services, Units, and Timers
A practical introduction to systemd: manage services with systemctl, write a unit file, read journal logs, and replace cron with a timer.
On this page
On most modern Linux distributions, systemd is the first process the kernel starts, PID 1. It brings up the system, supervises services, collects their logs, and schedules recurring jobs. You do not need to master all of it, but a working knowledge pays off the first time you deploy something that must stay running.
Units in one minute
systemd manages resources described by unit files. The type is the file extension:
.serviceruns a daemon or a one-shot command..timertriggers another unit on a schedule..socket,.mount, and.targetcover sockets, filesystems, and groups of units.
Your own system-wide units belong in /etc/systemd/system/, while packages install theirs under /usr/lib/systemd/system/.
systemctl essentials
systemctl status nginx # state, recent logs, main PIDsudo systemctl start nginxsudo systemctl stop nginxsudo systemctl restart nginxsudo systemctl enable nginx # start at bootsudo systemctl enable --now nginx # enable and start in one gosudo systemctl disable nginxsudo systemctl daemon-reload # re-read unit files after editingRemember the difference: start affects now, enable affects boot. After editing any unit file, run daemon-reload or systemd keeps using the old version.
The status output is worth reading closely. It shows whether the unit is active, when it started, its main PID, and the last few log lines, which is often enough to diagnose a failure without opening anything else.
For services that belong to your user rather than the system, add --user. Units go in ~/.config/systemd/user/, and no sudo is needed:
systemctl --user status notes-backup.serviceWriting a small service
Say I have a script that backs up my notes directory. It runs, finishes, and exits, so a one-shot service is enough. The [Unit] section describes the service and its ordering, and [Service] says what to execute and as whom:
[Unit]Description=Back up notes to the archive diskAfter=network-online.target
[Service]Type=oneshotUser=perdiExecStart=/usr/local/bin/notes-backup.shFor a long-running process, you will want it to come back after a crash. Here is the edit:
[Service]ExecStart=/usr/local/bin/notes-serverRestart=on-failureRestartSec=5on-failure restarts the process only after a non-zero exit or a crash, not when you stop it deliberately, and RestartSec avoids a tight restart loop. Save, run sudo systemctl daemon-reload, then restart the service. Make sure the script is executable; the permissions article on Linux file permissions explains why that matters.
Reading logs with journalctl
systemd captures stdout and stderr from services into the journal, so you rarely need a custom log file or a log rotation setup. Every line is stored with a timestamp and the unit that produced it, which makes filtering simple.
journalctl -u notes-backup.service # everything for the unitjournalctl -u notes-backup.service -f # follow livejournalctl -u notes-backup.service --since "1 hour ago"journalctl -u nginx --since today -p err # only errorsjournalctl -b # current bootReplacing cron with a timer
A timer is a second unit that activates the service with the same base name. Create it next to the service:
[Unit]Description=Run notes backup daily
[Timer]OnCalendar=dailyPersistent=true
[Install]WantedBy=timers.targetPersistent=true is the feature I miss most in cron: if the machine was off at the scheduled time, the job runs at the next boot. Enable the timer, not the service:
sudo systemctl daemon-reloadsudo systemctl enable --now notes-backup.timersystemctl list-timers --all # next and last run timessystemd-analyze calendar "Mon *-*-* 03:00" # validate a scheduleCompared with cron, you gain logging through the journal, dependency ordering, and a clear status view. The cost is two files instead of one line. The pairing works by name: notes-backup.timer starts notes-backup.service automatically, so you never enable the service itself.
What to do next
Pick one cron job or nohup command you currently rely on and convert it to a unit. Start with --user units so mistakes stay in your home directory, and check the result with systemctl status and journalctl -u. Once that works, add Restart= and resource limits as needed.