systemctl and journalctl: services and logs for beginners
Start, stop and enable services, then read their logs by unit, boot, time and priority.
Part 10 of 12 in Linux basics for self-hosters. Last updated
In short: sudo systemctl start caddy starts a service now, sudo systemctl enable caddy starts it at every boot (enable --now does both), and systemctl status caddy shows whether it runs plus its last log lines. journalctl -u caddy shows that service's full log, -f follows new lines, -b limits it to the current boot and --since '1 hour ago' to a time window. Rootless podman services use the same commands with --user and no sudo.
systemctl: the everyday commands
systemd starts almost everything on a current Linux system, and systemctl is how
you talk to it. Each service is a unit named like sshd.service; you may
leave out the .service. Changing a system service needs sudo; looking
does not.
systemctl status sshd # running? since when? last log lines
sudo systemctl start caddy # start now
sudo systemctl stop caddy # stop now
sudo systemctl restart caddy # stop and start
sudo systemctl reload caddy # re-read the configuration, if the service supports it
sudo systemctl enable --now caddy # start at every boot, and now
sudo systemctl disable --now caddy # the reverse
systemctl is-active caddy # prints active, inactive or failed (handy in scripts)
systemctl --failed # everything that failed
systemctl list-units --type=service --state=running
systemctl cat caddy # the unit file and its drop-ins
sudo systemctl edit caddy # add an override that survives updates
sudo systemctl daemon-reload # after editing unit files by hand systemctl status also accepts a PID and tells you which unit that process belongs
to. The full reference is systemctl(1).
Reading systemctl status
● sshd.service - OpenSSH Daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; preset: disabled)
Active: active (running) since Fri 2026-09-25 12:52:20 CEST; 1 day 21h ago
Main PID: 1234 (sshd)
Tasks: 1
Memory: 5.2M
CGroup: /system.slice/sshd.service
└─1234 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups" - Loaded: where the unit file is, and
enabled(starts at boot) ordisabled. The preset is only the distribution's default suggestion. - Active:
active (running)is good;failedmeans it crashed or exited with an error;activating (auto-restart)means it keeps crashing and systemd keeps trying. - The last lines below the header come from the journal. When a service fails, the reason is usually right there.
systemctl --failed is the quickest health check. On a real laptop it found two
units; the Cockpit one is a known openSUSE issue explained in the
Cockpit troubleshooting guide:
$ systemctl --failed --no-pager --no-legend
● vboxadd-service.service loaded failed failed Start/Stop VirtualBox Guest Linux module
● cockpit.socket loaded failed failed Cockpit Web Service Socket Real output on openSUSE Tumbleweed, systemd 261, 2026-09-28; user and host names and addresses redacted.
User services and --user
Every logged-in user has their own systemd instance. Rootless podman quadlets run there, so the
commands are the same with --user and without sudo:
systemctl --user daemon-reload # after adding or changing a quadlet
systemctl --user start whoami.service
systemctl --user status whoami.service
journalctl --user -u whoami.service -f
sudo loginctl enable-linger "$USER" # run user services at boot, without a login
Quadlet units are generated, so systemctl --user enable fails on them with
"transient or generated". The [Install] section in the .container file
does the enabling. See the whoami quadlet example for a
complete, tested one.
journalctl: reading logs
The journal collects the output of every service, the kernel and the boot process in one place. You filter it instead of hunting through files:
journalctl -u caddy # everything from one service, oldest first
journalctl -u caddy -f # follow new lines (Ctrl+C to stop)
journalctl -u caddy -n 50 # the last 50 lines
journalctl -u caddy -b # only since the current boot
journalctl -b -1 -p err # errors from the previous boot
journalctl --since "1 hour ago" # a time window
journalctl --since today --until "10:30"
journalctl -p warning..err -b # warnings and errors, this boot
journalctl -k -b # kernel messages (like dmesg)
journalctl -u sshd -g 'Failed password' # search the message text
journalctl -u caddy -o short-iso # ISO 8601 timestamps
journalctl --list-boots # which boots are stored
journalctl --disk-usage # how much space the journal uses
Filters combine: journalctl -u caddy -b -p err is "errors from caddy since this
boot". Without sudo you only see your own user's logs, and journalctl prints a hint saying so.
Tick the filters you need in the
journalctl command builder to get the
command with every flag explained. Reference:
journalctl(1).
Timers instead of cron
A systemd timer starts a service on a schedule. Compared with cron, the job gets the journal
for its output, Persistent=true catches up on runs missed while the machine was
off, and systemctl list-timers shows the next and last run of every job:
systemctl list-timers # all timers, next run first
systemctl --user list-timers # your own
sudo systemctl enable --now backup.timer
journalctl -u backup.service # what the last runs printed Timers that come with openSUSE, and the unit file of one of them:
$ systemctl list-timers --no-pager fstrim.timer snapper-timeline.timer snapper-cleanup.timer logrotate.timer
NEXT LEFT LAST PASSED UNIT ACTIVATES
Mon 2026-09-28 06:52:18 CEST 16min Mon 2026-09-28 05:52:18 CEST 43min ago snapper-cleanup.timer snapper-cleanup.service
Mon 2026-09-28 07:00:00 CEST 24min Mon 2026-09-28 06:00:03 CEST 35min ago snapper-timeline.timer snapper-timeline.service
Tue 2026-09-29 00:50:51 CEST 18h Mon 2026-09-28 00:06:45 CEST 6h ago logrotate.timer logrotate.service
Mon 2026-10-05 00:14:07 CEST 6 days Mon 2026-09-28 00:26:59 CEST 6h ago fstrim.timer fstrim.service
4 timers listed.
Pass --all to see loaded but inactive timers, too.
$ systemctl cat --no-pager fstrim.timer
# /usr/lib/systemd/system/fstrim.timer
[Unit]
Description=Discard unused filesystem blocks once a week
Documentation=man:fstrim
ConditionVirtualization=!container
ConditionPathExists=!/etc/initrd-release
[Timer]
OnCalendar=weekly
AccuracySec=1h
Persistent=true
RandomizedDelaySec=100min
[Install]
WantedBy=timers.target Real output on openSUSE Tumbleweed, systemd 261, 2026-09-28; user and host names and addresses redacted.
Before you trust a schedule, let systemd read it back. systemd-analyze calendar
prints the normalized form and the next runs:
$ systemd-analyze calendar --iterations=3 'Mon *-*-* 04:00'
Original form: Mon *-*-* 04:00
Normalized form: Mon *-*-* 04:00:00
Next elapse: Mon 2026-10-05 04:00:00 CEST
(in UTC): Mon 2026-10-05 02:00:00 UTC
From now: 6 days left
Iteration #2: Mon 2026-10-12 04:00:00 CEST
(in UTC): Mon 2026-10-12 02:00:00 UTC
From now: 1 week 6 days left
Iteration #3: Mon 2026-10-19 04:00:00 CEST
(in UTC): Mon 2026-10-19 02:00:00 UTC
From now: 2 weeks 6 days left Real output on openSUSE Tumbleweed, systemd 261, 2026-09-28; user and host names and addresses redacted.
Build a schedule in plain words with the cron schedule builder,
or turn an existing crontab line into a .timer and .service pair with the
cron to systemd timer converter. The
systemd unit explainer tells you what each line
of the result does.
Common mistakes
Editing a unit and forgetting daemon-reload
systemd keeps its own copy of every unit. After changing a file by hand, run
sudo systemctl daemon-reload (or systemctl --user daemon-reload) and
then restart the service. systemctl edit reloads for you.
enable without --now, or start without enable
enable alone waits for the next boot; start alone is forgotten at the
next boot. The classic result is a service that works until the first power cut. Use
enable --now.
Logs disappear after a reboot
Without the directory /var/log/journal, some systems keep the journal only in
memory. Create it with sudo mkdir -p /var/log/journal and run
sudo systemctl restart systemd-journald; from then on journalctl -b -1
works. Size limits go in a drop-in under /etc/systemd/journald.conf.d/. What to do
when a service eats CPU instead of failing is in
processes and resources.
Frequently asked questions
How do I follow the logs of a service?
journalctl -u name.service -f prints the last lines and then every new one as it arrives; stop with Ctrl+C. For a rootless user service add --user: journalctl --user -u name.service -f.
What is the difference between systemctl enable and start?
start runs the service now, until it stops or the machine reboots. enable makes it start automatically at boot but does not start it now. sudo systemctl enable --now name does both at once; disable --now undoes both.
How do I see logs from the previous boot?
journalctl -b -1 shows the previous boot, -b -2 the one before, and journalctl --list-boots lists them all. This only works when the journal is stored on disk, which is the default when /var/log/journal exists.
Why does journalctl show no entries for other services?
A normal user only sees their own logs. Use sudo, or add your user to the systemd-journal group and log in again. systemd also lets members of the adm and wheel groups read the system journal by default.