logrotate: Configuration That Doesn't Lose Logs
Introduction¶
logrotate keeps /var/log from filling the disk. The two things people get
wrong: the app keeps writing to the old file after rotation (so the new file
stays empty), and rotations silently stop happening. Both are easy to fix once
you know why.
Concepts¶
logrotate is run by a daily cron job or systemd timer — it is not a
daemon. Each run it reads /etc/logrotate.conf and everything in
/etc/logrotate.d/, and for each log file decides whether it's due for
rotation (by age or size), then renames/compresses/deletes according to the
config.
The core problem: a long-running process holds an open file descriptor to
/var/log/app.log. logrotate renames it to app.log.1. The process still
writes to the same fd — now app.log.1 — and the fresh app.log stays empty.
Two solutions:
create+ apostrotatesignal — logrotate makes a new empty file, then runs a script that tells the process to reopen its logs (kill -HUP,systemctl reload,nginx -s reopen). Preferred: no lost lines, no race.copytruncate— logrotate copies the file then truncates the original to zero. The process's fd still points at the same inode, now empty. Simple, but a small window exists where lines written during the copy are lost.
Hands-on¶
A typical /etc/logrotate.d/myapp:
/var/log/myapp/*.log {
daily
rotate 14 # keep 14 old files
missingok # don't error if the log is absent
notifempty # skip rotation if the file is empty
compress
delaycompress # compress on the NEXT rotation (last file stays readable)
dateext # app.log-20260827 instead of app.log.1
dateformat -%Y%m%d
sharedscripts # run postrotate once, not per matched file
postrotate
systemctl reload myapp.service >/dev/null 2>&1 || true
endscript
}
Or the copytruncate variant (for an app you can't signal):
/var/log/legacyapp/app.log {
daily
rotate 7
compress
delaycompress
copytruncate
missingok
notifempty
}
Size-based rotation¶
/var/log/chatty.log {
size 100M # rotate when it hits 100M (checked each logrotate run)
rotate 5
compress
missingok
}
size alone means "only on size"; combine with daily + maxsize 100M to
rotate daily or sooner if it gets big.
Verification and troubleshooting¶
# Force a run of one config, verbose, ignoring the "not due yet" check
sudo logrotate -f -v /etc/logrotate.d/myapp
# Dry run — show what it WOULD do
sudo logrotate -d /etc/logrotate.d/myapp
# When did each log last rotate? (the state file)
sudo cat /var/lib/logrotate/logrotate.status
- New log file stays empty after rotation — the app still holds the old fd.
Add a
postrotatethat reloads it, or usecopytruncate.lsof /var/log/myapp/app.log*shows which process has the deleted file. - Rotation never happens — the timer/cron isn't running
(
systemctl status logrotate.timer), or a syntax error in one config file aborts the whole run (logrotate -d /etc/logrotate.confshows the parse error), or the file isn't old enough / is empty (notifempty). - "error: skipping ... because parent directory has insecure permissions" —
logrotate refuses world-writable log dirs unless you add
su <user> <group>to the stanza or fix the directory perms. - Same log rotated twice / by two configs — a distro package config and your own both match the glob. logrotate warns "duplicate log entry"; remove one.
postrotateruns per file, reloading the service 10 times — addsharedscripts.- Compressed logs but the current one is also gzipped and unreadable — you
want
delaycompressso the most recent rotation stays plain text for a day. - Disk still fills —
rotate Nkeeps N copies; if the log grows faster than daily rotation clears space, addsize/maxsizeand lowerrotate, or fix the app's log verbosity.
Related tools and reading¶
- On-site: Linux commands reference, Crontab Generator.
- Related posts: Disk full but df says otherwise, journalctl recipes.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Linux & Sysadmin
resolv.conf and systemd-resolved
On modern distros /etc/resolv.conf points at a stub resolver managed by systemd-resolved. Editing it directly usually does nothing — here's the real model.
October 24, 2026Tuning Open File Limits on Linux
Four different limits control how many files a process can open. 'Too many open files' means you're hitting one specific one — here's how to find and raise it.
October 24, 2026ss and conntrack for Connection Debugging
ss shows you every socket and its send/receive queues; conntrack shows what the firewall is tracking. Together they explain most 'connection refused' and 'no buffer space' problems.
October 24, 2026