Nginx Error: 'could not open error log file: open() '/var/log/nginx/error.log' failed (13: Permission denied)' — Cause, Fix, and Troubleshooting Guide
Fix could not open error log file: open() failed (13: Permission denied): create /var/log/nginx, fix its owner and SELinux, and check logrotate.
- #nginx
- #web-server
- #troubleshooting
Stuck on this NGINX error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
What this error means
This is one of the earliest failures NGINX can hit: before it can even open its normal log, it tries to open the error log file and is denied permission to write to it (or the directory holding it does not exist). Because the error log itself is unavailable, NGINX writes this message to stderr, so you see it on the console or in journalctl, not in error.log:
nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (13: Permission denied)
nginx: [emerg] open() "/var/log/nginx/error.log" failed (13: Permission denied)
The 13 is EACCES. NGINX cannot start, so the whole service is down. This most often appears right after a fresh install where /var/log/nginx is missing, after someone deleted the log directory, after a logrotate run recreated the file with the wrong owner, or when the log volume has become read-only.
How the server responds
systemctl start nginxfails andjournalctl -u nginxshowscould not open error log file ... (13: Permission denied)on stderr.- Nothing is written to
error.logitself, because that is precisely the file it cannot open. - It appears right after install, after
/var/log/nginxwas removed, or after a nightlylogrotaterun. ls -ld /var/log/nginxshows the directory missing, or owned byrootwith no worker access.- The master process starts as
rootbut the failure can still occur if the path or the mount is unwritable. - On RHEL-family hosts,
getenforceisEnforcingandausearchshows an AVC denial on the log path.
Testing the server configuration
Because the message goes to stderr, read it from the service journal and by running the binary directly:
# The failure as systemd captured it (stderr)
journalctl -u nginx --since "10 min ago" --no-pager
# Run the config test — it reveals the same open() failure on stderr
sudo nginx -t
Check whether the directory exists and who owns it and the log file:
ls -ld /var/log/nginx
ls -l /var/log/nginx/
namei -l /var/log/nginx/error.log
Confirm the configured log path (it may not be the default) and the worker user:
sudo nginx -T | grep -E "error_log|access_log|^user"
Rule out a full or read-only mount, and SELinux on RHEL-family hosts:
df -h /var/log
mount | grep " /var" # look for 'ro' in the options
getenforce
ls -Zd /var/log/nginx
sudo ausearch -m avc -ts recent | grep nginx
Server configuration causes
/var/log/nginxdoes not exist. After a partial install, a manual cleanup, or a stripped container image, the parent directory is simply gone andopen()fails.- Wrong ownership/permissions on the directory or file. The log dir or
error.logis owned by another user or is mode700such that the process cannot write to it. logrotaterecreated the file with the wrong owner. Acreateline in the rotate config that omits or mis-sets the owner leaveserror.logowned byroot:rootor an unexpected user after rotation, and the next start/reopen fails.- SELinux context on the log path is wrong. The directory lacks the
var_log_ttype (e.g. a bind-mounted or custom log location), so thehttpd_tdomain is denied even with correct Unix permissions. - Read-only or full filesystem. The
/var/logmount is read-only (common after an fsck or a disk error) or/varis full, so no file can be opened for writing. - A custom
error_logpath pointing somewhere unwritable. Anerror_log /opt/logs/nginx.err;directive aimed at a directory the process cannot write to.
The fix
1. Create the directory and set correct ownership. The log directory should be owned by the worker user (or root with group access) and be writable. On most distros:
sudo mkdir -p /var/log/nginx
# Debian/Ubuntu
sudo chown -R www-data:adm /var/log/nginx
sudo chmod 755 /var/log/nginx
# RHEL/CentOS/Fedora
sudo chown -R nginx:nginx /var/log/nginx
sudo chmod 755 /var/log/nginx
If error.log exists but is owned by the wrong user (a classic post-logrotate state), fix the file too:
sudo chown www-data:adm /var/log/nginx/error.log # or nginx:nginx on RHEL
sudo chmod 640 /var/log/nginx/error.log
2. Fix the logrotate config so it recreates the file correctly. The create directive must set the owner NGINX expects; otherwise you will hit this again after the next rotation:
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm # RHEL: create 0640 nginx nginx
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
Test the rotation without waiting a day:
sudo logrotate -d /etc/logrotate.d/nginx # dry run
sudo logrotate -f /etc/logrotate.d/nginx # force, then confirm ownership
3. Restore the SELinux context (RHEL-family). For a default or custom log path:
sudo semanage fcontext -a -t var_log_t "/var/log/nginx(/.*)?"
sudo restorecon -Rv /var/log/nginx
4. Handle a read-only or full filesystem. If mount shows ro, remount read-write after resolving the underlying disk issue, and clear space if /var is full:
sudo mount -o remount,rw /var
df -h /var/log
5. Validate and start. Since NGINX is not running, start it (a reload has nothing to signal):
sudo nginx -t && sudo systemctl start nginx
Safe configuration practice
- This message goes to stderr, not
error.log— by definition the log is unavailable. Always checkjournalctl -u nginx, not the file, for this one. logrotateis the usual repeat offender. If the error returns every night around rotation time, thecreateowner in the rotate config is wrong — fix it there, not just with a one-offchown.- Match ownership to the worker user in
nginx.conf. Changing theuserdirective without re-chowning/var/log/nginxreintroduces the failure. - Custom
error_logpaths need the same treatment — directory must exist, be owned/writable, and carryvar_log_tunder SELinux. - Read-only
/varmasquerades as a permission error. Checkmountforrobefore chasing ownership; alert on failed NGINX starts via your monitoring dashboard. - In containers, prefer logging to stdout/stderr (
error_log /dev/stderr;) so there is no log directory to permission at all.
Related server errors
- Nginx Error: Open Failed No Such File Static — the same
open()failure class, but on request-time static content rather than the log file at startup. - Nginx Error: Permission Denied Connecting Upstream Selinux — the same
(13)errno when SELinux, not Unix modes, is the blocker; the diagnosis pattern carries over. - Nginx Error: Bind() Address Already In Use — another startup-blocking
[emerg]that stops NGINX before it serves traffic, worth ruling out during a failed start.
See the NGINX category for more guides.
Fixed it? Get 500 NGINX & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.