journalctl Command: Tutorial & Examples
Every log line every daemon ever printed, indexed and filterable — no more digging through /var/log.
What It Is
journalctl is the front door to the systemd journal — a single, indexed, structured log that captures everything the kernel, the init system, and every service on the box emits. One command, one query language, every log on the system. If you've ever opened /var/log on a new server and felt the dread of which file?, this is the page that ends that feeling.
The promise sounds modest until you've lived without it. Before systemd shipped journald, every daemon on a Linux box picked its own log location, its own format, and its own rotation strategy — you'd have /var/log/syslog, /var/log/messages, /var/log/auth.log, /var/log/kern.log, /var/log/apache2/, /var/log/nginx/, and each one was a different text format that you grep'd with a different prayer. journalctl unified all of it into one queryable journal where every line carries metadata — which process, which user, which boot, which unit, what priority — and the queries compose. It's the most controversial systemd feature and its quietest win.
Your First Look
Type the command on any modern Linux box and you fall straight into a pager (less) at the oldest entry:
journalctl
-- Boot 9c2a8e3b80f7415cb89d4a3e2b1c5d6e --
May 20 09:14:02 web-01 kernel: Linux version 6.1.0-13-amd64 (debian-kernel@lists.debian.org)
May 20 09:14:02 web-01 kernel: Command line: BOOT_IMAGE=/boot/vmlinuz-6.1.0-13-amd64 root=UUID=...
May 20 09:14:03 web-01 systemd[1]: Starting systemd-journald.service - Journal Service...
May 20 09:14:03 web-01 systemd[1]: Started ssh.service - OpenBSD Secure Shell server.
May 20 09:14:03 web-01 sshd[612]: Server listening on 0.0.0.0 port 22.
May 27 14:21:07 web-01 sshd[18402]: Accepted publickey for holger from 10.0.0.5 port 51234 ssh2
...
Five columns per line, syslog-style: timestamp, hostname, process name and PID, then the message. The -- Boot ... -- markers separate reboots so you can see where the kernel handed off and the box came back up. Hit q to quit the pager (same key as less) — that one trips everyone the first time. Hit G to jump to the end, /pattern to search inside the pager.
The default view is the whole journal since journald started keeping records, which on a busy server can be gigabytes. Nobody actually reads it like this — every real session of journalctl starts by filtering. That's the next section, and it's the section that turns this tool from "log dump" into "structured query engine."
How I Read It
When something breaks, here is the exact sequence I run, in order, in maybe ten seconds.
First, scope to one unit and follow it live. Nine times out of ten I already know which service is misbehaving — ssh, nginx, postfix, mariadb — and the move is journalctl -u <service> -f. That -f follows in real time (the tail -f of the systemd world), scoped to one unit. Reload the service in another window and watch the log react. This single invocation is the workhorse.
Pro Tip
journalctl -u ssh.service -fis the most useful invocation in this entire page. It's thetail -f /var/log/...reflex you've built over the years, but unified and structured — no guessing which filesshdwrites to, no missing the line because it went to a sibling file, no rotation surprises. Pair it with--since '5 min ago'to also get the recent context before the live stream starts. Veterans keep onetmuxpane permanently onjournalctl -u <theservice> -fwhile they deploy or test in another.
Second, ask the triage question: what's gone wrong since this boot? One command answers it:
journalctl -p err -b
-p err keeps only entries of priority err or worse (the syslog priorities — emerg, alert, crit, err, warning, notice, info, debug — numbered 0–7, lower is louder); -b restricts to the current boot. The output is short, almost always — the actual errors, with the noisy info chatter filtered out. This is my go-to triage move on any unfamiliar box.
Note
-bis the boot filter, and it's worth knowing all three forms.-b(or-b 0) means this boot;-b -1means the previous boot;-b -2two boots ago, and so on.journalctl --list-bootsshows every boot the journal remembers with its ID and timestamps. When someone asks "why did the server reboot last night?",journalctl -b -1 -e(jump to the end of the previous boot) shows you the last lines the dying box wrote before it went down — invaluable, and you can't get this from rotated text logs.
Third, narrow by time when I'm hunting a specific incident: --since '1 hour ago', --since '2026-05-27 14:00' --until '2026-05-27 14:30'. The time parser accepts both absolute timestamps and human phrases ("yesterday", "10 min ago", "Monday"). Fourth, search inside messages with -g 'pattern' (PCRE) — the journal's own grep. And fifth, when I need everything about one line I pass -o verbose and see every field: the unit, the PID, the UID, the executable path, the cgroup, the boot ID, the source code file in the daemon that emitted it.
That's it: unit, priority, boot, time, pattern, verbose. Six dimensions; compose them freely.
The Filters Explained
The filters are the whole product. Every one of them composes with every other one — multiple filters are AND'd together — so journalctl -u ssh.service -p err -b --since '1 hour ago' reads as "errors from ssh since this boot, in the last hour." Here's the menu, with the reading for each.
-u <unit>— one systemd unit.journalctl -u ssh.service,-u nginx,-u mariadb. You can repeat-uto OR several units.--user-unitdoes the same for per-user units.-p <priority>— by syslog severity. Single value (-p err) keeps that level and worse; a range (-p warning..emerg) is explicit. Levels:0 emerg,1 alert,2 crit,3 err,4 warning,5 notice,6 info,7 debug.-b [ID]— by boot.-bfor this boot,-b -1for the previous,-b -Nfor N boots ago.--list-bootsshows the menu.-S/--sinceand-U/--until— by time. Accepts absolute (2026-05-27 14:00:00) or relative phrases (1 hour ago,yesterday,08:00).-g <pattern>— PCRE regex against message text.--case-sensitive=yesto force, defaults to smart-case.-t <ident>— by syslog identifier (the program name part —sshd,cron,kernel).-Texcludes one.-k— kernel messages only. This is journald's version ofdmesg, and unlikedmesgit persists across boots —journalctl -k -b -1reads the previous boot's kernel ring buffer, which the livedmesghas long forgotten._FIELD=valuematches — the structured query layer underneath all of the above._SYSTEMD_UNIT=ssh.service,_PID=1234,_UID=0,_HOSTNAME=web-01,PRIORITY=3,_COMM=sshd. List every field journald indexes withjournalctl -N; list every value of one field with-F _SYSTEMD_UNIT. Combine:journalctl _SYSTEMD_UNIT=ssh.service _PID=1234 PRIORITY=3— that's the magic, and it's what-u,-p,-tetc. are sugar for.-n <N>— last N lines.-r— reverse (newest first).-e— jump to the end in the pager (most common when you want recent).-f— follow (tail -f-style).
The output controls are worth knowing too: -o short-iso for sortable ISO timestamps in scripts, -o cat for just the messages (no timestamp/host/process — perfect for piping to grep or less), -o verbose to see all the indexed fields for each line, and -o json / -o json-pretty to export structured records you can pipe into jq or ship to an aggregator. The data has always been there; the format only hides it from you unless you ask.
Reading It by Example
The handful of invocations that will cover 95% of what you ever need.
Follow one service live, the way you used to tail -f a log file:
journalctl -u nginx.service -f
This replaces tail -f /var/log/nginx/error.log — except nginx's access log still goes to a file by default (see Gotchas), so use -f for errors and the file for access lines.
"What broke since this boot?" — the triage one-liner:
journalctl -p err -b
If the list is empty, this boot has had no errors. If it isn't, you have your starting point. Add -x (--catalog) to get human-readable explanations for messages that ship with a catalog entry — sometimes you get an actual paragraph about the error and a suggested fix.
Why did the box reboot? Read the previous boot's tail:
journalctl -b -1 -e
-e jumps to the end of the pager. The last hundred lines before -1 ended are almost always the story — a kernel panic, an OOM kill, a planned shutdown from systemctl. Pair with journalctl -k -b -1 to see only the kernel side.
A specific incident window:
journalctl --since '2026-05-27 14:00' --until '2026-05-27 14:30' -p warning
Warnings and worse, in a thirty-minute window. Add -u <service> to scope further.
Search across everything for a string:
journalctl -g 'failed password' --since yesterday
PCRE regex; this is the auth-failure scan that fits in one line and used to mean grepping /var/log/auth.log and its rotated .gz siblings.
One process, one PID, every field it logged:
journalctl _PID=18402 -o verbose
When ps gives you a PID and you want everything that process ever said. (PIDs are reused, so cross-check the timestamp.)
Pipe structured data to jq:
journalctl -u ssh.service --since '1 hour ago' -o json | jq -r 'select(.PRIORITY|tonumber <= 4) | .MESSAGE'
The data was always there.
Cheat Sheet
The invocations worth memorizing:
journalctl -u <unit> -f— follow one service live. The workhorse.journalctl -p err -b— errors and worse, this boot. The triage move.journalctl -b -1 -e— end of the previous boot. "Why did it reboot?"journalctl -k— kernel messages only (dmesgwith history).journalctl --since '1 hour ago'/-S ... -U ...— time window.journalctl -g 'pattern'— regex search across messages.journalctl -n 50— last 50 lines.-rnewest first.-ejump to end.journalctl -t sshd— by syslog identifier (program name).journalctl _PID=1234/_SYSTEMD_UNIT=.../PRIORITY=3— structured field matches; the real query layer.journalctl -N— list every field name.-F <field>— list every value of one field.journalctl --list-boots— every boot the journal remembers.journalctl -o verbose— every field per entry.-o json-pretty— structured export.journalctl -o cat— just the messages, no preamble. Pipes cleanly.journalctl --disk-usage— how much disk the journal is eating.journalctl --vacuum-time=7d/--vacuum-size=500M— prune old entries (run as root).journalctl --verify— check journal file consistency.
How You'll Actually Use It
In practice journalctl lives in three moments. One: a service misbehaves and you do journalctl -u <service> -f in one pane, restart it in another, watch the startup messages, see the bind error or the config typo, fix it, watch it come up clean. Two: something happened overnight — journalctl -p warning -b --since 'yesterday 18:00' and you scroll the short list looking for the smoking gun. Three: the box rebooted unexpectedly and you do journalctl -b -1 -e to read the last words of the previous boot.
If you find yourself building a big grep pipeline against journalctl output, stop and check whether a structured filter exists — _SYSTEMD_UNIT=, PRIORITY=, _COMM=, -g, -t cover most "I want to grep for X" cases without the grep. For shipping logs to an aggregator, -o json is your friend. For a daemon that doesn't use journald at all (nginx access logs, apache per-vhost logs, mysql slow query logs that go to their own files), the old reflexes still work — tail -f, less, grep, cat on the file directly. journalctl is for everything systemd touches, which on a modern Linux box is almost everything — but not quite.
Gotchas
qto quit. The default pager islessand the keybindings areless's; new users sometimes Ctrl-C their way out and worry they broke something.- The default view is oldest first. Surprising if you expect newest. Use
-e(jump to end) or-r(reverse) every time you want recent. -ffollows; without it, you get a snapshot. Same difference astailvstail -f.- Not every log is in the journal.
nginx's access log,apache's per-vhost logs,mysql's slow query log, application logs that write directly to a file — those still live under/var/logand you read them withtail/less/grep/cat. Whatjournalctlcovers is stderr/stdout from every systemd unit plus the kernel and anything that talks to journald via the syslog socket — which is most daemons, but check before assuming. - The journal may not persist across reboots. Default storage on some distros is
auto, which means RAM-only if/var/log/journal/doesn't exist. To persist:sudo mkdir /var/log/journal && sudo systemctl restart systemd-journald. Check withjournalctl --list-boots— if you only see this boot, your journal is volatile. - Disk usage can creep up.
journalctl --disk-usageshows the total;--vacuum-time/--vacuum-sizeprune it. The default cap is 10% of the filesystem, configured in/etc/systemd/journald.conf. -p errincludes everything worse than err too. People sometimes expect "only err" — that's-p err..err.-u nginxmatchesnginx.serviceautomatically, but the journal also indexes the literal unit name — if you havenginx@foo.serviceandnginx@bar.service, the bare-u nginxwon't match them. Use-u 'nginx@*'(glob) or be explicit.- Rotated journal files don't merge across boots automatically in every output mode. Almost always fine, but for forensics on archived journals, point at the directory with
-D /var/log/journal/<machine-id>/.
History & Philosophy
The Linux logging world before systemd was syslog and its descendants — rsyslog being the standard for most of the 2000s and 2010s. The model was simple and unstructured: every program wrote a line of text to a Unix socket, rsyslog routed it to a file based on facility and priority, and logrotate periodically renamed and gzipped the files so the disk didn't fill. It worked, but every line was just a string — no metadata, no schema, no way to ask "which PID wrote this?" or "this is from which boot?" without parsing free text. Querying meant grepping a dozen files in a dozen formats.
When Lennart Poettering and Kay Sievers shipped journald with systemd in 2011, the bet was simple: make every log line a structured record with metadata attached automatically by the kernel and the init system, store them in an indexed binary journal, and give one query tool — journalctl — that filters on any field. The metadata is free because journald already knows it: a daemon talking to the syslog socket comes with a verified PID, UID, GID, executable path, cgroup, systemd unit, and boot ID, attached by the kernel. Programs don't need to opt in. The line Failed password for root becomes a record with _SYSTEMD_UNIT=ssh.service, _PID=18402, _UID=0, PRIORITY=4, _BOOT_ID=..., MESSAGE=Failed password for root, all queryable independently. That's the whole magic, and it's why the query language feels like a different decade — because it is.
It was, and to some extent still is, controversial — a binary log format in the Unix world that has always preferred plain text was a hard sell, and "I can't grep /var/log/messages any more" was a real complaint. The pragmatic answer: rsyslog and syslog-ng still work, often alongside journald (journald forwards to the syslog socket by default), so if you want flat text files too you have them. But the query power of the structured journal has quietly won the day, and a generation of admins now reach for journalctl reflexively the way the previous generation reached for tail and grep against /var/log/syslog.
See Also
- systemd — the init system that ships journald and defines units
- systemctl — manage the services whose logs you read with journalctl
- dmesg — the kernel ring buffer;
journalctl -kis the persistent version - tail / less / cat / grep — for daemons that still write to files under /var/log
- rsyslog — the older logging daemon journald replaced (or coexists with)
- /var/log — where non-journald logs still live
- /var/log/syslog / /var/log/auth.log / /var/log/kern.log — the classic text logs
- syslog — the protocol and severity levels journalctl honors
- service — the systemd unit type behind
-u - boot — what
-bfilters on - ssh / nginx / apache / mysql / postfix — the daemons whose units you most often filter
Tired of remembering whether
sshdlogs go toauth.log,syslog, orjournalctl -u ssh?CleverUptime watches the symptoms this page is about — services failing or flapping, a box that rebooted without warning, OOMs the kernel quietly killed something for — and flags the underlying cause in plain language so you know which service to dig into before opening the journal.
Want to see your own server's health right now? One command, no signup, no install.