The problem with copytruncate
If you've ever configured logrotate for a high-throughput service like nginx or syslog, you might have used copytruncate. It copies the log file then truncates it in place, avoiding the need to signal the process to reopen its file descriptors.
The issue: there's a window between the copy and the truncate. Any log lines written during that window get either dropped or duplicated when rotation runs on a busy system.
What it looks like in practice
# /etc/logrotate.d/nginx
/var/log/nginx/access.log {
daily
rotate 7
copytruncate
compress
}
On a server doing high request volume, that copy-then-truncate gap can silently eat log lines per rotation. If you're doing incident response and wondering why your access logs don't match your application logs, this is one of the places to check.
The more reliable approach
Use postrotate to signal the service to reopen its logs, then let logrotate do an atomic rename:
/var/log/nginx/access.log {
daily
rotate 7
compress
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
nginx (and most well-behaved services) will finish writing the current line, then reopen to the new file on SIGUSR1. No gap, no dupes.
Another gotcha: dateext collisions
If you're using dateext with dateformat, watch out for rotation runs that trigger twice in the same second (can happen with noisy cron or manual runs). The second rotation will fail to create a unique file. Set dateformat with enough precision:
dateformat -%Y%m%d-%s
The %s gives you Unix timestamp seconds, which keeps it unique even if cron fires twice.
Bottom line
copytruncate is a workaround for services that don't handle SIGUSR1 gracefully. If your service supports log reopening, use postrotate. If you're stuck with copytruncate, be aware it has a silent data loss window on busy systems.
Top comments (0)