Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for NGINX By James Joyner IV · · 8 min read Last reviewed Jul 2026

Nginx Error: 'mkdir() '/var/cache/nginx/proxy_temp' failed (13: Permission denied)' — Cause, Fix, and Troubleshooting Guide

Quick answer

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
Free toolkit

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 -t and systemctl start/reload nginx fail with [emerg] mkdir() "..." failed (13: Permission denied).
  • The service will not start; systemctl status nginx shows a failed start with the same message.
  • It appeared after you manually created /var/cache/nginx (often as root) or added a new proxy_cache_path.
  • journalctl -u nginx shows 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/nginx is root:root mode 755, the worker cannot mkdir inside 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_path points 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 user directive and directory ownership. The nginx.conf user was changed (e.g. to nginx) but the cache dirs are still owned by www-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_t context 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 user directive and directory owner in sync. Changing user without re-chowning the cache tree reintroduces this exact error.
  • Prefer use_temp_path=off so 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_t via semanage fcontext + restorecon, or SELinux denies the write even with correct Unix ownership.
  • Watch for full or read-only /var — a 13-looking failure can actually be ENOSPC/read-only mount; df -h and mount confirm it. Alert on failed NGINX starts via your monitoring dashboard.

See the NGINX category for more guides.

Free download · 368-page PDF

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?

Free download · 368-page PDF

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.