Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
World desk9 min

How to Fix the Nginx WSGI “Permission Denied” Upstream Error

A practical runbook for fixing Nginx WSGI upstream permission errors, including Unix-socket permissions, Gunicorn and uWSGI configuration, systemd runtime paths, protocol mismatches, and SELinux or AppArmor checks.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

connect() to unix:… failed (13: Permission denied) while connecting to upstream means the Nginx worker could not open the configured upstream endpoint. For a Unix socket, inspect the socket owner, group, mode, and every parent directory’s search permission, then confirm that Nginx is using the protocol your WSGI server actually speaks.

Read the error before changing permissions

Start with the exact event in Nginx’s error log:

sudo tail -f /var/log/nginx/error.log

Record the complete endpoint and errno. A typical message looks like this:

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
connect() to unix:/run/uwsgi/app.sock failed (13: Permission denied) while connecting to upstream

Errno 13 is an access-control failure at the endpoint Nginx tried to use. It does not prove that the socket file itself has the wrong mode. Nginx may be unable to traverse one of the directories leading to the socket, the socket may have been recreated with different ownership, or a mandatory access-control policy may be denying the operation.

Log symptom What it usually indicates
Permission denied or errno 13 The Nginx worker cannot access the endpoint or a path component.
No such file or directory The configured path does not exist, or a parent directory is missing.
Connection refused A TCP address or socket exists, but no process is accepting connections there.
A successful connection followed by protocol errors Nginx reached the process, but the proxy directive and backend protocol do not match.

Use this diagnostic runbook

  1. Confirm that the backend is running

    Check the service status and its logs using the service manager used on your system. Then verify that the process is listening at the same Unix path or TCP port in the Nginx configuration. A running Gunicorn or uWSGI process is not enough if it was started with a different bind address.

    sudo systemctl status your-wsgi-service
    sudo ss -lxnp
    sudo ss -ltnp

    Use the Unix-socket listing for unix: endpoints and the TCP listing for an address such as 127.0.0.1:8000.

  2. Inspect every directory in the path

    Run namei against the exact path printed in the error:

    Free tools Windows power users keep installed

    One-click scans. No signup required.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
    namei -l /run/uwsgi/app.sock

    The output shows the owner, group, and mode of each component. The Nginx account needs execute, also called search, permission on every directory from the filesystem root through the directory containing the socket. It does not need directory read permission merely to traverse a known path. A restrictive mode on /run/uwsgi can therefore cause errno 13 even when the socket itself is mode 0660.

  3. Find the Nginx worker account

    Look for the user directive in the active Nginx configuration and confirm the account used by the running workers:

    sudo nginx -T | grep -E '^[[:space:]]*user[[:space:]]'
    ps -o user,group,cmd -C nginx

    Common accounts include www-data and nginx, but use the value shown on your host rather than assuming one.

  4. Inspect socket ownership and mode

    stat -c '%A %U %G %n' /run/uwsgi/app.sock

    Compare the owner and group with the Nginx account and with the account that launches the WSGI server. The mode must grant the Nginx account the operation required to connect, either directly as owner or through a shared group. Record the directory metadata from namei as well; changing only the final file is incomplete.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  5. Test access as Nginx, not as root

    Root can open almost any Unix socket, so a test made from a root shell does not reproduce Nginx’s permissions. For an HTTP-speaking backend, make a request as the worker account:

    sudo -u www-data curl --unix-socket /run/gunicorn.sock http://localhost/

    Replace www-data and the path with the values on your machine. For a backend using the uWSGI protocol, use a protocol-aware health check; an HTTP curl request is not a valid uWSGI probe. A failure that reproduces under sudo -u confirms an operating-system access problem rather than an Nginx reload issue.

  6. Check the protocol and directive together

    Nginx’s upstream directive must match what the process speaks. A uWSGI protocol endpoint uses uwsgi_pass and the uWSGI parameter include. An HTTP-speaking Gunicorn endpoint uses HTTP proxying. A protocol mismatch can look like an upstream failure even after filesystem permissions are correct.

  7. Apply the change, then validate in order

    After changing the service or socket configuration, restart or reload the service that owns the socket so it recreates the endpoint with the intended identity. Validate Nginx’s syntax before reloading it:

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
    sudo nginx -t
    sudo systemctl reload nginx

    Watch the error log while making a request and check the WSGI service log at the same time. This catches a socket that disappears during a restart or is recreated with a different mode.

