journalctl

Read and filter the systemd journal

Updated 2026-08-17

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.
30 outputs, collapsed by default

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.

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