copytruncate 是 logrotate 的一种日志切割方式,先拷贝再清空原文件以保持 inode 不变,使 Nginx 无需重启或 reload 即可继续写入;但存在少量日志丢失风险,且不兼容 dateext 和 create。
Nginx 本身不支持直接重开日志文件(即不发 kill -USR1 也能刷新日志),所以用 logrotate 配合 copytruncate 是一种“免重启”切割方案,但要注意它本质是妥协式做法,有日志丢失风险,仅适用于无法优雅 reload 的场景。
copytruncate 的逻辑分两步:
access.log 拷贝一份(比如 access.log-20260729)> /var/log/nginx/access.log),保留 inode 不变,Nginx worker 进程仍往同一文件句柄写入因为进程没感知文件被清空,也不需要重新打开日志文件,所以无需 nginx -s reload 或 kill -USR1。
⚠️ 注意:Nginx worker 可能缓存少量日志(几 KB)或延迟 flush,清空瞬间可能丢失最后几行日志。生产环境更推荐用 create + postrotate reload 组合。
copytruncate,否则 logrotate 默认会 rename 原文件并新建——但 Nginx 不会自动切到新文件,导致日志继续写进旧 inode(即 rename 后的 .1 文件),原 access.log 变为空白create:create 是为 rename 后新建空文件准备的;而 copytruncate 下原文件还在,新建操作多余且可能权限错乱dateext 冲突:dateext 依赖 rename 行为,与 copytruncate 不兼容,会导致日期后缀不生效missingok:防止日志文件暂不存在时报错中断示例配置 /etc/logrotate.d/nginx:
/var/log/nginx/*.log {dailyrotate 30compressdelaycompressmissingokcopytruncatedateformat -%Y%m%d}
⚠️ dateformat 在 copytruncate 下实际无效(因无 rename),若需日期标识,得靠 dateext + postrotate 脚本配合,但这已脱离 copytruncate 本意。
调试时先用 -d 检查语法:
logrotate -d /etc/logrotate.d/nginx
强制执行一次(不等 cron):
logrotate -f /etc/logrotate.d/nginx
执行后观察:
access.log 大小归零(但 inode 不变)access.log-20260729(如果用了 dateext 且没冲突)或 access.log.1(默认命名)nginx 访问仍在写入 access.log,无中断如果允许轻微中断(毫秒级),优先用标准 reload 方式:
/var/log/nginx/*.log {dailyrotate 30compresscreate 0644 www-data www-datasharedscriptspostrotateif [ -f /var/run/nginx.pid ]; thenkill -USR1 `cat /var/run/nginx.pid`fiendscript}
这种方式无日志丢失,权限可控,且 create 确保新日志文件属主正确。
不复杂但容易忽略细节