systemd and Services

K
Kai··4 min read

On a Linux server, background services (web server, database, your own app) are managed by systemd — the init system on most modern distros (Ubuntu, Debian, Fedora, RHEL...). Knowing systemd is how you start/stop services, have them auto-start when the server boots, and view logs when they fail.

Environment note (important): systemd is the PID 1 process of the entire system, so it does not run in an ordinary container (a container's PID 1 is the process you start — we verified this: in an Ubuntu container, systemctl isn't even present). To practice this article, use a Linux VM (Multipass, VirtualBox) or a VPS/cloud instance — which is also exactly where you'll meet systemd in real life. There are ways to run systemd in a container (a dedicated image + --privileged) but they're fiddly; a VM is more straightforward.

What systemd is

When Linux boots, the kernel runs a first process (PID 1) called the init system, and it's responsible for starting and managing everything else. systemd is the most common init system today.

systemd manages everything as a unit. The most common unit type is the service (.service) — a background process. There are also .socket, .timer (a cron replacement — Article 17), .mount... but the service is the one you use most.

systemctl: controlling services

systemctl is the main command for working with systemd:

systemctl status nginx      # service status (running? failed? recent logs)
systemctl start nginx       # start now
systemctl stop nginx        # stop
systemctl restart nginx     # stop then start again (apply new config)
systemctl reload nginx      # reload config without a full stop (if the service supports it)

(These commands usually need sudo — Article 12.)

systemctl status is the command you run most when debugging:

● nginx.service - A high performance web server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; ...)
     Active: active (running) since ...
       Main PID: 1234 (nginx)

Quick read: Active: active (running) = running; enabled = will auto-start at boot. If failed, the section below shows a few recent error log lines — the first clue for debugging.

start vs enable: an important difference

This is a core systemd distinction (like the update/upgrade pair in Article 11):

  • start — run the service right now. But if the server reboots, it will not auto-start.
  • enable — register the service to auto-start at every boot. But it doesn't start it right now.

So to have a service both run now and auto-start after reboot, do both (or use --now):

systemctl enable --now nginx     # enable + start in one command
systemctl disable --now nginx    # disable + stop

The classic mistake: start a service, everything's fine, but forget to enable it — the server reboots and the service is gone. Remember: start = run now, enable = auto-start at boot.

Writing your own unit file

To run your application as a systemd service (auto-restart on crash, auto-start at boot — replacing nohup & from Article 8), create a .service file. For example /etc/systemd/system/myapp.service:

[Unit]
Description=My App
After=network.target

[Service]
ExecStart=/usr/bin/node /opt/myapp/server.js
WorkingDirectory=/opt/myapp
User=appuser
Restart=on-failure

[Install]
WantedBy=multi-user.target

Reading the blocks:

  • [Unit] — description and ordering: After=network.target means run after the network is ready.
  • [Service] — how to run: ExecStart is the start command, User is the user it runs as (should be a dedicated user, not root — Article 12), Restart=on-failure tells systemd to auto-restart when the app crashes. This is a big benefit over nohup.
  • [Install]WantedBy=multi-user.target so enable hooks it into the normal boot process.

After creating/editing a unit file, reload then enable:

systemctl daemon-reload          # make systemd re-read the unit files
systemctl enable --now myapp

daemon-reload is the easily forgotten step — edit a unit file without reloading and systemd still uses the old version.

journalctl: viewing a service's logs

systemd collects every service's logs into one place (the journal), viewed with journalctl:

journalctl -u nginx          # logs for the nginx service
journalctl -u nginx -f       # follow new logs in real time (like tail -f)
journalctl -u nginx --since "10 min ago"   # logs from the last 10 minutes
journalctl -p err -b         # only error-level logs, in this boot (-b)

journalctl -u <service> -f is the golden pair with systemctl status when debugging: status tells you whether the service is alive/dead, journalctl tells you why.

Recall Article 10 — the journal can eat disk. Cap it with journalctl --vacuum-size=200M or configure it in /etc/systemd/journald.conf.

🧹 Cleanup

If practicing on a VM and you created a test service:

systemctl disable --now myapp
rm /etc/systemd/system/myapp.service
systemctl daemon-reload

Wrap-up

systemd is the init system that manages services on modern Linux; each service is a .service unit. systemctl controls it: start/stop/restart, and distinguishes start (run now) from enable (auto-start at boot) — use enable --now for both. You run your own app as a service with a unit file (with Restart=on-failure for auto-recovery), remembering daemon-reload after edits. journalctl -u <service> views the logs. Because systemd is PID 1, practice on a real VM/server, not a container.

Article 16 turns to automation: shell scripting — combining commands into scripts with variables, conditionals, and loops, so you don't retype them and can automate your work.