journalctl
Read and filter the systemd journal
journalctl reads the log that systemd collects. Anything a service writes to stdout or
stderr goes there, along with the kernel's messages and systemd's own account of what it
started and why it stopped, all with the metadata to filter by unit, priority, time or boot.
The journal is not a text file. It is an indexed binary store, which is why journalctl
has flags for questions that would otherwise be grep with a date regex, and why
/var/log/ looks emptier on a systemd machine than you might expect. grep still works on
the output, and grep is the right tool once you have narrowed things
down with -u and --since.
Three flags do most of the work:
journalctl -u nginx # one unit
journalctl -p err # errors and worse
journalctl --since "1 hour ago"
They combine, and most useful invocations use at least two of them together.
Priorities
-p takes a syslog level by name or number, and matches that level and everything more
severe: -p warning includes errors and critical messages too. From most to least severe
they are emerg, alert, crit, err, warning, notice, info, debug.
Who can read what
Reading your own user's entries needs no privileges. Reading everything, meaning other users'
services, the kernel and most system units, means being root or a member of the
systemd-journal group:
sudo usermod -aG systemd-journal "$USER" # log out and back in
The examples below are shown as a root shell would run them. Prefix them with sudo if you
are not root and not in that group. Note that sudo writes its own entry to the journal, so
sudo journalctl -n 3 shows you the command you just typed.
Sample files used on this page
Every example below was run against these files. Recreate them to follow along.
what is in the journal for these examples a failing backup-sync.service, plus three entries tagged deploy-agent at different priorities - written fresh before each example, so they are always the most recent entries
deploy 4a91c2 finished in 4.2s
retrying upload, attempt 2 of 3
upload failed: no space left on device
backup-sync.service: Main process exited, code=exited, status=1/FAILURE
backup-sync.service: Failed with result 'exit-code'.
Failed to start backup-sync.service - Nightly backup sync.
Reading the journal
journalctl opens less by default. --no-pager is what you want in a script or when piping, and every example here uses it.
Show the most recent entries
journalctl -n 3 --no-pager -o cat
-n limits how many entries you get, newest last. Without it you start at the oldest entry the journal still holds, which on a busy machine is a lot of scrolling.
Show output
deploy 4a91c2 finished in 4.2s
retrying upload, attempt 2 of 3
upload failed: no space left on device
See the timestamps and which program logged each line
journalctl -n 3 --no-pager
The default format: time, hostname, the program's name and process id, then the message. This is what you get without -o cat.
Show output
Your output will differ: the date, time and process ids are from the machine that produced this
Aug 17 12:45:28 deb1 deploy-agent[1993]: deploy 4a91c2 finished in 4.2s
Aug 17 12:45:28 deb1 deploy-agent[1996]: retrying upload, attempt 2 of 3
Aug 17 12:45:28 deb1 deploy-agent[1999]: upload failed: no space left on device
Show the newest entries first
journalctl -n 3 -r --no-pager -o cat
-r (--reverse) puts the most recent line at the top, which is what you want when checking whether something just failed.
Show output
upload failed: no space left on device
retrying upload, attempt 2 of 3
deploy 4a91c2 finished in 4.2s
Print just the messages, without any metadata
journalctl -n 2 --no-pager -o cat
-o cat drops the timestamp, hostname and program name. Useful when piping into another tool, and when a log line is long enough that the metadata pushes it off the screen.
Show output
retrying upload, attempt 2 of 3
upload failed: no space left on device
Count how many entries match
journalctl -t deploy-agent -n 3 --no-pager -o cat | wc -l
journalctl has no counting flag of its own, so pipe it. Bound the query with -n first, or you count the whole journal.
Show output
3
Filtering by unit and by program
The filter you will use most: everything a single service logged, without the rest of the machine's noise.
Show one service's log
journalctl -u backup-sync -n 3 --no-pager -o cat
-u takes a unit name, with or without the .service suffix. This is the command to run the moment a service misbehaves.
Show output
backup-sync.service: Main process exited, code=exited, status=1/FAILURE
backup-sync.service: Failed with result 'exit-code'.
Failed to start backup-sync.service - Nightly backup sync.
See what a service printed, not just what systemd said about it
journalctl -u backup-sync -n 5 --no-pager -o cat | head -2
A unit's log mixes systemd's own notices with whatever the program wrote to stdout and stderr. The program's own words are usually the ones that explain the failure.
Show output
connecting to backup.example.com
cannot reach backup.example.com: connection refused
Follow two services at once
journalctl -u backup-sync -u cron -n 3 --no-pager -o cat
-u repeats, and the entries are interleaved in time order. Worth knowing when a failure spans a service and whatever triggered it.
Show output
backup-sync.service: Main process exited, code=exited, status=1/FAILURE
backup-sync.service: Failed with result 'exit-code'.
Failed to start backup-sync.service - Nightly backup sync.
Filter by the program's name rather than its unit
journalctl -t deploy-agent -n 3 --no-pager -o cat
-t (--identifier) matches the syslog identifier - the name the program logged under, which is not always the unit's. Use it for anything logging through logger or systemd-cat.
Show output
deploy 4a91c2 finished in 4.2s
retrying upload, attempt 2 of 3
upload failed: no space left on device
Search the messages for a pattern
journalctl -t deploy-agent -g "no space" -n 3 --no-pager -o cat
-g (--grep) matches the message text with a case-insensitive regular expression, and unlike piping to grep it filters before the formatting, so -n still counts matches rather than lines.
Show output
upload failed: no space left on device
Show only entries from the current boot
journalctl -b -t deploy-agent -n 1 --no-pager -o cat
-b limits the query to this boot, which is the difference between 'the service failed' and 'the service failed before the last reboot'. -b -1 is the previous boot.
Show output
upload failed: no space left on device
Filtering by priority
-p matches the level you name and everything more severe, so -p warning includes errors too.
Show only errors
journalctl -p err -t deploy-agent -n 3 --no-pager -o cat
err and worse. The first filter to apply on a machine you have just been told is broken.
Show output
upload failed: no space left on device
Include warnings as well as errors
journalctl -p warning -t deploy-agent -n 3 --no-pager -o cat
Widening from err to warning picks up the retry that preceded the failure, which is often where the actual cause is.
Show output
retrying upload, attempt 2 of 3
upload failed: no space left on device
Ask for one priority exactly
journalctl -p warning..warning -t deploy-agent -n 3 --no-pager -o cat
A range with the same level at both ends is the only way to get a single priority: -p warning alone would include the errors above it.
Show output
retrying upload, attempt 2 of 3
Use a numeric priority instead of a name
journalctl -p 3 -t deploy-agent -n 3 --no-pager -o cat
The syslog numbers, 0 (emerg) to 7 (debug), work anywhere the names do. 3 is err.
Show output
upload failed: no space left on device
Find every error on the machine since it booted
journalctl -b -p err -n 3 --no-pager -o cat
The one-line health check. On a machine that is behaving, this prints nothing at all.
Show output
Failed to start backup-sync.service - Nightly backup sync.
upload failed: no space left on device
Filtering by time
--since and --until accept YYYY-MM-DD HH:MM:SS, plain English like yesterday, and relative offsets like -2h.
Show entries from the last few minutes
journalctl --since "5 minutes ago" -t deploy-agent -n 3 --no-pager -o cat
Relative times are the quickest way to scope a query to whatever just happened.
Show output
deploy 4a91c2 finished in 4.2s
retrying upload, attempt 2 of 3
upload failed: no space left on device
Use a shorthand offset
journalctl --since -10m -t deploy-agent -n 2 --no-pager -o cat
-10m means ten minutes ago; -2h and -1d work the same way. Quicker to type than the quoted form and identical in effect.
Show output
retrying upload, attempt 2 of 3
upload failed: no space left on device
Bound a query at both ends
journalctl --since "1 hour ago" --until "now" -t deploy-agent -n 1 --no-pager -o cat
--until closes the window, which matters when you are looking at an incident that ended rather than one still running.
Show output
upload failed: no space left on device
Show everything since yesterday
journalctl --since yesterday -p err -n 2 --no-pager -o cat
yesterday, today and tomorrow are understood directly. Combined with -p err, this is a reasonable morning check on a server you look after.
Show output
Failed to start backup-sync.service - Nightly backup sync.
upload failed: no space left on device
Output formats
-o changes how each entry is printed, from the message alone to every field the journal stored.
Show full ISO timestamps
journalctl -t deploy-agent -n 2 --no-pager -o short-iso
The default format omits the year and the timezone. short-iso includes both, which matters when you are correlating against logs from another machine.
Show output
Your output will differ: the date, time and process ids are from the machine that produced this
2026-08-17T12:45:39+00:00 deb1 deploy-agent[2798]: retrying upload, attempt 2 of 3
2026-08-17T12:45:39+00:00 deb1 deploy-agent[2801]: upload failed: no space left on device
Print entries as JSON for another program to read
journalctl -t deploy-agent -n 1 --no-pager -o json | grep -o '"MESSAGE":"[^"]*"'
-o json emits one object per entry with every field the journal holds - cursors, boot ids, the lot. Extract the field you want rather than reading it whole. grep -o is fine for one field on one line; jq is what you want the moment the query gets more interesting than that.
Show output
"MESSAGE":"upload failed: no space left on device"
See every field stored with an entry
journalctl -t deploy-agent -n 1 --no-pager -o verbose | grep -E "^ +(PRIORITY|SYSLOG_IDENTIFIER|MESSAGE)="
-o verbose shows the metadata the journal indexed, which is what makes filtering possible. Any of these field names can be matched directly as FIELD=value.
Show output
PRIORITY=3
SYSLOG_IDENTIFIER=deploy-agent
MESSAGE=upload failed: no space left on device
Match on a field directly
journalctl PRIORITY=3 -t deploy-agent -n 2 --no-pager -o cat
A bare FIELD=value argument filters on the indexed metadata, which is how -p and -u work underneath. Useful for fields with no flag of their own.
Show output
upload failed: no space left on device
Following the log as it happens
-f behaves like tail -f, printing new entries until you stop it with Ctrl-C.
Watch new entries arrive
journalctl -f
Prints the last few entries, then blocks and prints each new one as it is written. The command to leave running in a second terminal while reproducing a problem.
Watch one service
journalctl -fu backup-sync
-f and -u combine into the single most useful log command on a systemd machine: restart the service in one terminal, watch exactly what it says in the other.
Watch for errors across the whole machine
journalctl -f -p err
Quiet until something goes wrong, which is what you want on a screen you are half-watching during a deployment.
Disk usage and cleaning up
The journal is capped and rotates itself, but the cap is generous and worth checking on a small disk.
See how much disk the journal is using
journalctl --disk-usage
Counts the active and archived journal files together. On a long-lived server this is often larger than people expect.
Show output
Your output will differ: the size depends on how long the machine has been up and how much it logs
Archived and active journals take up 8M in the file system.
Trim the journal to a size
journalctl --vacuum-size=200M
Deletes archived journal files, oldest first, until the total fits. It never touches the active file, so the figure afterwards can still exceed what you asked for.
Delete entries older than a given age
journalctl --vacuum-time=30d
The retention policy most people want. Set it permanently with MaxRetentionSec= in /etc/systemd/journald.conf rather than running this by hand.
Check a journal file for corruption
journalctl --verify | tail -1
Verifies the checksums journald writes as it goes. Worth running after a machine has lost power mid-write and the logs look truncated.
Show output
Your output will differ: the file names contain the machine and boot ids, and the totals depend on your journal
PASS: /var/log/journal/14b8ba58690b4c419b1d03c1ec547919/system.journal
Boots and the kernel
The journal keeps entries across reboots, so it can answer what happened before a machine went down.
List the boots the journal still holds
journalctl --list-boots --no-pager
Each line is one boot, with the index to pass to -b. A machine that reboots unexpectedly leaves its evidence in the boot before the current one.
Show output
Your output will differ: the boot id, the dates and how many boots are listed are all yours; a container that has booted once shows a single row
IDX BOOT ID FIRST ENTRY LAST ENTRY
0 44e5960633ed42fc9397fef921bdbe35 Mon 2026-08-17 12:45:42 UTC Mon 2026-08-17 12:45:42 UTC
Read the previous boot's log
journalctl -b -1 -p err --no-pager
-b -1 is the boot before this one, -b -2 the one before that. The first place to look after an unexplained reboot, and empty here because this machine has only booted once.
Show kernel messages only
journalctl -k -b
The equivalent of dmesg, but with the journal's filtering and its persistence across reboots. A container has no kernel of its own, so this is a command to run on a real machine.
In scripts
Bound the query, choose a format without metadata, and journalctl composes like any other command.
Fail a check when a service logged an error
if journalctl -u backup-sync -p err -n 1 --no-pager -o cat | grep -q .; then
echo "backup-sync logged an error"
fi
-o cat with grep -q . is the readable way to ask 'did anything match?', since journalctl exits 0 whether or not it found entries.
Show output
backup-sync logged an error
Group entries by priority
journalctl -t deploy-agent -n 20 --no-pager -o json |
grep -o '"PRIORITY":"[^"]*"' | sort | uniq -c
The JSON output gives one object per line, so ordinary text tools can group it. Swap PRIORITY for _SYSTEMD_UNIT to find which service is responsible for a flood of errors. jq reads that one-object-per-line shape natively, without the quoting games.
Show output
1 "PRIORITY":"3"
1 "PRIORITY":"4"
1 "PRIORITY":"6"
Send a line to the journal from a script
echo "nightly job finished" | systemd-cat -t nightly-job -p info
sleep 0.3
journalctl -t nightly-job -n 1 --no-pager -o cat
systemd-cat pipes a program's output into the journal with a tag of your choosing, so a cron job or a script logs the same way a service does. logger does the same for syslog.
Show output
nightly job finished