Skip to content

Journal and Logging | Linux - Wyatt's Notes

systemd-journald is the central logging daemon in systemd-based systems. It collects log messages From multiple sources and stores them in a structured, indexed binary format.

flowchart LR
A["Kernel<br />(dmesg)"] --> J["journald"]
B["Services<br />(stdout/stderr)"] --> J
C["syslog()"] --> J
D["Audit subsystem"] --> J
J --> S["/run/log/journal/<br />(volatile)"]
J --> P["/var/log/journal/<br />(persistent)"]
J --> F["Forward to<br />rsyslog/syslog"]
SourceDescription
stdout/stderrAll service output captured by systemd
Kernel messagesprintk() messages (equivalent to dmesg)
syslog()Traditional syslog calls
Audit eventsKernel audit subsystem
/dev/kmsgKernel log device
Internal journalJournal”s own diagnostic messages
Volatile (/run/log/journal/):
- Stored in tmpfs (RAM)
- Lost on reboot
- Default when /var/log/journal/ does not exist
- Size limited by RuntimeMaxUse (default: 10% of RAM)
Persistent (/var/log/journal/):
- Stored on disk
- Survives reboots
- Created with: mkdir -p /var/log/journal && systemd-tmpfiles --create --prefix /var/log/journal
- Size limited by SystemMaxUse (default: 10% of filesystem)
Terminal window
## Check current storage mode
journalctl --header | grep "Storage"
## Enable persistent storage
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
# Verify
ls -la /var/log/journal/
# drwxr-xr-x 2 root systemd-journal 4096 ...
Terminal window
# Show all journal entries (newest first)
journalctl
# Show boot log (current boot)
journalctl -b
# Show previous boot
journalctl -b -1
# Show specific boot by boot ID
journalctl --list-boots
journalctl -b <boot-id>
# Follow live output
journalctl -f
# Show kernel messages
journalctl -k
journalctl -k -f
# Show since a specific time
journalctl --since "2026-04-01"
journalctl --since "2026-04-01 09:00:00"
journalctl --since "2 hours ago"
journalctl --since yesterday
journalctl --since today
# Show until a specific time
journalctl --until "2026-04-01 10:00:00"
journalctl --since "1 hour ago" --until "now"
Terminal window
# By unit (service)
journalctl -u nginx
journalctl -u nginx -u postgresql # multiple units
# By PID
journalctl _PID=12345
# By executable
journalctl _COMM=sshd
# By systemd unit
journalctl _SYSTEMD_UNIT=nginx.service
# By priority (0=emerg, 1=alert, 2=crit, 3=err, 4=warning, 5=notice, 6=info, 7=debug)
journalctl -p err
journalctl -p warning..err # range
journalctl -p 3 # error
# By facility (syslog facility codes)
journalctl -f FACILITY=daemon
# By message content
journalctl --grep="connection refused"
journalctl --grep="OutOfMemory"
# By boot
journalctl -b 0 # current boot
journalctl -b -1 # previous boot
# By user session
journalctl _UID=1000
# Combine filters
journalctl -u nginx --since "1 hour ago" -p err
journalctl -u sshd _COMM=sshd --grep="Failed"
Terminal window
# Default (human-readable)
journalctl
# Short (default, but without legend)
journalctl -o short
# Verbose (show all fields)
journalctl -o verbose
# JSON (one entry per line)
journalctl -o json
# JSON pretty-printed
journalctl -o json-pretty
# Export format (for journalctl --import)
journalctl -o export
# Cat (show message only, no metadata)
journalctl -o cat
# With field values
journalctl -o with-unit
Terminal window
# Common journal fields
_SYSTEMD_UNIT # systemd unit name
_COMM # executable name
_PID # process ID
_UID # user ID
_GID # group ID
_HOSTNAME # hostname
_TRANSPORT # source: journal, syslog, kernel, etc.
_PRIORITY # syslog priority (0-7)
_MESSAGE # log message
_MESSAGE_ID # structured message ID
_EXE # executable path
_CMDLINE # command line
_SOURCE_REALTIME # timestamp (microseconds since epoch)
_BOOT_ID # unique boot identifier
_MACHINE_ID # unique machine identifier
# Show all fields for recent entries
journalctl -o verbose -n 5
# Filter by specific field
journalctl _HOSTNAME=server01
journalctl _TRANSPORT=syslog
Terminal window
# Find all failed service starts in the last 24 hours
journalctl --since yesterday -p err --grep="Failed"
# Track all SSH login attempts
journalctl -u sshd -o cat | grep -E "Accepted|Failed"
# Show nginx access logs with timestamps
journalctl -u nginx --since "1 hour ago" -o cat
# Find OOM killer events
journalctl -k --grep="Out of memory"
journalctl --grep="invoked oom-killer"
# Show the last 100 lines of a service's log
journalctl -u myapp -n 100
# Export logs for analysis
journalctl -u nginx --since "2026-04-01" -o json-pretty > nginx_april.json
# Pipe to jq for analysis
journalctl -u nginx --since "1 hour ago" -o json | \
jq -r 'select(.PRIORITY >= 4) | .__REALTIME_TIMESTAMP + " " + .MESSAGE'
/etc/systemd/journald.conf
[Journal]
# Storage mode: auto, volatile, persistent, none
Storage=auto
# Maximum disk space for persistent storage
SystemMaxUse=500M
# Minimum disk space to keep (before vacuuming)
SystemKeepFree=1G
# Maximum size of individual journal file
SystemMaxFileSize=50M
# Maximum time to keep journal files
MaxFileSec=1month
# Maximum disk space for volatile storage (in RAM)
RuntimeMaxUse=100M
RuntimeKeepFree=50M
RuntimeMaxFileSize=10M
# Compress journal files (default: yes)
Compress=yes
# Split journal files by UID (one per user)
SplitMode=uid
# Forward to traditional syslog daemon
ForwardToSyslog=yes
# Forward to wall (broadcast to logged-in users)
ForwardToWall=no
# Maximum rate of messages from a single service
RateLimitIntervalSec=30s
RateLimitBurst=10000
# Line rate limit (per-service)
LineRateLimitIntervalSec=30s
LineRateLimitBurst=1000
# File sealing (prevent tampering)
Seal=yes
# ReadKMsg (kernel messages)
ReadKMsg=yes
# TTYPath (console output)
TTYPath=/dev/console
Terminal window
# After changing configuration
systemctl restart systemd-journald
# Verify configuration
journalctl --header

