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:
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journaldFirst look: the basics
Run it bare and you get the whole journal, oldest first, inside a pager:
sudo journalctlOn 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:
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 -fThe -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:
journalctl -u ssh.service
journalctl -u nginx.service -n 100
journalctl -u docker.service -fCombine 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:
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:
journalctl -b # this boot only
journalctl -b -1 # the previous boot
journalctl -b -1 -p err # errors from the previous boot onlyThat 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:
journalctl -p err -b # errors and worse, this boot
journalctl -p warning -b # warnings and worse, this bootOn 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:
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:
journalctl -g "timed out" --since today
journalctl -u nginx.service -g "502|upstream"
journalctl -k -p err -b # kernel errors this bootA real troubleshooting run
Here is the sequence, in order, when a service misbehaves. It takes about a minute.
- Confirm the failure:
systemctl status nginx.service - Read its recent log:
journalctl -u nginx.service -n 50 --no-pager - If the failure started after a reboot, check the previous boot:
journalctl -u nginx.service -b -1 -p err - Follow it live while you restart:
journalctl -u nginx.service -f, thensudo systemctl restart nginx.servicein another terminal - Widen only if needed:
journalctl -p err -bto 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:
SystemMaxUse=500M
SystemMaxFileSize=50MThen restart the daemon with sudo systemctl restart systemd-journald. If the journal is already too big, reclaim space without deleting files by hand:
sudo journalctl --vacuum-size=200M
sudo journalctl --vacuum-time=2weeksCommon 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.


