logrotate Basics - Keeping Logs Manageable with Automatic Rotation

logrotate Basics - Keeping Logs Manageable with Automatic Rotation

What Is logrotate?

logrotate is a Linux utility that automatically rotates, compresses, and deletes old log files on a schedule. Without it, log files under /var/log grow indefinitely until the disk fills up and services fail with No space left on device. It ships by default on Ubuntu and RHEL-based systems and runs daily via cron.

Quick Summary

  • Automatically rotates, compresses, and removes log files
  • Two-layer config: /etc/logrotate.conf (global defaults) and /etc/logrotate.d/ (per-app)
  • Always verify new configs with logrotate -d (dry run) before relying on them

Why Does Log Rotation Matter?

A busy nginx or MySQL server can generate gigabytes of logs per day. Without rotation:

  • Disk fills up silently overnight
  • Services crash with No space left on device
  • grep through a 10 GB file becomes painfully slow

Most distributions ship with /etc/cron.daily/logrotate already in place:

cat /etc/cron.daily/logrotate
#!/bin/sh
/usr/sbin/logrotate /etc/logrotate.conf

This runs once per day as root. Your job is to write the config files that tell it what to do.

How Is the Config Structured?

logrotate uses a two-layer config structure. /etc/logrotate.conf sets global defaults and includes everything under /etc/logrotate.d/ with a single include directive.

/etc/logrotate.conf        ← global defaults
/etc/logrotate.d/          ← per-app configs (where most editing happens)
├── nginx
├── mysql-server
├── rsyslog
└── ...

A typical /etc/logrotate.conf:

cat /etc/logrotate.conf
weekly
rotate 4
create
dateext
compress
include /etc/logrotate.d

weekly and rotate 4 are global defaults. Any per-app config in /etc/logrotate.d/ can override them.

The nginx package drops its own config automatically:

cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
        daily
        missingok
        rotate 52
        compress
        delaycompress
        notifempty
        create 0640 www-data adm
        sharedscripts
        postrotate
                if [ -f /run/nginx.pid ]; then
                        kill -USR1 `cat /run/nginx.pid`
                fi
        endscript
}

Key Directives

The directives you will use most often:

Directive Meaning
daily / weekly / monthly Rotation frequency
rotate N Number of old files to keep (e.g. rotate 7 keeps 7 generations)
size N Rotate when file exceeds this size (e.g. size 100M)
compress Compress rotated files with gzip
delaycompress Delay compression by one cycle
missingok Do not error if the log file is absent
notifempty Skip rotation if the file is empty
create MODE USER GROUP Create a fresh empty log file after rotation
dateext Append date suffix to rotated filenames
sharedscripts Run postrotate once even if multiple files matched the glob
postrotate / endscript Shell commands to run after rotation

Always pair compress with delaycompress. Right after rotation, the running process may still have the old file open. Compressing it immediately can cause write failures. delaycompress waits one cycle before gzipping.

Writing a Custom Config

Create /etc/logrotate.d/myapp for an application you deploy yourself:

/var/log/myapp/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data adm
    dateext
    sharedscripts
    postrotate
        systemctl reload myapp > /dev/null 2>&1 || true
    endscript
}

Key points:

  • Without sharedscripts, postrotate runs once per matched file — a reload storm if you have many logs
  • || true at the end of the postrotate command prevents logrotate from failing when the service is stopped
  • nginx uses kill -USR1, Apache uses apachectl graceful, systemd services use systemctl reload to reopen their log files

How to Verify and Debug

Always test new configs with -d (dry run) before relying on the daily cron job. The -d flag makes no changes to files or the status file.

logrotate -d /etc/logrotate.d/myapp
reading config file /etc/logrotate.d/myapp
Allocating hash table for state file, size 15360 B

Handling 1 logs

rotating pattern: /var/log/myapp/*.log  after 1 days (30 rotations)
...
rotating log /var/log/myapp/access.log, log->rotateCount is 30
...
not rotating log, since it was already rotated less than 1 days ago

If the output says "already rotated less than 1 days ago" and you want to test the full rotation anyway, force it with -f:

logrotate -f /etc/logrotate.d/myapp

-f ignores the timestamp in /var/lib/logrotate/status and rotates regardless. Running it multiple times in quick succession will overwrite generation files. Check the status file afterward.

Check current rotation status:

cat /var/lib/logrotate/status
logrotate state -- version 2
"/var/log/myapp/access.log" 2026-6-1-3:0:0
"/var/log/nginx/access.log" 2026-6-1-3:0:0

Common Mistakes

postrotate not taking effect

If the service keeps writing to the old (now rotated) file, the postrotate script either is not sending the correct signal, or sharedscripts is missing. Verify with lsof:

lsof | grep deleted | grep log

Any process listed here has the old file handle open. Fix the postrotate to send the appropriate signal or reload command.

dateext collision

Running logrotate -f twice on the same day causes a dateext filename collision. For test environments, add dateformat -%Y%m%d-%s (second-precision) or manually remove the rotated file before re-running.

User or group does not exist

create 0640 www-data adm fails silently if www-data is not a valid user on that system. Verify with id www-data and use verbose mode to see the exact error:

logrotate -v /etc/logrotate.d/myapp 2>&1 | head -30

Next Reading