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:
| Goal | Command |
|---|---|
| Last N lines | journalctl -n 20 |
| End + explain | journalctl -xe |
| No pager | journalctl … --no-pager |
| By unit | journalctl -u name.service |
| Several units | journalctl -u a -u b |
| Errors+ | journalctl -p err |
| Priority range | journalctl -p warning..err |
| Since / until | journalctl --since "1 hour ago" |
| Current boot | journalctl -b |
| Previous boot | journalctl -b -1 |
| List boots | journalctl --list-boots |
| Follow | journalctl -u name -f |
| Kernel (journal) | journalctl -k -b |
| ISO timestamps | journalctl -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.
- Print the last 15 journal lines without opening a pager.
- Show errors-and-worse from the current boot only.
- List boots, then show the last 30 lines from the previous boot.
- Pick a real unit on your system (for example
ssh.serviceorcron.service) and show its logs since 1 hour ago. - 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.