Skip to content
DevOps devops linux-admin 6 min read

Viewing Logs with journalctl

When something breaks on a Linux server, the first question is always “what do the logs say?” On modern Ubuntu (22.04 and 24.04 LTS), most of your answers live in the systemd journal — a single, structured log store that journalctl reads. Instead of hunting through dozens of plain-text files, you ask one tool to show you exactly the logs you want: a specific service, the last 10 minutes, only errors, or only this boot. This page teaches you to read that journal like a pro so you can debug failed services fast.

First, two terms. systemd is the program that starts and manages services (background programs) on Ubuntu — it is the very first thing that runs after the kernel boots. The journal is the log database that systemd keeps, and journalctl (“journal control”) is the command you use to read it.

The systemd journal vs traditional /var/log files

For decades, Linux programs wrote logs as plain text into files under /var/log (a folder full of log files). A separate program called rsyslog sorted those messages into files like /var/log/syslog and /var/log/auth.log. That still happens on Ubuntu today, but systemd adds a second, smarter store: the binary journal, kept in /var/log/journal/ or /run/log/journal/.

The journal is “structured,” meaning every log line carries metadata fields — which service wrote it, the priority, the exact timestamp, the process ID — not just text. That is why journalctl can filter so precisely.

Featurejournalctl (systemd journal)/var/log text files
FormatBinary, structured fieldsPlain text
Filter by serviceBuilt in (-u)Manual grep
Filter by time / priorityBuilt in (--since, -p)Manual / awkward
Read withjournalctl onlycat, less, grep, tail
Survives rebootOnly if persistent storage enabledUsually yes
Best forServices, debugging, modern appsApp-specific logs (nginx access logs, etc.)

By default Ubuntu stores the journal in /run, which is RAM, so logs are wiped on every reboot. To keep them, run sudo mkdir -p /var/log/journal && sudo systemctl restart systemd-journald. After that the journal persists across reboots — essential for debugging a crash that happened before you logged in.

When to use journalctl: for anything managed by systemd (your app service, ssh, nginx via systemd, cron). When to use /var/log files instead: for application-specific logs that a program writes itself, like nginx’s /var/log/nginx/access.log — those do not go to the journal.

Reading logs for one service with -u

The single most useful flag is -u (short for “unit” — a systemd unit is a service, timer, or socket). It shows only the logs from one service.

journalctl -u nginx.service

You can drop the .service suffix; journalctl -u nginx works too. The output opens in a pager (a scrollable viewer) — press the down arrow to scroll and q to quit.

Output:

Jun 15 09:12:03 web01 systemd[1]: Starting nginx - high performance web server...
Jun 15 09:12:03 web01 systemd[1]: Started nginx - high performance web server.
Jun 15 14:31:55 web01 nginx[8123]: 2026/06/15 14:31:55 [error] connect() failed

Following logs live with -f

The -f flag means “follow.” It keeps the log open and prints new lines as they appear — exactly like tail -f on a text file. This is what you want while reproducing a bug or watching a deploy.

journalctl -u nginx -f

Press Ctrl + C to stop following. Combine it with -u to watch just one service, or run plain journalctl -f to watch everything on the system at once.

Filtering by time with —since and —until

--since and --until accept friendly phrases or exact timestamps, so you can zoom in on the moment something went wrong.

journalctl -u nginx --since "10 minutes ago"
journalctl -u ssh --since "2026-06-15 14:00" --until "2026-06-15 14:30"
journalctl --since today
journalctl --since yesterday --until "03:00"

When to use this: an alert fired at 2 PM — --since "1:55 PM" --until "2:05 PM" shows only the relevant window instead of pages of noise.

Filtering by priority with -p

Every log line has a priority (how serious it is), borrowed from the old syslog scale. -p shows only messages at that level or worse (more severe).

NumberNameMeaning
0emergSystem is unusable
1alertAct immediately
2critCritical condition
3errError
4warningWarning
5noticeNormal but notable
6infoInformational
7debugDebug detail
journalctl -p err
journalctl -u myapp -p warning --since today

The first command shows every error, critical, alert, and emergency message across the whole system. Lower number = more severe, and -p includes everything at that number and below.

Limiting to the current boot with -b

-b (“boot”) restricts logs to the current boot session — everything since the machine last started. This cuts out history from previous runs.

journalctl -b
journalctl -b -1
journalctl --list-boots

journalctl -b is the current boot, -b -1 is the previous boot, and --list-boots shows all stored boots (only useful if you enabled persistent storage). When to use this: a server rebooted unexpectedly and you want to see what it logged just before the crash — check -b -1.

Debugging a failed service step by step

Imagine your custom app service won’t start. Here is the exact workflow.

First, confirm the failure:

sudo systemctl status myapp.service

Output:

× myapp.service - My Web App
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
     Active: failed (Result: exit-code) since Mon 2026-06-15 14:40:11 UTC
   Main PID: 9211 (code=exited, status=203/EXEC)

status shows the last few lines, but to see the full picture, read the journal for that service since the last boot, errors only:

journalctl -u myapp -b -p err -e

The -e flag jumps to the end (the newest lines). Here you might find:

Output:

Jun 15 14:40:11 web01 systemd[1]: myapp.service: Failed to execute /usr/local/bin/myapp: No such file or directory
Jun 15 14:40:11 web01 systemd[1]: myapp.service: Main process exited, code=exited, status=203/EXEC

Status 203/EXEC plus “No such file or directory” tells you the binary path in the service file is wrong. Fix the ExecStart= line in the unit file, then reload and restart:

sudo systemctl daemon-reload
sudo systemctl restart myapp
journalctl -u myapp -f

Watch the live output to confirm it stays up.

Best Practices

  • Always pair -u <service> with -b and -p err when debugging — it strips the noise down to “what broke this boot.”
  • Enable persistent storage (/var/log/journal) on real servers so logs survive reboots; the default RAM storage loses them.
  • Use --since / --until with real timestamps to match the exact moment an alert fired, instead of scrolling.
  • Use journalctl -f while reproducing a bug rather than reading after the fact — you see cause and effect in real time.
  • Cap journal disk usage with SystemMaxUse= in /etc/systemd/journald.conf (e.g. SystemMaxUse=500M) so logs never fill the disk.
  • Remember application logs (nginx access logs, PostgreSQL logs) usually stay in /var/log — check there when the journal looks empty.
Last updated June 15, 2026
Was this helpful?