Logrotate#

Why#

Logs live on tmpfs (Filesystem chapter), in bounded RAM, without rotation, a chatty service would eventually saturate its allocated quota. logrotate handles this rotation, but two services in this project have special needs beyond the generic case.

How#

nginx#

cat > /etc/logrotate.d/nginx <<'EOF'
/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    delaycompress
    compress
    notifempty
    create 0640 www-data www-data
    sharedscripts
    postrotate
        [ ! -f /var/run/nginx.pid ] || kill -HUP $(cat /var/run/nginx.pid)
    endscript
}
EOF

chrony#

cat > /etc/logrotate.d/chrony <<'EOF'
/var/log/chrony/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    postrotate
        /usr/bin/chronyc cyclelogs > /dev/null 2>&1 || true
        /usr/bin/chronyc trimlog   > /dev/null 2>&1 || true
    endscript
}
EOF

logrotate -d /etc/logrotate.d/nginx
logrotate -d /etc/logrotate.d/chrony

rotate 14 keeps two weeks of compressed logs, a reasonable window given /var/log’s tmpfs budget (256 MB, Filesystem chapter): these logs are lost on every reboot anyway, so this rotation isn’t meant for long-term archival, just a safety net between reboots. delaycompress delays compressing the most recently rotated file by one cycle: the process in question may need a brief moment after rotation to honor the postrotate signal and stop writing to the old file descriptor, compressing immediately would risk corrupting or losing the last few lines written during that window.

Pitfalls to avoid#

Never create a logrotate.d/audit configuration. auditd manages its own internal rotation via the max_log_file and num_logs directives in auditd.conf. External rotation by logrotate on top of auditd’s internal rotation would create double management of the same files, with a real risk of corrupting the audit daemon’s internal state (documented behavior in auditd.conf(8)). This is a deliberate exception, not an oversight.

  • Forgetting nginx’s postrotate. After rotation, nginx keeps writing to the old file (now renamed) until explicitly told to reopen its file descriptors, the SIGHUP sent to the master process does exactly that. Without it, new logs would silently keep going into the compressed/archived file.
  • Forgetting chronyc cyclelogs. Chrony has the same need as nginx, reopening its log files after rotation, but through its own command rather than a generic Unix signal.

Verify#

# Dry-run simulation without actual execution (-d = debug)
logrotate -d /etc/logrotate.d/nginx
logrotate -d /etc/logrotate.d/chrony

# Force an actual rotation to test
logrotate -f /etc/logrotate.d/nginx
ls -la /var/log/nginx/
# Should show a fresh .log file and a .log.1.gz (or .log.1 before delayed compression)