When a service fails at 03:00, hunting through scattered files under /var/log is the slow path. On systemd systems the answer usually lives in the journal — a structured log store that journalctl knows how to query. Filter by unit, severity, time, or which boot you care about, and you get the incident window instead of a wall of unrelated noise.

You do not need every match field on day one. Start with a short recent dump, then layer on unit filters, priorities, time ranges, and boot selectors as the problem narrows. The sections below walk through the commands you will reach for most often — with enough context that each one feels intentional, not magical.

Warm-up: peek at the journal

Unlike file-based tools, there is no sample log to create — your playground is the live journal on the machine in front of you. Start small so you are not scrolling through days of history.

Print the last 20 lines:

journalctl -n 20

Typical lines look something like this (your hosts, units, and messages will differ):

Aug 05 00:10:12 hostname systemd[1]: Started CUPS Scheduler.
Aug 05 00:10:15 hostname sshd[1234]: Accepted publickey for alice from 192.168.1.10
Aug 05 00:10:18 hostname NetworkManager[567]: <info> device (eth0): state change

Add -xe when you want the pager focused on the end of the journal with explanatory help text for priority lines — a common first stop after a failed systemctl start:

journalctl -xe

In scripts and copy-paste sessions, disable the pager so output streams straight to the terminal (or into a pipe):

journalctl -n 20 --no-pager

Note: On many systems, unprivileged users only see their own user journal. System services often need sudo journalctl …. If the result looks suspiciously empty, retry with elevated rights before assuming nothing was logged.

Filter by unit

Most incidents start with a service name. -u limits output to one systemd unit so you are not reading the whole machine.

Inspect the SSH daemon (unit names vary slightly by distro — ssh.service vs sshd.service):

journalctl -u ssh.service -n 30 --no-pager

If you are unsure of the exact unit name, list active services and grep for a clue:

systemctl list-units --type=service --all | grep -i ssh

Pass -u more than once to merge related units into one timeline — useful when a web app and its proxy fail together:

journalctl -u nginx.service -u php-fpm.service -n 50 --no-pager

Short names often work when they are unambiguous (-u nginx), but the full name.service form is clearer in scripts and shared runbooks.

Note: -u filters on the unit, not on a free-text program name in the message. A binary that logs without a matching unit may still appear under syslog or another sink — confirm the unit with systemctl status <name> when the journal looks empty.

Filter by severity

A busy journal mixes informational chatter with real faults. Priority filters cut the noise the same way log levels do in application code.

Show errors and worse (err, crit, alert, emerg) with -p:

journalctl -p err -n 20 --no-pager

Priorities are ordered from emerg (0) through debug (7). A single name means “this level and more severe.” For a window of levels, use a range:

journalctl -p warning..err -n 20 --no-pager

Numeric form works too if you prefer it in scripts:

journalctl -p 3 -n 20 --no-pager

Note: Reach for -p err (or warning) early when triaging a noisy host. Drop the priority filter only when you need informational context around a failure you already found.

Filter by time range

When you know when something broke, bound the query with --since and --until instead of scrolling.

Relative times are usually enough for live incidents:

journalctl --since "1 hour ago" --no-pager
journalctl --since "2026-08-05 09:00:00" --until "2026-08-05 10:00:00" --no-pager

Today and yesterday are convenient anchors:

journalctl --since today --no-pager
journalctl --since yesterday --until today --no-pager

Combine time with a unit so you only replay the service inside the incident window:

journalctl -u ssh.service --since "30 min ago" --no-pager

Note: Timestamps follow the journal’s understanding of local time unless you pass UTC-oriented options. When correlating with UTC application logs, keep the timezone story consistent on both sides.

Filter by boot

Reboots reset a lot of runtime state, but the journal can keep history across boots when persistence is enabled. -b selects which boot’s messages you are reading.

Current boot only (the usual default you want while the machine is up):

journalctl -b --no-pager | tail -n 20

Previous boot — the first place to look after an unexpected reboot or a failed upgrade:

journalctl -b -1 -n 50 --no-pager

