The logrotate copytruncate race condition that silently drops logs

The logrotate copytruncate race condition that silently drops logs

# backend# devops# linux
The logrotate copytruncate race condition that silently drops logsSchiff Heimlich

The problem with copytruncate If you've ever configured logrotate for a high-throughput...

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
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.