Every Linux system keeps a diary. Services start, fail, retry, and crash, and each event gets written down somewhere. The trouble is that "somewhere" used to mean a dozen scattered text files in /var/log, each with its own format and rotation schedule. On any modern distribution running systemd, there is one place that collects almost all of it: the systemd journal. And journalctl is the tool that reads it.

If you have ever stared at a failed service and wondered what actually went wrong, this guide takes you from zero to reading logs like an operator: filtering by service, boot, and time, following logs live, and finding the one error line that matters.

What journalctl actually reads

journalctl queries the journal kept by systemd-journald. The kernel, systemd itself, and every service it starts send their messages there. Each entry is a structured record: timestamp, the unit that produced it, process name and PID, and a severity level. That structure is what makes filtering fast and precise.

On most systems the journal lives in /var/log/journal/ (persistent across reboots) or /run/log/journal/ (volatile, wiped on reboot). If yours only keeps the volatile one, logs from the previous boot are gone after a restart, which is exactly when you usually need them. Fix that first:

BASH
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald

First look: the basics

Run it bare and you get the whole journal, oldest first, inside a pager:

BASH
sudo journalctl

On a busy machine that is thousands of lines, so treat the bare command as a demo, not a workflow. The pager is less: q quits, G jumps to the end, /pattern searches inside the output.

A normal user only sees its own user services. To read the full system journal you need sudo, or membership in the systemd-journal group (sudo usermod -aG systemd-journal $USER, effective at next login).

Three flags cover most day-to-day reading:

BASH
journalctl -e          # jump straight to the newest entries
journalctl -n 50       # show only the last 50 lines
journalctl -f          # follow new entries as they arrive, like tail -f

The -f flag is the one you will reach for during an incident. Restart a service in one terminal, watch its log react in another.

Filter by service: the flag you will use most

When a service fails, the first question is always "what did that service say?" The -u flag scopes the journal to one systemd unit:

BASH
journalctl -u ssh.service
journalctl -u nginx.service -n 100
journalctl -u docker.service -f

Combine it with -f and you have a live window into exactly one service. Pair it with systemctl status, which shows the last few log lines itself: status tells you that it failed, journalctl -u tells you why.

If you recently hardened SSH, the auth trail lives here too. Checking failed logins after setting up SSH keys is a single query:

BASH
journalctl -u ssh.service --since "1 hour ago" | grep -i "failed password"

Filter by boot: find what happened before the crash

Every boot gets its own identity in the journal. List them with journalctl --list-boots, then scope your reading to one boot. -b means the current boot, -b -1 the previous one, -b -2 two boots back:

BASH
journalctl -b            # this boot only
journalctl -b -1         # the previous boot
journalctl -b -1 -p err  # errors from the previous boot only

That last command is the first thing to run after an unexplained reboot: it shows only what went wrong, without the noise of a healthy boot.

Filter by priority: cut the noise

Every entry carries a syslog severity, from emerg (0) down to debug (7). The -p flag keeps a level and everything more severe than it:

BASH
journalctl -p err -b      # errors and worse, this boot
journalctl -p warning -b  # warnings and worse, this boot

On a healthy desktop, -p err -b should be short. On a sick one, it is your triage list.

Filter by time: the incident window

When you know when something broke, time filters do the rest. They accept plain language and exact timestamps:

BASH
journalctl --since "1 hour ago"
journalctl --since "2026-11-07 09:00" --until "2026-11-07 09:30"
journalctl --since today --until "12:00"

The short forms -S and -U do the same thing. Time-boxing is the single highest-leverage habit in log work: instead of scrolling, you ask for the thirty minutes around the incident and read only that.

Search inside the messages

For a specific error string, -g searches message text with a regular expression, which is more precise than piping everything through grep:

BASH
journalctl -g "timed out" --since today
journalctl -u nginx.service -g "502|upstream"
journalctl -k -p err -b  # kernel errors this boot

A real troubleshooting run

Here is the sequence, in order, when a service misbehaves. It takes about a minute.

  1. Confirm the failure: systemctl status nginx.service
  2. Read its recent log: journalctl -u nginx.service -n 50 --no-pager
  3. If the failure started after a reboot, check the previous boot: journalctl -u nginx.service -b -1 -p err
  4. Follow it live while you restart: journalctl -u nginx.service -f, then sudo systemctl restart nginx.service in another terminal
  5. Widen only if needed: journalctl -p err -b to see whether something else broke at the same time

Notice the order: narrow first, widen later. Start with one unit, one boot, one time window, and expand only when the narrow view comes up empty.

If you are watching logs over SSH and the connection drops, run the follow session inside tmux so it survives the disconnect.

Keep the journal from eating your disk

The journal grows, and on a small VPS it can grow a lot. Check its size with journalctl --disk-usage, then cap it in /etc/systemd/journald.conf:

INI
SystemMaxUse=500M
SystemMaxFileSize=50M

Then restart the daemon with sudo systemctl restart systemd-journald. If the journal is already too big, reclaim space without deleting files by hand:

BASH
sudo journalctl --vacuum-size=200M
sudo journalctl --vacuum-time=2weeks

Common traps

"No entries" for a service that definitely ran. You are probably looking at the volatile journal, or the wrong boot. Check /var/log/journal/ exists, and try -b -1.

Everything is in the future or the past. A wrong system clock writes wrong timestamps. Fix the clock with timedatectl before trusting any time filter.

You see only your own logs. That is the permission model working as intended. Use sudo or join the systemd-journal group.

Logs from a firewall rule never appear. If you just configured UFW on Ubuntu, its blocked packets show up under journalctl -k -g UFW or journalctl -u ufw.service, not under a generic search.

The habit to keep

Logs are not something you read only when everything is on fire. Spend two minutes a week on journalctl -p err -b and journalctl --disk-usage, and the next incident starts with you already knowing what normal looks like. The tool is already installed, the journal is already recording, and the only thing missing is the habit of asking it questions.

Sources