At the end of the package management post I said we finally had a machine we could navigate, control and keep stocked with software — but that we had not yet made it actually do anything on its own. Time to fix that. There is a world of difference between running a program and running a service, and the gap between those two is the whole reason this post exists.
Run a program the obvious way — type its name, press enter — and it is tethered to you. It runs while you are logged in, in that terminal, and the moment you close the window or your SSH session drops, it dies with you. That is fine for ls. It is useless for a web server, which needs to keep running at three in the morning when nobody is watching, come back by itself after a reboot, and pick itself up if it crashes. Something has to manage all that, and on virtually every modern Linux that something is systemd.
The building manager
Think of systemd as the building manager for your machine. You do not personally come in at dawn to flick every switch: someone unlocks the doors, brings the lifts and heating and lights up in a sensible order, restarts the boiler if it trips overnight, and keeps a logbook of everything that happened while you were asleep. systemd is that manager — the init system — and it holds a very special position: it is PID 1, the first process the kernel starts at boot and the ancestor of every other process on the machine. Everything else descends from it.
It was not always this way. The older approach, SysV init, booted the system by running a pile of shell scripts in a fixed numbered order — workable, but slow and rather brittle. systemd replaced it with something that starts services in parallel, understands the dependencies between them, and keeps an eye on them once they are up. That last part is the bit we care about most.
Tidbit: the most argued-about program on Linux
It would be remiss not to mention that systemd is divisive. It began life as a humble init system and grew, over the years, to absorb logging, network naming, device management, user sessions and a good deal more — and a vocal camp feels that is far too much for one project to swallow, against the old Unix instinct of “do one thing well.” Others simply appreciate that it works, consistently, everywhere. You do not need a side in this war to use it well; just know that if you ever see someone getting heated about it online, this is what they are on about.
Units — the things it looks after
systemd manages units, and a unit is simply “a thing systemd looks after.” They come in several flavours — timers that run jobs on a schedule, sockets, mount points, and targets that group other units together — but the one you will meet ninety percent of the time is the service (a unit whose filename ends in .service): a long-running program that systemd starts, watches, and restarts. For this post, “unit” and “service” are close enough to the same thing that you can read them interchangeably.
systemctl — the remote control
The command you drive it all with is systemctl, and the everyday verbs are exactly what you would guess:
| Command | What it does |
|---|---|
systemctl start nginx | Start it now |
systemctl stop nginx | Stop it now |
systemctl restart nginx | Stop then start — a full bounce |
systemctl reload nginx | Re-read its config without dropping connections (if it supports it) |
systemctl status nginx | Is it running? Plus its recent log lines |
start vs enable — the one everyone trips on
Here is the distinction that catches every newcomer, and it is worth pinning down properly because it is the systemd cousin of the update-versus-upgrade trap from the package post. start runs a service right now, for this boot only; enable makes it start automatically at every future boot. They are two completely independent switches.
Start now, or start forever
You can enable a service without starting it (it comes up on the next reboot, but not before), and start one without enabling it (it runs now, but a reboot forgets all about it). Nine times out of ten what you actually want for a real service is both — so systemd gives you the shorthand systemctl enable --now nginx, which enables it and starts it in a single go. If a service you installed quietly vanishes after a reboot, “I started it but never enabled it” is very nearly always the reason.
$ systemctl status nginx
● nginx.service - A high performance web server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Sat 2026-08-02 09:14:07 AEST; 2h 3min ago
Main PID: 1287 (nginx)
Tasks: 3 (limit: 4657)
Memory: 8.1M
Read that little block and you already know the three things that matter: enabled (yes, it will come back after a reboot), active (running) (yes, it is up right now), and the process ID it is running under. The green dot is systemd’s way of saying “all well here.”
journalctl — the logbook
Because systemd is the thing that started your service, it also captures everything the service prints — every line of standard output and standard error goes into the journal. This quietly solves one of the oldest new-to-Linux frustrations: “where on earth is the log file for this thing?” The answer is now always the same. Ask the journal:
$ journalctl -u nginx # everything nginx has logged
$ journalctl -u nginx -f # follow live, like tail -f
$ journalctl -u nginx -b # just this boot
$ journalctl -u nginx --since "1 hour ago"
One command, one syntax, for every service on the box. No more rummaging through /var/log hoping the author kept to a sensible convention.
service or systemctl? Both, sort of
You will absolutely run into the older command service — service nginx start in place of systemctl start nginx — and it is worth knowing why, because the two are not simply the same thing wearing different clothes. service comes from the SysV-init era; it is the old way. On a modern systemd machine it still works, but only as a thin compatibility wrapper that quietly forwards your command on to systemctl. On a genuinely old system with no systemd, service is the real tool and systemctl is nowhere to be found.
| Task | systemd (modern) | SysV-era (older boxes) |
|---|---|---|
| Start | systemctl start nginx | service nginx start |
| Stop | systemctl stop nginx | service nginx stop |
| Restart | systemctl restart nginx | service nginx restart |
| Status | systemctl status nginx | service nginx status |
| Start at boot | systemctl enable nginx | chkconfig nginx on / update-rc.d nginx enable |
The practical rule: reach for systemctl by default, because that is what almost everything runs now. But if you ever ssh into some venerable box and systemctl comes back “command not found”, do not panic — you have simply travelled back in time, and service plus chkconfig (the Red Hat side) or update-rc.d (the Debian side) are the tools waiting for you there.
Writing your own service
Here is the genuinely satisfying part: turning any command of your own into a proper managed service takes about six lines. Say you have a little script at /usr/local/bin/heartbeat.sh that you want running permanently, restarted if it ever falls over, and started automatically at boot. You describe it to systemd in a unit file:
# /etc/systemd/system/heartbeat.service
[Unit]
Description=My heartbeat script
After=network.target
[Service]
ExecStart=/usr/local/bin/heartbeat.sh
Restart=always
RestartSec=3
User=heartbeat
[Install]
WantedBy=multi-user.target
Three sections, and each earns its keep. [Unit] describes it and says what it should start after (here, once the network is up). [Service] is the meat: ExecStart is the command to run, and Restart=always is the whole payoff of this post — if the script dies for any reason, systemd waits three seconds and brings it straight back, whether you are watching or not. [Install]‘s WantedBy=multi-user.target is the line that makes enable mean “start at boot.” Then you tell systemd to read it and switch it on:
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now heartbeat.service
$ systemctl status heartbeat.service
That first line, daemon-reload, matters and is easy to forget: systemd does not notice a new or edited unit file until you tell it to re-read them. Skip it and you will swear blind the service does not exist. After that, enable --now switches it on for this boot and every future one — and you have built yourself a real, self-healing background service. Nginx, a database, your own scripts: under the bonnet they are all just unit files exactly like this one.
Targets, in one paragraph
One last piece of vocabulary you will bump into. Where the old world had numbered runlevels, systemd has targets — named groups of units describing an overall state of the machine. The two you will actually meet are multi-user.target (a normal, fully working server with networking and logins, but no desktop) and graphical.target (that, plus a graphical desktop). A server almost always boots to multi-user — which is exactly why the WantedBy=multi-user.target line above is what hooks your service into a normal boot. You rarely touch targets directly, but now the word will not catch you out.
Warning: mind the branch you are sitting on
A caution earned by sysadmins the hard way. When you are working on a remote machine over SSH, think twice before you stop or disable the very service you are connected through. systemctl stop sshd on a box you are SSHed into is the classic way to lock yourself out instantly, with no route back short of physical access or a console. The same care goes for firewalls and networking services. Before you disable anything on a remote machine, always ask the quiet question: “am I currently sitting on this branch?”
The one-sentence version
systemd is the init system that runs as PID 1 and manages your machine’s background services as units; you drive it with systemctl (start to run now, enable to run at every boot, status to check), you read what a service logged with journalctl -u, and turning your own command into a self-restarting service is a six-line unit file plus a daemon-reload. The whole command set — including the older service equivalents — lives in the systemd Cheatsheet.
That is the machine finally standing on its own two feet: navigable, controllable, stocked, and now able to keep its own services alive without you hovering over it. From here the Linux series turns outward — giving the box a presence on the network, and then deciding what it is allowed to say to the world. Next time: networking on Linux, where the addresses and subnets from the networking series finally meet the command line.
