Nginx日志写入旧文件是因进程持有原文件描述符,Logrotate未触发reopen;需在配置中添加postrotate调用nginx -s reopen,或临时使用copytruncate。

这是因为 Nginx 进程仍持有旧日志文件的文件描述符(fd),Logrotate 虽已重命名或删除原文件,但只要进程没释放 fd,内核就继续往该 inode 写入数据——你看到的“旧文件”其实可能已是被 rename 后的副本(如 access.log.1),而 Nginx 完全不知情。
确认 Nginx 是否仍在写旧文件
运行以下命令检查 Nginx 工作进程实际写入的是哪个文件:
- 查主进程和 worker 进程 PID:
ps aux | grep nginx(注意区分 master 和 worker) - 查看某个 worker 进程打开的日志文件:
lsof -p| grep log - 重点看输出中 DEVICE 和 INODE 列:即使文件名显示为
access.log.1,只要 inode 没变,说明它还在往那个底层文件写
根本原因:Logrotate 缺少 reload 或 reopen 操作
Logrotate 默认只做文件移动/压缩,不会通知服务重新打开日志。Nginx 需要显式触发日志 reopen 才会关闭旧 fd、打开新 access.log。
- 在 Logrotate 配置中(如
/etc/logrotate.d/nginx),必须包含postrotate段,并调用nginx -s reopen - 正确示例:
/var/log/nginx/*.log { daily missingok rotate 30 compress delaycompress notifempty create 0644 www-data www-data sharedscripts postrotate if [ -f /var/run/nginx.pid ]; then nginx -s reopen >/dev/null 2>&1 || true fi endscript } - 注意:
sharedscripts确保postrotate只执行一次(多个日志文件时);nginx -s reopen是轻量操作,不中断服务
替代方案:用 copytruncate(仅限无可靠信号机制时)
如果因权限、容器环境等无法执行 nginx -s reopen,可用 copytruncate:
- Logrotate 先复制日志内容到新文件,再清空原文件(truncate)
- Nginx 继续写原路径,不受影响——但存在极小概率丢失 truncate 时刻的几行日志
- 只需在配置中加入
copytruncate,无需postrotate - 不推荐作为首选,尤其高流量场景;仅用于临时兜底
验证与排障小技巧
执行一次强制轮转并观察行为:
- 手动触发:
logrotate -f /etc/logrotate.d/nginx - 立刻检查:
ls -li /var/log/nginx/access.log*(看 inode 是否变化) - 再查 worker 进程 fd:
lsof -p $(cat /var/run/nginx.pid) | grep access.log - 若仍指向旧 inode,说明
postrotate未生效——检查脚本权限、PID 路径、nginx 用户是否能执行-s reopen











