Linux Logs: journalctl and /var/log Complete Guide

Tested on: Ubuntu 26.04 LTS · Debian 12 · Fedora 44 · Arch Linux · openSUSE Tumbleweed — Last updated: June 2026
Linux logs are the first place to look when something breaks. On modern systemd-based systems, journalctl is the primary interface for reading structured log data — covering services, the kernel, boot sequences, and every unit systemd manages. This guide covers journalctl thoroughly, explains the traditional /var/log files that still matter, and walks through real debugging workflows you'll use regularly.
Prerequisites
- A systemd-based Linux distribution (Ubuntu, Debian, Fedora, Arch, openSUSE, RHEL/Rocky, etc.)
- A user account with
sudoaccess — some journal queries require root to see logs from all users and system services - Basic terminal familiarity: piping,
grep,less
To confirm systemd is running on your system:
systemctl --versionsystemd 255 (255.4-1ubuntu8)
+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT ...The Two Log Systems on Modern Linux
Most Linux systems in 2026 run two parallel log systems. Understanding why both exist prevents confusion when logs appear in one place but not the other.
| System | Tool | Storage | What it covers |
|---|---|---|---|
| systemd journal | journalctl | /run/log/journal or /var/log/journal | All systemd units, kernel, boot |
| syslog (rsyslog / syslog-ng) | tail, less, grep | /var/log/*.log | Traditional text log files |
Both systems coexist on most distros. The journal captures everything systemd manages; rsyslog may forward a subset of those messages to /var/log/syslog (Debian/Ubuntu) or /var/log/messages (Fedora/RHEL) for compatibility. On minimal or container-optimized installs, only the journal may be present. Application-specific logs — Nginx, PostgreSQL, MySQL — write directly to their own files under /var/log/ regardless of which daemon is running.
Persistent vs. Volatile Journal Storage
Before querying logs across reboots, you need to know whether your journal is persistent. By default, some distributions store the journal in RAM (/run/log/journal), which means it disappears on reboot. Others write to disk immediately.
# Check where the journal is currently stored
journalctl --disk-usageArchived and active journals take up 1.2G in the file system.If the path shown is /run/log/journal, logs are volatile. To make them persistent:
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
# Confirm
ls /var/log/journal/Once persistent storage is active, logs survive reboots and you can query previous boots with -b -1, -b -2, etc.
journalctl: Essential Commands
View All Logs
# All logs, oldest first — opens in less (q to quit, Space to page, G to jump to end)
journalctl
# All logs, newest first
journalctl -r
# Follow live output (like tail -f)
journalctl -f
# Show the last N lines
journalctl -n 100Filter by Time
Time filtering is one of journalctl's strongest features. Natural language expressions work alongside ISO timestamps.
# Logs since last boot
journalctl -b
# Logs since today midnight
journalctl --since today
# Logs from a specific timestamp
journalctl --since "2026-06-09 10:00:00"
# Logs between two times
journalctl --since "09:00" --until "10:30"
# Relative time expressions
journalctl --since "1 hour ago"
journalctl --since "30 min ago"
journalctl --since yesterdayFilter by Service (Unit)
The -u flag is the most common filter in day-to-day use. You can combine multiple units in a single command.
# Logs for a specific service
journalctl -u nginx
journalctl -u sshd
journalctl -u docker
# Follow a service live
journalctl -u nginx -f
# Multiple services at once — output is interleaved with timestamps
journalctl -u nginx -u php8.3-fpm
# Service logs since last boot only
journalctl -u nginx -b
# Last 50 lines for a service
journalctl -u postgresql -n 50Filter by Priority (Severity)
Syslog-compatible priority levels run from 0 (emergency) to 7 (debug). Specifying a level shows that level and all levels more severe.
# Priority levels:
# 0 emerg — system is unusable
# 1 alert — action must be taken immediately
# 2 crit — critical conditions
# 3 err — error conditions
# 4 warning — warning conditions
# 5 notice — normal but significant
# 6 info — informational
# 7 debug — debug-level messages
# Show errors and above (err, crit, alert, emerg)
journalctl -p err
# Warnings and above across all services since boot
journalctl -p warning -b
# Critical messages only, current boot
journalctl -p crit -b
# Errors from a specific service today
journalctl -u nginx -p err --since todayFilter by Process, PID, or User
# By executable path — shows all log entries from that binary
journalctl /usr/sbin/nginx
journalctl /usr/bin/python3
# By specific PID
journalctl _PID=4821
# By user ID (UID 1000 = first non-root user on most systems)
journalctl _UID=1000
# By system user (e.g., www-data, postgres)
journalctl _UID=$(id -u www-data)Kernel Messages
# Only kernel messages — equivalent to dmesg but with journal filtering
journalctl -k
# Kernel messages since last boot
journalctl -k -b
# Follow kernel messages live (useful during hardware troubleshooting)
journalctl -k -f
# Kernel errors since boot
journalctl -k -b -p errBoot Log History
When the journal is persistent, you can query any previous boot — invaluable after a crash or unexpected reboot.
# List all recorded boots with timestamps
journalctl --list-boots-3 a1f2e3d4... Mon 2026-06-06 08:12:01 UTC—Mon 2026-06-06 18:44:22 UTC
-2 b5c6d7e8... Tue 2026-06-07 09:00:05 UTC—Tue 2026-06-07 17:55:10 UTC
-1 f9a0b1c2... Wed 2026-06-08 08:30:00 UTC—Wed 2026-06-08 22:14:33 UTC
0 d3e4f5a6... Thu 2026-06-09 07:45:11 UTC—present# Current boot
journalctl -b 0
# Previous boot (the one before current)
journalctl -b -1
# Two boots ago
journalctl -b -2
# Errors from previous boot — essential after a crash
journalctl -b -1 -p err
# Precise timestamps (useful for correlating with application logs)
journalctl -b -o short-preciseOutput Formats
journalctl supports several output modes. JSON output is particularly useful when feeding logs into aggregation tools like Elasticsearch, Loki, or a custom parser.
# Default output (short format)
journalctl -u sshd -n 5
# One line per entry, minimal metadata
journalctl -u sshd -o short
# Message text only — no timestamps, no hostname
journalctl -u sshd -o cat
# JSON, one object per line (machine-readable)
journalctl -u sshd -o json -n 5
# Pretty-printed JSON (human-readable, useful for inspection)
journalctl -u sshd -o json-pretty -n 2
# Verbose — shows all journal fields for each entry
journalctl -u sshd -o verbose -n 3Example JSON output for a single entry:
journalctl -u sshd -o json-pretty -n 1{
"__REALTIME_TIMESTAMP" : "1749456311000000",
"_HOSTNAME" : "webserver01",
"SYSLOG_IDENTIFIER" : "sshd",
"_PID" : "1842",
"MESSAGE" : "Accepted publickey for deploy from 203.0.113.45 port 52341",
"PRIORITY" : "6",
"_SYSTEMD_UNIT" : "sshd.service"
}Searching Log Content
journalctl has no built-in text search — pipe to grep. For large journals, combine time and unit filters first to reduce the data volume before searching.
# Search nginx logs for 404s today
journalctl -u nginx --since today | grep " 404 "
# Case-insensitive error search across all logs since boot
journalctl -b | grep -i "error"
# Search with context lines (2 lines before and after each match)
journalctl -b | grep -i "fail" -B 2 -A 2
# Count occurrences
journalctl -u sshd --since today | grep "Failed password" | wc -l
# Search for multiple patterns
journalctl -b -p err | grep -E "nginx|php|mysql"Journal Disk Usage and Maintenance
Left unconfigured, a busy server's journal can consume several gigabytes. The vacuum commands clean old data without restarting the journal daemon.
# Check current disk usage
journalctl --disk-usage
# Delete journal files older than 2 weeks
sudo journalctl --vacuum-time=2weeks
# Keep only the most recent 500 MB of logs
sudo journalctl --vacuum-size=500M
# Keep only the last 5 boots worth of journal files
sudo journalctl --vacuum-files=5Configure Persistent Retention in journald.conf
For long-term control, edit the journal configuration file. Changes require a daemon restart to take effect.
sudo vim /etc/systemd/journald.conf[Journal]
# Store logs persistently on disk (auto = persistent if /var/log/journal exists)
Storage=persistent
# Maximum disk space the journal may use
SystemMaxUse=1G
# Minimum free disk space to leave on the filesystem
SystemKeepFree=2G
# Maximum age of log entries
MaxRetentionSec=4week
# Maximum size of a single journal file before rotation
SystemMaxFileSize=100M
# Compress journal data
Compress=yes
# Forward logs to syslog (set to no if you don't use rsyslog)
ForwardToSyslog=yes# Apply changes
sudo systemctl restart systemd-journaldTraditional Log Files in /var/log
The /var/log directory remains relevant even on fully systemd-managed systems. Application daemons write here directly, and rsyslog forwards journal messages to text files for tools that expect them.
| File | Contents | Distros |
|---|---|---|
/var/log/syslog | General system messages | Debian, Ubuntu |
/var/log/messages | General system messages | Fedora, RHEL, Arch |
/var/log/auth.log | Authentication, sudo, SSH | Debian, Ubuntu |
/var/log/secure | Authentication events | Fedora, RHEL |
/var/log/kern.log | Kernel messages | Debian, Ubuntu |
/var/log/dpkg.log | Package install/remove history | Debian, Ubuntu |
/var/log/dnf.log | Package history | Fedora, RHEL |
/var/log/nginx/ | Access and error logs | All (when Nginx installed) |
/var/log/apache2/ | Access and error logs | Debian/Ubuntu (Apache) |
/var/log/postgresql/ | Database logs | All (when PostgreSQL installed) |
/var/log/mysql/ | MySQL error log | All (when MySQL installed) |
