Introduction
Nginx log rotation uses the system's logrotate utility to manage growing log files. When misconfigured, rotation fails with permission denied errors, causing log files to grow unbounded until they fill the disk. The error typically appears in syslog:
error: error running non-shared postrotate script for /var/log/nginx/*.log
chmod: changing permissions of '/var/log/nginx/access.log': Operation not permittedThis is especially common on systems with SELinux enabled or after manual log directory changes.
Symptoms
- Logrotate reports "permission denied" or "Operation not permitted" errors
- Log files grow beyond expected size (multi-gigabyte access.log)
- Disk fills up with log data
- Nginx continues writing to the old (rotated) log file
/var/log/messagesorjournalctlshows logrotate failures
Common Causes
- Log directory ownership is root:root but Nginx worker process runs as www-data or nginx user
- SELinux security context prevents logrotate from relabeling or truncating files
- Postrotate script fails to signal Nginx to reopen log file descriptors
- Custom log directory path does not inherit correct permissions
- logrotate configuration references wrong user/group
Step-by-Step Fix
- 1.Fix log directory ownership and permissions:
- 2.```bash
- 3.sudo chown -R nginx:adm /var/log/nginx/
- 4.sudo chmod 750 /var/log/nginx/
- 5.sudo chmod 640 /var/log/nginx/*.log
- 6.
` - 7.The nginx user needs write access; the adm group allows log reading tools access.
- 8.Verify the logrotate configuration at
/etc/logrotate.d/nginx: - 9.
` - 10./var/log/nginx/*.log {
- 11.daily
- 12.missingok
- 13.rotate 14
- 14.compress
- 15.delaycompress
- 16.notifempty
- 17.create 0640 nginx adm
- 18.sharedscripts
- 19.postrotate
- 20.[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
- 21.endscript
- 22.}
- 23.
` - 24.The
createdirective sets the correct ownership and permissions for new log files after rotation. - 25.Handle SELinux contexts if applicable. Check current context:
- 26.```bash
- 27.ls -Z /var/log/nginx/
- 28.
` - 29.If the context is wrong, restore it:
- 30.```bash
- 31.sudo restorecon -Rv /var/log/nginx/
- 32.
` - 33.If a custom path is used, add a new SELinux policy:
- 34.```bash
- 35.sudo semanage fcontext -a -t httpd_log_t "/custom/log/path(/.*)?"
- 36.sudo restorecon -Rv /custom/log/path/
- 37.
` - 38.Test logrotate manually without actually rotating:
- 39.```bash
- 40.sudo logrotate -d /etc/logrotate.d/nginx
- 41.
` - 42.This shows what would happen without making changes. Look for errors in the output.
- 43.Force a rotation to verify the fix:
- 44.```bash
- 45.sudo logrotate -f /etc/logrotate.d/nginx
- 46.ls -la /var/log/nginx/
- 47.
` - 48.New log files should have the correct ownership (nginx:adm) and permissions (640).
Prevention
- Include log directory permission setup in your server provisioning scripts (Ansible, Terraform, etc.)
- Monitor log file sizes with an alerting threshold (e.g., alert when any log exceeds 500MB)
- Test logrotate configuration after any manual changes to the log directory
- Use
copytruncateinstead ofcreateonly if the application cannot handle USR1 signal (not recommended for Nginx) - Document any custom log paths and their required SELinux contexts in your runbook
- Run
logrotate -das part of your post-deployment verification checklist