List known boots so you can pick an older index intentionally:

journalctl --list-boots

Typical output looks like this (IDs and offsets are host-specific):

IDX BOOT ID                          FIRST ENTRY                 LAST ENTRY
 -1 abcdef1234567890...              Mon 2026-08-04 08:01:11 IST Mon 2026-08-04 22:14:03 IST
  0 fedcba0987654321...              Tue 2026-08-05 00:05:44 IST Tue 2026-08-05 00:30:12 IST

Kernel messages for the current boot (companion to dmesg) live behind -k:

journalctl -k -b -n 20 --no-pager

Note: If --list-boots only shows the current boot, the journal may be volatile (in-memory only) or newly rotated. Persistence is a journald configuration choice — for day-to-day filtering you still use the same -b flags on whatever history exists.

Combine filters for real incidents

Real triage stacks filters. Unit + priority + time (and sometimes boot) turns “the whole journal” into “what failed in this service during this window.”

Errors from a unit in the last hour:

journalctl -u nginx.service -p err --since "1 hour ago" --no-pager

Previous boot, warnings and above, for a database unit:

journalctl -b -1 -u postgresql.service -p warning --no-pager

Add -o short-iso when you need machine-friendly timestamps for pasting into a ticket:

journalctl -u ssh.service --since "1 hour ago" -o short-iso --no-pager

Note: Add one constraint at a time. If the combined query returns nothing, loosen priority or widen --since before assuming the unit never logged.

Follow live, and the kernel bridge

When you are reproducing a failure, follow the journal the way you would tail -f a file. -f streams new entries as they arrive; Ctrl+C stops.

Watch a unit live:

journalctl -u ssh.service -f

Watch errors across the system as you trigger the bug:

journalctl -p err -f

For kernel-only lines, journalctl -k reads the journal’s kernel facility. That overlaps with dmesg, with the important difference that the journal can retain history across buffer wraps and reboots when persistence is on. Prefer journalctl -k for “what did the kernel say last boot?”; prefer dmesg when you want the live ring buffer and util-linux’s buffer-centric flags.

journalctl -k -b -1 -n 50 --no-pager

Note: Follow still respects journal permissions. If a plain query needs sudo, so does -f.

Quick reference card

Keep this nearby until the flags become muscle memory:

GoalCommand
Last N linesjournalctl -n 20
End + explainjournalctl -xe
No pagerjournalctl … --no-pager
By unitjournalctl -u name.service
Several unitsjournalctl -u a -u b
Errors+journalctl -p err
Priority rangejournalctl -p warning..err
Since / untiljournalctl --since "1 hour ago"
Current bootjournalctl -b
Previous bootjournalctl -b -1
List bootsjournalctl --list-boots
Followjournalctl -u name -f
Kernel (journal)journalctl -k -b
ISO timestampsjournalctl -o short-iso

Practice drills

Use your own machine’s journal (with sudo if needed) and try these without peeking. The point is to choose the filter with intent, not to memorize syntax under pressure.

  1. Print the last 15 journal lines without opening a pager.
  2. Show errors-and-worse from the current boot only.
  3. List boots, then show the last 30 lines from the previous boot.
  4. Pick a real unit on your system (for example ssh.service or cron.service) and show its logs since 1 hour ago.
  5. Combine unit + priority + time: warnings and above for that unit in the last 24 hours, ISO timestamps, no pager.

When you are ready to compare, here are solid answers — not the only ones, but clear and portable (adjust the unit name to match your host):

journalctl -n 15 --no-pager
journalctl -b -p err --no-pager
journalctl --list-boots
journalctl -b -1 -n 30 --no-pager
journalctl -u ssh.service --since "1 hour ago" --no-pager
journalctl -u ssh.service -p warning --since "24 hours ago" -o short-iso --no-pager

If you can work through those five comfortably, you already cover most real journalctl work: recent dumps, severity cuts, boot-scoped triage, unit timelines, and combined incident queries. Start with -n and --no-pager, then tighten with -u, -p, --since, and -b only when the first pass gets noisy.