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 grepthrough 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,postrotateruns once per matched file — a reload storm if you have many logs || trueat the end of the postrotate command prevents logrotate from failing when the service is stopped- nginx uses
kill -USR1, Apache usesapachectl graceful, systemd services usesystemctl reloadto 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