systemd services & journalctl
systemctl start/stop/enable, writing your own unit file, and reading logs with journalctl.
On a server, the important programs (web servers, databases, SSH, your own API) run as services: background processes that start at boot, restart if they crash, and log somewhere sensible. On almost every modern distribution (Ubuntu, Debian, Fedora, RHEL, Arch) that job belongs to systemd, the first process the kernel starts (PID 1). You talk to it with systemctl, and you read its logs with journalctl.
💡 These commands need a real systemd system: a VM, a cloud server or a desktop install. Most Docker containers and WSL setups without systemd enabled will say "System has not been booted with systemd as init system". On WSL, add
systemd=trueunder[boot]in/etc/wsl.confand restart WSL.
Units#
systemd manages units, each described by a small text file. The most common types:
You can usually omit .service: systemctl status nginx means nginx.service.
Checking a service#
Read it like this:
- Loaded shows where the unit file is, and whether it's enabled (starts at boot).
- Active is the current state:
active (running),inactive (dead),failed, oractivating. - Main PID, memory, CPU and the CGroup show every process belonging to the service.
- The last lines are the most recent log entries.
(The service is called ssh on Debian/Ubuntu and sshd on Fedora/RHEL.) Quick yes/no checks, which are handy in scripts:
Controlling services#
💡 Prefer
reloadafter editing configs when the service supports it, and test the config first:sudo nginx -t,sudo sshd -t,sudo apachectl configtest. A typo plusrestartequals downtime.
Note the distro difference: on Debian/Ubuntu, installing a service package usually starts and enables it right away. On Fedora/RHEL it doesn't, so you run sudo systemctl enable --now nginx yourself.
Listing units#
Writing your own service#
Say you have a small Python web app in /opt/myapp. Rather than leaving it in tmux or behind nohup, give it a unit file:
What each part does:
[Unit]: a description and ordering.After=sets ordering only;Wants=/Requires=set dependencies.[Service]:ExecStartmust be an absolute path. It isn't run through a shell, so there's no~, pipes or&&. If you need those, useExecStart=/bin/bash -c '...'.User=runs the app as an unprivileged account (create it withsudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp).Environment=andEnvironmentFile=set variables. The-means "ignore if the file is missing".Restart=on-failurerestarts it if it crashes.alwaysrestarts it even after a clean exit.Type=simple(the default) suits programs that stay in the foreground, which is what you want. Don't daemonise yourself.
[Install]:WantedBy=multi-user.targetmeans "start during normal boot" when enabled.
Load and start it:
Never edit files in /usr/lib/systemd/system/ (or /lib/systemd/system/); package upgrades overwrite them. Put your own units in /etc/systemd/system/, and to tweak a packaged unit, use a drop-in override:
That opens an editor for /etc/systemd/system/nginx.service.d/override.conf, where you add only the lines you want to change:
systemctl edit runs daemon-reload for you; then sudo systemctl restart nginx. Use systemctl cat nginx to see the original plus overrides, and sudo systemd-analyze verify /etc/systemd/system/myapp.service to lint a unit.
Reading logs with journalctl#
systemd collects the stdout/stderr of every service, plus kernel and system messages, into the journal:
A typical failure investigation:
The fix: install flask into the app's venv, then sudo systemctl restart myapp. If a unit restarts too often it hits the start limit. In that case, run sudo systemctl reset-failed myapp after fixing the cause.
Reading other users' and system logs requires sudo or membership of the adm or systemd-journal group. You'll learn more about the journal and /var/log in the logs lesson.
Boot, targets and power#
Common mistakes#
- Forgetting
daemon-reloadafter editing a unit, so systemd keeps using the old version (it warns you instatus). startwithoutenable. The service works until the next reboot, then it's gone.- Relative paths or shell syntax in
ExecStart. Use absolute paths, or wrap the command in/bin/bash -c. - Running your app as root. Set
User=to a dedicated system user. - Editing vendor units in
/usr/lib/systemd/system. Usesystemctl editdrop-ins instead. - Daemonising inside the service (
&,nohup,--daemon) withType=simple. systemd thinks the program exited. Keep it in the foreground.
What's next#
Services store data and logs on disk. Next: disks and storage, covering how much space you have, what fills it, and how filesystems are mounted.
Check your understanding
Quick quiz
1.What is the difference between
systemctl start nginxandsystemctl enable nginx?2.You edited a unit file in /etc/systemd/system/. What must you run before systemd sees the change?
3.Which command follows the live logs of the
myappservice?
Finished reading?
Mark this lesson complete to track your progress.