A field-reference for reading, filtering, and rotating logs on Debian-family (Ubuntu, Debian, Mint) and Red Hat-family (RHEL, CentOS, Rocky, Fedora, Alma) systems — built for daily use at the terminal.
Almost every distro logs through one of two pipelines, and most modern ones run both together.
Applications and the kernel send messages to a syslog daemon, which writes plain-text lines into flat files under /var/log/, routed by "facility" (auth, cron, kern, mail…) and "severity" (emerg → debug). This is the traditional model, still dominant on Debian/Ubuntu.
systemd's own logging service captures stdout/stderr from every unit plus kernel and auth events into a binary, indexed journal. Queried with journalctl. Default and primary on RHEL/CentOS/Fedora; also present alongside rsyslog on Debian/Ubuntu.
journalctl first. If it's Debian/Ubuntu, reach for /var/log/syslog and friends first — but both families have both tools available.File names and even their existence vary by distro. Table below shows the equivalent log for each family.
| Purpose | DEBIAN / UBUNTU | RHEL / CENTOS / FEDORA |
|---|---|---|
| All-purpose system log | /var/log/syslog | /var/log/messages |
| Authentication / sudo / SSH logins | /var/log/auth.log | /var/log/secure |
| Kernel ring buffer messages | /var/log/kern.log | journalctl -k (or /var/log/dmesg) |
| Boot messages | /var/log/boot.log | /var/log/boot.log |
| Cron job output | /var/log/cron.log (or in syslog) | /var/log/cron |
| Package manager history | /var/log/apt/history.log | /var/log/yum.log & /var/log/dnf.log |
| Mail server | /var/log/mail.log | /var/log/maillog |
| X11 / display | /var/log/Xorg.0.log | /var/log/Xorg.0.log |
| Web server (Apache) | /var/log/apache2/{access,error}.log | /var/log/httpd/{access,error}_log |
| Nginx | /var/log/nginx/{access,error}.log | /var/log/nginx/{access,error}.log |
| Database (MySQL/MariaDB) | /var/log/mysql/error.log | /var/log/mariadb/mariadb.log |
| Firewall (UFW / firewalld) | /var/log/ufw.log | journalctl -u firewalld |
| Login/logout & failed logins | /var/log/wtmp, /var/log/btmp (via last, lastb) | /var/log/wtmp, /var/log/btmp (via last, lastb) |
| systemd journal (both families) | /var/log/journal/ (persistent, if enabled) | /run/log/journal/ (volatile, default) | |
Step 1 — Quick look at the end of a file (most recent entries):
tail -n 100 /var/log/syslog # last 100 lines
tail -f /var/log/syslog # follow in real time, like watching it live
tail -f /var/log/nginx/error.log -n 50 # start with last 50, then followStep 2 — Scroll through a full file (searchable, paginated):
less /var/log/auth.log
# inside less: /pattern = search forward n = next match N = prev match G = end g = start q = quitStep 3 — journalctl (systemd journal, RHEL default / also on Debian):
journalctl # entire journal, oldest first, pipe into less automatically
journalctl -e # jump straight to the end (newest)
journalctl -f # follow live, like tail -f
journalctl -r # reverse order, newest first
journalctl -b # only since the current boot
journalctl -b -1 # only the previous boot
journalctl -k # kernel messages only (like dmesg)
journalctl -p err # priority filter: err and worseless/tail to read; use logrotate or the app's own tools to truncate/manage.Plain-text syslog files (rsyslog format: Mon DD HH:MM:SS):
# All lines from a specific calendar day
grep "Aug 8" /var/log/syslog # note: two spaces if day < 10 (syslog pads with space)
# A specific date + hour
grep "Aug 8 14:" /var/log/syslog
# A precise time range using awk (14:00:00 to 14:30:00)
awk '$3 >= "14:00:00" && $3 <= "14:30:00"' /var/log/syslog
# Today only, dynamically
grep "$(date '+%b %e')" /var/log/syslogISO-timestamped logs (Nginx/Apache error logs, JSON logs, etc.):
grep "2026-08-08" /var/log/nginx/error.log
grep "2026-08-08T14" /var/log/app/app.log # hour 14journalctl (works on both families, most flexible — human-friendly time expressions):
journalctl --since "2026-08-08 14:00:00" --until "2026-08-08 14:30:00"
journalctl --since today
journalctl --since yesterday --until today
journalctl --since "1 hour ago"
journalctl --since "-15 min""2 hours ago", yesterday) — no regex needed. For flat files, always confirm the exact timestamp format first with head -n 3 file.log.journalctl by systemd unit — cleanest method on both families:
journalctl -u sshd # everything from the ssh daemon
journalctl -u nginx -f # follow nginx live
journalctl -u docker --since today
journalctl -u cron -u sshd # multiple units at once
systemctl list-units --type=service # find exact unit names firstFlat-file syslog by process tag (the bracketed name in each line):
grep 'sshd\[' /var/log/auth.log # Debian: auth events tagged sshd[pid]
grep 'CRON\[' /var/log/syslog
grep 'sshd' /var/log/secure # RHEL equivalentPer-app logs (when the service writes its own file instead of syslog/journal):
tail -f /var/log/nginx/error.log
tail -f /var/log/apache2/error.log # Debian
tail -f /var/log/httpd/error_log # RHEL (single _log, no .log)Flat files — keyword grep (case-insensitive, whole words):
grep -i "error" /var/log/syslog
grep -Ei "error|fail|critical|denied" /var/log/syslog
grep -i "error" /var/log/syslog -A 3 -B 1 # show 1 line before / 3 lines after for context
grep -c -i "error" /var/log/syslog # just count matches
grep -i "error" /var/log/*.log # across every rotated/plain log file
zgrep -i "error" /var/log/syslog.2.gz # search inside compressed rotated logsjournalctl — filter by real priority level (much more reliable than keyword grep):
journalctl -p err # err and everything worse (crit, alert, emerg)
journalctl -p warning # warning and worse
journalctl -p 3 # numeric priority also works (3 = err)
journalctl -p err -b # errors since this boot only
journalctl -p err --since "1 hour ago"Failed services / units at a glance:
systemctl --failed # list every unit currently in a failed state
systemctl status nginx # status + last log lines for one unitlogrotate prevents logs from growing forever. It runs once a day (usually via cron or a systemd timer), reads config files, and for each matched log decides whether to rotate it.
Core directives you'll see in every config:
| Directive | Meaning |
|---|---|
daily / weekly / monthly | How often to consider rotating |
rotate N | Keep N old copies before deleting the oldest |
compress | Gzip rotated files (delayed by one cycle if delaycompress also set) |
size 100M | Force rotation once file exceeds this size (overrides daily/weekly) |
missingok | Don't error if the log file is missing |
notifempty | Skip rotation if the log is empty |
create 0640 www-data adm | Recreate an empty log file with these perms/owner after rotating |
postrotate … endscript | Shell commands run after rotation — typically to signal the app (reload/HUP) |
sharedscripts | Run postrotate once total, not once per matched file |
dateext | Name rotated files with a date suffix (e.g. syslog-20260808) instead of .1, .2 |
postrotate tells the app to close and reopen its log (e.g. systemctl reload nginx) so it starts writing to the new file. systemd-journald doesn't need this — the journal manages its own rotation via SystemMaxUse= in journald.conf.Step 1 — Confirm logrotate is installed and running (it is by default via cron):
dpkg -l | grep logrotate
cat /etc/cron.daily/logrotate # the daily trigger scriptStep 2 — Create a new rule for a custom app, e.g. /var/log/myapp/app.log:
sudo nano /etc/logrotate.d/myapp/var/log/myapp/app.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 myapp adm
postrotate
systemctl reload myapp > /dev/null 2>&1 || true
endscript
}Step 3 — Validate the config before trusting it:
sudo logrotate -d /etc/logrotate.d/myapp # -d = dry run, prints what WOULD happen
sudo logrotate -f /etc/logrotate.d/myapp # -f = force an actual rotation right now
cat /var/lib/logrotate/status # see last-rotated timestampsStep 1 — Confirm logrotate is installed and check its trigger mechanism (systemd timer on modern RHEL, not cron):
rpm -q logrotate
systemctl list-timers | grep logrotate # RHEL 8+/9 uses logrotate.timer
systemctl status logrotate.timerStep 2 — Create the rule (identical syntax to Debian — logrotate itself is the same tool):
sudo vi /etc/logrotate.d/myapp/var/log/myapp/app.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 myapp adm
postrotate
systemctl reload myapp > /dev/null 2>&1 || true
endscript
}Step 3 — SELinux: restore correct context after creating a new log path (RHEL-family specific!):
sudo semanage fcontext -a -t var_log_t "/var/log/myapp(/.*)?"
sudo restorecon -Rv /var/log/myappStep 4 — Validate:
sudo logrotate -d /etc/logrotate.d/myapp # dry run
sudo logrotate -f /etc/logrotate.d/myapp # force rotate now/etc/systemd/journald.conf:
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=30daysudo systemctl restart systemd-journald.| Task | Command |
|---|---|
| Watch a log live | tail -f /var/log/syslog |
| Watch a service live | journalctl -u nginx -f |
| Errors in the last hour | journalctl -p err --since "1 hour ago" |
| Today's activity for one service | journalctl -u sshd --since today |
| Search across all rotated + live logs | zgrep -i "error" /var/log/syslog* |
| List currently failed services | systemctl --failed |
| Force-test a logrotate rule | sudo logrotate -d /etc/logrotate.d/<file> |
| Force an actual rotation now | sudo logrotate -f /etc/logrotate.d/<file> |
| Disk space used by logs | du -sh /var/log/* | sort -h |
| Journal disk usage | journalctl --disk-usage |
| Manually shrink the journal | sudo journalctl --vacuum-size=200M |