Nginx Error: 'mkdir() '/var/cache/nginx/proxy_temp' failed (13: Permission denied)' — Cause, Fix, and Troubleshooting Guide
Fix mkdir() /var/cache/nginx/proxy_temp failed (13: Permission denied): chown the cache and temp dirs to the nginx worker user so it can create them.
- #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 a startup ([emerg]) error: while initializing, the NGINX master tries to create the temporary/working directories for a proxy or FastCGI cache and the process does not have permission to create the directory on that path. The 13 is EACCES. Because it happens during configuration processing, NGINX refuses to start (or reload) at all:
nginx: [emerg] mkdir() "/var/cache/nginx/proxy_temp" failed (13: Permission denied)
nginx: configuration file /etc/nginx/nginx.conf test failed
Every proxy_cache_path, fastcgi_cache_path, proxy_temp_path, client_body_temp_path, uwsgi_temp_path, and scgi_temp_path implies a directory NGINX must be able to create and own. When the parent directory is owned by root (or another user) and the worker user cannot write into it, the master aborts. The whole service is down until the ownership is corrected — this is not a per-request error, it blocks the entire startup.
How the server responds
sudo nginx -tandsystemctl start/reload nginxfail with[emerg] mkdir() "..." failed (13: Permission denied).- The service will not start;
systemctl status nginxshows a failed start with the same message. - It appeared after you manually created
/var/cache/nginx(often asroot) or added a newproxy_cache_path. journalctl -u nginxshows the emerg line at the top of the last start attempt.- The named parent directory exists but is owned by
root:root, not the worker user. - A fresh install works, but a host with a hand-built cache directory fails.
Testing the server configuration
The error text names the exact directory it could not create — start there. Confirm the config and see which cache/temp paths are declared:
sudo nginx -t
sudo nginx -T | grep -E "proxy_cache_path|fastcgi_cache_path|proxy_temp_path|client_body_temp_path|user"
Identify the worker user and inspect ownership of the parent the error mentions:
# Worker user from the config
sudo nginx -T | grep -E "^user" | head -1
# Ownership and mode of the cache parent and its contents
ls -ld /var/cache/nginx
ls -l /var/cache/nginx
namei -l /var/cache/nginx/proxy_temp
Prove the worker cannot write there by attempting the mkdir as that user:
sudo -u nginx mkdir -p /var/cache/nginx/proxy_temp # RHEL/CentOS
sudo -u www-data mkdir -p /var/cache/nginx/proxy_temp # Debian/Ubuntu
Rule out a full/read-only filesystem and, on RHEL-family hosts, SELinux:
df -h /var/cache/nginx
mount | grep -E " /var| /var/cache"
getenforce
ls -Zd /var/cache/nginx
sudo ausearch -m avc -ts recent | grep nginx
Server configuration causes
- The cache/temp parent is owned by the wrong user. NGINX creates the leaf temp dirs (
proxy_temp,cache, etc.) as the worker user. If/var/cache/nginxisroot:rootmode755, the worker cannotmkdirinside it. - A hand-created cache directory during setup. Someone ran
mkdir -p /var/cache/nginx/...as root, so the tree is root-owned and the worker is locked out. proxy_temp_path/client_body_temp_pathpoints somewhere unwritable. A custom temp path on a different volume (e.g./data/nginx-temp) that the worker user cannot write to.- Mismatch between the
userdirective and directory ownership. Thenginx.confuserwas changed (e.g. tonginx) but the cache dirs are still owned bywww-data(or vice versa). - SELinux blocks writes to a non-standard cache location. On RHEL-family hosts a custom path lacking the
httpd_cache_t/var_tcontext is denied even when Unix ownership looks correct. - A read-only or full filesystem under the cache path, so no directory can be created regardless of ownership.
The fix
1. Give the cache/temp tree to the worker user. This is the fix in the vast majority of cases. Match the owner to the user directive in nginx.conf:
# RHEL/CentOS/Fedora — worker user is 'nginx'
sudo mkdir -p /var/cache/nginx
sudo chown -R nginx:nginx /var/cache/nginx
sudo chmod 700 /var/cache/nginx
# Debian/Ubuntu — worker user is 'www-data'
sudo mkdir -p /var/cache/nginx
sudo chown -R www-data:www-data /var/cache/nginx
sudo chmod 700 /var/cache/nginx
You only need to create and own the parent (/var/cache/nginx); NGINX creates proxy_temp, cache, and the level directories itself on start, as long as it can write to the parent.
2. Make sure the declared paths match what you own. In http context, confirm your directives point at the owned tree:
http {
proxy_cache_path /var/cache/nginx/cache levels=1:2 keys_zone=app_cache:10m
max_size=1g inactive=60m use_temp_path=off;
# If you set an explicit temp path, the worker must own it too
proxy_temp_path /var/cache/nginx/proxy_temp;
}
Setting use_temp_path=off keeps temp files inside the cache directory (avoiding a cross-directory rename and one fewer path to permission), which is generally recommended.
3. Custom location on another volume. If the cache lives outside /var/cache/nginx, create and chown that path the same way, and on SELinux hosts label it:
sudo chown -R nginx:nginx /data/nginx-cache
sudo semanage fcontext -a -t httpd_cache_t "/data/nginx-cache(/.*)?"
sudo restorecon -Rv /data/nginx-cache
4. Validate and start. Because the emerg happens at start/reload, once ownership is fixed a reload will succeed; if NGINX is currently stopped, start it:
sudo nginx -t && sudo systemctl reload nginx
# or, if the service failed to start at all:
sudo systemctl start nginx
Safe configuration practice
- Own the parent, not just the leaf. NGINX makes the temp/cache subdirectories itself; it only needs write access to the directory named in the emerg message’s parent.
- Keep the
userdirective and directory owner in sync. Changinguserwithout re-chowning the cache tree reintroduces this exact error. - Prefer
use_temp_path=offso temp writes stay inside the cache dir — fewer directories to own and no cross-filesystem rename penalty. - Don’t create cache dirs as root during provisioning without a matching
chown; automate ownership in your config-management role. - On RHEL-family hosts, label non-default cache paths with
httpd_cache_tviasemanage fcontext+restorecon, or SELinux denies the write even with correct Unix ownership. - Watch for full or read-only
/var— a13-looking failure can actually beENOSPC/read-only mount;df -handmountconfirm it. Alert on failed NGINX starts via your monitoring dashboard.
Related server errors
- Nginx Error: Open Failed No Such File Static — another
open()/filesystem class error, but at request time on static content rather than at startup on cache dirs. - Nginx Error: Permission Denied Connecting Upstream Selinux — the same
(13)errno driven by SELinux, useful when a chown alone doesn’t clear the denial. - Nginx Error: 502 Bad Gateway — a broken proxy cache can cascade into 502s once NGINX is running, so this is the downstream symptom to watch.
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.