systemd-tmpfiles manages temporary files and directories, including journal file rotation.

/usr/lib/tmpfiles.d/systemd.conf
# Systemd's built-in journal cleanup
# Automatically cleans up journal files based on SystemMaxUse/SystemKeepFree
# Manual vacuum
journalctl --vacuum-size=500M # keep at most 500M
journalctl --vacuum-time=7d # keep at most 7 days
journalctl --vacuum-files=10 # keep at most 10 journal files
# Check disk usage
journalctl --disk-usage

logrotate is the traditional log rotation tool, still widely used for application-specific logs.

/etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 nginx adm
sharedscripts
postrotate
[ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid)
endscript
}
/etc/logrotate.d/myapp
/var/log/myapp/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0644 myapp myapp
size 100M
maxsize 200M
dateext
dateformat -%Y%m%d
}
Terminal window
# Test configuration
logrotate -d /etc/logrotate.conf # debug mode (dry run)
# Force rotation
logrotate -f /etc/logrotate.conf
# Verify a specific config
logrotate -d /etc/logrotate.d/nginx

Processes are programs in execution, each with its own memory space and priority. Systemd manages the lifecycle of services, starting them at boot and restarting them if they fail. Understanding process states (running, sleeping, stopped, zombie) helps you diagnose why a service is not responding. Signals like SIGTERM and SIGKILL provide graceful and forceful ways to control processes.