Reading Logs With journalctl
Every service you start, every kernel message, every boot — systemd captures all of it in a single, structured log called the journal, and journalctl is the tool for reading it. Unlike a plain text log file, the journal stores genuine structured metadata alongside every entry — which unit generated it, its priority level, which boot it belongs to — which is exactly what makes filtering it so much more precise than scrolling through a text file by eye.
The Basic View
journalctlThis opens the entire journal in a pager (behaving like less, covered earlier in this course), oldest entries first, most recent at the bottom. On any system that’s been running for a while, this is a genuinely enormous amount of output — in practice, you’ll almost always reach for one of the filters below rather than browsing the whole thing unfiltered.
Filtering By Service: -u
journalctl -u nginxShows only entries logged by the nginx unit — precisely the same log lines you’d have glimpsed briefly in systemctl status nginx’s output, now shown in full rather than just the last few lines.
Filtering By Boot: -b
Every time the system boots, that boot is assigned its own identifier internally, and the journal remembers exactly where one boot’s entries end and the next one’s begin.
journalctl -bShows only entries from the current boot — genuinely useful for isolating “what’s happened since the system last started,” without older, unrelated history mixed in.
journalctl -b -1Shows the previous boot instead — the single most useful command for diagnosing a crash or an unexpected reboot, since it lets you see exactly what the system was logging right up until it went down, from a session you no longer have live access to.
Following Logs Live: -f
journalctl -fDirectly analogous to tail -f from earlier in this course — instead of a static view, this streams new journal entries as they happen, staying open until you press Ctrl+C. Combine it with -u to watch one specific service in real time while you test something against it:
journalctl -u nginx -fFiltering By Priority: -p
Every journal entry carries a priority level, following the same standard syslog severity scale:
| Level | Name |
|---|---|
| 0 | emerg — system is unusable |
| 1 | alert — action must be taken immediately |
| 2 | crit — critical condition |
| 3 | err — an error |
| 4 | warning |
| 5 | notice |
| 6 | info |
| 7 | debug |
journalctl -p errShows entries at the given level and more severe — -p err includes err, crit, alert, and emerg, but not warning or below. This is often the fastest way to scan for genuine problems across the entire system, cutting through routine informational noise entirely.
Filtering By Time: --since And --until
journalctl --since "2026-01-15 09:00"
journalctl --since today
journalctl --since "1 hour ago"
journalctl --since yesterday --until todayjournalctl accepts both exact timestamps and genuinely natural relative phrases — --since "1 hour ago" works exactly as it reads. Combine time filtering with everything else covered so far for a precise, targeted query:
journalctl -u nginx -p err --since todayThis reads as close to plain English as a command in this course gets: nginx’s error-level-or-worse entries, from today only.
Kernel Messages: -k
journalctl -kShows only kernel-originated messages — the same category of information dmesg covers, but with a genuine advantage: journalctl -k can show kernel messages from previous boots too (combine with -b -1), while dmesg’s ring buffer only ever holds the current boot’s messages and is wiped entirely on reboot.
Limiting Output: -n
journalctl -u nginx -n 20Shows just the most recent 20 entries — the journalctl equivalent of tail -n, useful when you just want a quick recent glance rather than the full filtered history.
Persistent vs. Volatile Storage
This is a genuinely important detail that catches people off guard: by default on many systems, the journal is stored in /run/log/journal — a location that lives in memory and is wiped on every reboot. If you’ve ever tried to check journalctl -b -1 after a crash and found nothing at all, this is very likely why.
To make the journal persistent across reboots, the fix is straightforward:
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journaldSimply creating that directory is enough — systemd-journald checks for its existence and switches to persistent storage there automatically once it’s present, keeping journal data safely on disk across reboots from that point forward.
Managing Journal Size
A persistent journal can grow substantially over time. Check how much space it’s actually using:
journalctl --disk-usageTo deliberately trim it back:
sudo journalctl --vacuum-size=500M
sudo journalctl --vacuum-time=2weeks--vacuum-size removes the oldest entries until the journal fits within the given size; --vacuum-time removes anything older than the given duration instead. Either is a reasonable thing to run periodically on a system where disk space matters and long historical logs aren’t a hard requirement.
Output Formats: -o
By default, journalctl prints a human-readable, single-line-per-entry format. -o changes that:
journalctl -u nginx -o json-prettyFormats each entry as structured, readable JSON — genuinely useful when you need to feed journal data into another tool that expects structured input rather than free-form text.
journalctl -u nginx -o catStrips out all the metadata — timestamp, hostname, unit name — showing just the raw message text itself, closer to what a plain log file would have looked like.
What’s Next
You can now find essentially anything the system has logged, filtered exactly the way you need it. The journal isn’t the only logging mechanism you’ll encounter in practice, though — the next chapter covers the older, still widely used syslog approach, and how it coexists with everything covered in this chapter on a typical Debian or Ubuntu system.