How Unix-socket permissions actually work

A Unix socket is subject to normal filesystem access checks. Four properties matter:

  • Socket owner: the user identity assigned to the socket.
  • Socket group: a group whose members can be granted access without making the socket public.
  • Socket mode: the owner, group, and other permission bits.
  • Parent-directory traversal: execute/search permission on every directory in the path.

For a least-privilege deployment, keep the application process under its own account and place Nginx and that account in a dedicated group. A mode such as 0660 grants access to the owner and group only. Some uWSGI documentation uses --chmod-socket=664 as a more sensible example than 666; whether the world-readable bit is acceptable depends on the host, so use the narrowest mode that works.

Why chmod 777 on the socket often changes nothing

Making the socket world-accessible does not grant search permission on a directory such as /run/uwsgi. It also does not fix a wrong path, a process that recreates the socket immediately, a protocol mismatch, or a denial from SELinux or AppArmor. Finally, a service manager or the WSGI server may replace the socket during every restart, undoing a manual mode change. Treat a manual chmod as a diagnostic experiment at most; configure the owner and mode at the point where the socket is created.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose a deliberate ownership pattern

Shared group for a hand-managed socket

A shared group is appropriate when the application account should remain separate from the web-server account:

sudo groupadd --system webapps
sudo usermod -a -G webapps www-data
sudo usermod -a -G webapps app

Use the actual service and Nginx accounts. Existing processes do not automatically gain a newly added supplementary group, so restart the affected services or start a new login session. Configure the service to create the socket with the shared group and a restrictive mode, then verify:

sudo chown app:webapps /run/uwsgi/app.sock
sudo chmod 0660 /run/uwsgi/app.sock
namei -l /run/uwsgi/app.sock
stat -c '%A %U %G %n' /run/uwsgi/app.sock

The ownership commands above are useful for confirming a diagnosis, but they will not persist if the WSGI process recreates the socket. Put the group and mode in the process or service configuration.

Gunicorn settings

Gunicorn’s --user, --group, and --umask options affect Unix-socket permissions. Set them explicitly in the service definition rather than relying on the shell that happens to launch Gunicorn. For example, an application account can own the process while a shared group is used for the socket:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
gunicorn --user app --group webapps --umask 007 --bind unix:/run/gunicorn.sock myproject.wsgi:application

A 007 umask removes permissions for “other” users from a newly created socket, yielding group access when the creation defaults allow it. Confirm the resulting mode with stat; the effective mode also depends on how the socket is created and on the parent directory.

uWSGI settings

For a uWSGI socket, set the group and socket mode in uWSGI’s startup configuration. The documented --chmod-socket=664 example illustrates the important point: permissions must be managed by the process that creates the socket, not patched after every restart. A stricter 660 mode is preferable when no other local users need access.

Make the Nginx directive match the backend

uWSGI protocol

When the upstream speaks the uWSGI binary protocol, the location normally includes the uWSGI parameters and uses uwsgi_pass:

location / {
    include uwsgi_params;
    uwsgi_pass unix:/run/uwsgi/app.sock;
}

The socket path must exactly match the path supplied to uWSGI. A typo or a stale socket can produce a different errno, so always compare the launch command, service unit, and Nginx configuration.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

HTTP-speaking Gunicorn

Gunicorn commonly serves HTTP, so Nginx must proxy HTTP rather than send uWSGI frames. For a TCP bind, the pattern is:

location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

