Skip to content

Log Management

Reference: systemd journald Docs · Debian Wiki — syslog · journalctl(1)


Log Locations

On Debian 12 and 13 the systemd journal is the system log. rsyslog is no longer installed by default, so the classic text files /var/log/syslog, /var/log/auth.log and /var/log/kern.log simply don't exist on a fresh server. The journal is persistent (/var/log/journal/), so logs survive reboots.

What you want Debian 13 (journal) Old text file (only with rsyslog)
Everything journalctl /var/log/syslog
SSH logins / failures journalctl -u ssh /var/log/auth.log
sudo usage journalctl _COMM=sudo /var/log/auth.log
Kernel messages journalctl -k / dmesg /var/log/kern.log
Package installs /var/log/dpkg.log, /var/log/apt/history.log (same, these are still files)
unattended-upgrades runs /var/log/unattended-upgrades/ (same)
Container logs docker logs <container> /var/lib/docker/containers/<id>/*-json.log

Want the text files back?

sudo apt install rsyslog recreates /var/log/syslog and friends (it reads from the journal). Ubuntu still installs rsyslog by default, which is why most guides grep /var/log/auth.log. On Debian, translate those to the journalctl filters above. Last-login tools changed too: last/lastb/lastlog are gone in trixie; use wtmpdb last or lslogins --failed.


Reading Logs

# Follow everything live (like tail -f /var/log/syslog)
journalctl -f

# Follow SSH logins live (like tail -f /var/log/auth.log)
journalctl -u ssh -f

# Last 50 lines
journalctl -n 50

# Search within the log
journalctl -g "error"                 # --grep, regex
journalctl -u ssh -g "Failed|Invalid" # case-insensitive when the pattern is all lowercase

# Plain text files still work the usual way
less /var/log/apt/history.log
tail -f /var/log/dpkg.log

# View kernel ring buffer (hardware/boot messages)
sudo dmesg
sudo dmesg | tail -20
sudo dmesg -T        # with human-readable timestamps
journalctl -k -b -1  # kernel messages from the PREVIOUS boot (after a crash)

journalctl — systemd Journal

The journal is the primary log store on Debian. See also the Systemd page for more detail.

# All logs
journalctl

# Follow live
journalctl -f

# Last 100 lines
journalctl -n 100

# Logs for a specific service
journalctl -u nginx
journalctl -u docker -f

# Logs since last boot
journalctl -b

# Logs from a specific time window
journalctl --since "2024-01-15 09:00:00" --until "2024-01-15 10:00:00"
journalctl --since "1 hour ago"
journalctl --since today

# Filter by priority
journalctl -p err          # errors and above
journalctl -p warning      # warnings and above
journalctl -p debug        # everything

# Priority levels: emerg, alert, crit, err, warning, notice, info, debug

# Kernel messages only
journalctl -k

# Show full output (no truncation)
journalctl --no-pager

# Output as JSON (useful for log forwarding)
journalctl -u myservice -o json-pretty

Log Rotation — logrotate

logrotate automatically rotates, compresses, and removes old log files.

# Config files
/etc/logrotate.conf          # global defaults
/etc/logrotate.d/            # per-application configs

# Test a config (dry run)
sudo logrotate --debug /etc/logrotate.d/nginx

# Force run logrotate now
sudo logrotate -f /etc/logrotate.conf

Example Config

# /etc/logrotate.d/myapp
# (logrotate only allows comments on their own line, not after a directive)
/var/log/myapp/*.log {
    # rotate daily
    daily
    # don't error if log file missing
    missingok
    # keep 14 rotated logs
    rotate 14
    # gzip old logs
    compress
    # compress on second rotation (app may still write)
    delaycompress
    # don't rotate empty files
    notifempty
    # create new log with these perms/owner
    create 0640 myapp myapp
    postrotate
        systemctl reload myapp > /dev/null 2>&1 || true
    endscript
}

journald Configuration

# Config file
sudo nano /etc/systemd/journald.conf

# Key settings:
[Journal]
# Limit journal disk usage
SystemMaxUse=500M

# Keep logs for max 30 days
MaxRetentionSec=30day

# Max size per log file
SystemMaxFileSize=50M

# Persist journal across reboots (default is sometimes volatile)
Storage=persistent
# Apply changes
sudo systemctl restart systemd-journald

# Check current disk usage
journalctl --disk-usage

# Manual cleanup
sudo journalctl --vacuum-size=200M    # trim to under 200MB
sudo journalctl --vacuum-time=14d     # remove logs older than 14 days

Searching & Filtering Logs

# Context lines around a match: pipe to grep
journalctl -b | grep -A 3 -B 3 "Out of memory"
# -A 3 = 3 lines after match, -B 3 = 3 before, -C 3 = either side

# Time window + priority
journalctl --since "2026-01-15" --until "2026-01-16" -p err

# Count failed SSH logins
journalctl -u ssh --since today | grep -c "Failed password"

# Failed SSH attempts by source IP
journalctl -u ssh -g "Failed password" -o cat | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn

# sudo usage
journalctl _COMM=sudo

# OOM kills
journalctl -k -g "Out of memory|oom-kill"

# Search text logs that still exist as files
grep -r "connection refused" /var/log/

Forwarding Logs (Loki / Remote Syslog)

Docker Logging Driver → Loki

My servers keep Docker's default json-file driver with rotation (see Docker post-installation) and read logs with docker logs/Dozzle. If you do centralise logs, a Loki driver looks like this. Add to /etc/docker/daemon.json to send all container logs to Loki without changing compose files:

{
  "log-driver": "loki",
  "log-opts": {
    "loki-url": "http://localhost:3100/loki/api/v1/push",
    "loki-batch-size": "400",
    "loki-retries": "3",
    "loki-max-backoff": "800ms",
    "loki-timeout": "2s"
  }
}
# Install the Loki Docker driver plugin first
# (pin a version tag in production instead of :latest)
docker plugin install grafana/loki-docker-driver:latest \
    --alias loki \
    --grant-all-permissions

# Restart Docker to apply
sudo systemctl restart docker

Remote Syslog Forwarding (rsyslog)

Not installed by default on Debian: sudo apt install rsyslog first. It reads the journal and forwards it.

# Forward to remote syslog server
sudo nano /etc/rsyslog.d/90-forward.conf

# Add (one of):
*.* @@192.168.1.50:514    # TCP (@@)
*.* @192.168.1.50:514     # UDP (@)

Audit Log (auditd)

For tracking file access, user commands, or privilege escalation:

sudo apt install auditd

# Watch a file for reads/writes
sudo auditctl -w /etc/passwd -p rwa -k passwd-watch

# View audit log
sudo ausearch -k passwd-watch
sudo aureport --summary

Sources & further reading