If Gunicorn is bound to a Unix socket, use Nginx’s HTTP Unix-socket proxy syntax supported by your Nginx version, and test it with an HTTP client running as the Nginx account. Do not change to uwsgi_pass merely because the application is a WSGI application; WSGI describes the application interface, while the wire protocol between Nginx and the server is a separate choice.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Use systemd to make runtime permissions reproducible

Files under /run are temporary. A reboot or service restart can remove a hand-created directory and recreate the socket with defaults. systemd provides two useful patterns.

RuntimeDirectory for a service-owned directory

Set RuntimeDirectory=uwsgi (or another name) in the service unit. systemd creates the matching directory below /run at service start and removes it when the service stops. Combine it with an explicit service User, Group, and directory mode, then bind the WSGI server to the resulting path, such as /run/uwsgi/app.sock. Ensure the directory’s group and mode allow the Nginx account to search it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Socket unit ownership

When using systemd socket activation, define the endpoint in a .socket unit and set:

[Socket]
ListenStream=/run/gunicorn.sock
SocketUser=www-data
SocketGroup=webapps
SocketMode=0660

The corresponding service must support the descriptor passed by systemd. The advantage is that startup order, cleanup, owner, group, and mode are declared in one reproducible place. After editing units, reload systemd’s unit files and restart the socket and service, then inspect the actual result with namei and stat.

Unix socket or TCP loopback?

Consideration Unix socket TCP loopback
Access control Depends on socket ownership, mode, and directory traversal, plus any mandatory-access policy. Uses network binding and firewall or policy controls rather than filesystem ownership.
Exposure Not reachable through a network address, but any local account allowed to open it can communicate with the service. Binding to loopback avoids external exposure; binding to a non-loopback address can expose the service and requires additional protection.
Operations Requires reliable creation and cleanup of a runtime path. Usually simpler to inspect with ss, but the port must be coordinated and kept private.
Typical failure Errno 13 from ownership, mode, directory traversal, or MAC policy. Connection refused, wrong port, bind failure, or a firewall/policy denial.

Switching from a Unix socket to TCP can bypass a filesystem-permission problem, but it is a deployment change, not a repair. Bind only to loopback unless the application is intentionally serving another host, and update both the WSGI server and Nginx configuration together.

When normal permissions look correct

The socket is recreated after every restart

Check the service log immediately after a restart and inspect the new inode with stat. Configure the service’s group and umask, or use systemd runtime and socket settings, instead of editing the current file. If the path is a symlink, inspect the target and every directory in the target path with namei -l.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Nginx can see the file but still gets errno 13

Inspect mandatory access-control logs and policy for your distribution. SELinux and AppArmor can deny Nginx access even when Unix owner, group, mode, and directory traversal are correct. There is no single policy command that applies to every distribution; use the distribution’s security documentation and adjust the policy narrowly for the intended socket path.

The request reaches the backend but returns a gateway or protocol error

At that point, filesystem access is no longer the primary suspect. Confirm whether the backend speaks HTTP or uWSGI, then use the corresponding Nginx directive and parameter include. Check that the application path, Host header, and any required forwarding headers are also configured for that backend.

A manual test succeeds but Nginx fails

Repeat the test as the exact Nginx worker account, not as your shell user or root. Compare its supplementary groups with the groups shown for the WSGI service. A login session may have an updated group membership while the long-running Nginx process does not; restart Nginx after changing group membership.

Final verification checklist

  • The error log’s endpoint exactly matches the endpoint created by the WSGI service.
  • The WSGI process is running and listening on that path or port.
  • namei -l shows search permission for the Nginx account on every parent directory.
  • stat shows the intended socket owner, group, and mode.
  • The Nginx worker account is a member of the required group, and Nginx was restarted after group changes.
  • The socket mode is restrictive, normally 0660 or another explicitly justified value.
  • The Nginx directive matches the backend protocol: uwsgi_pass for uWSGI protocol and HTTP proxying for an HTTP backend.
  • nginx -t passes before the reload.
  • A request made as the Nginx account succeeds using a probe appropriate to the backend protocol.
  • If all Unix checks pass, the relevant SELinux or AppArmor logs and policy have been reviewed.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.