A Gunicorn socket error such as connect() to unix:/run/gunicorn.sock failed (13: Permission denied) is an operating-system access decision. Nginx is running as one user, the socket has an owner, group and mode, and every directory in the path must allow that user to traverse it. Identify the exact denied path and process identity first; changing permissions blindly, especially with recursive chmod 777, can create a security problem without fixing the real cause.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
The reliable repair is to align the Nginx worker identity with the socket’s owner, group and mode, or to bind Gunicorn on loopback TCP instead. A socket created by systemd can be given durable ownership and permissions, while Django’s static files, media, logs and generated files must be checked as separate filesystem cases.
As an Amazon Associate I earn from qualifying purchases.
What “permission denied” means in a Django and Gunicorn stack
Linux returns permission denied when a process cannot perform an operation on a specific filesystem object. In this stack, the failing operation is often Nginx trying to connect to a Unix-domain socket, but it may instead be Nginx opening a static file, Gunicorn writing a log, or Django saving an upload.
The path and the process matter. A socket at /run/gunicorn.sock can be perfectly usable by Gunicorn and still inaccessible to Nginx. Conversely, changing the socket mode will not help if the denied path is /var/www/app/static/css/site.css. Treat each error as a separate path-and-identity diagnosis.
#1 Best Overall
Start with the exact error, not a permission guess
Read the Nginx error log and the Gunicorn service journal at the time of the failure. Record the complete path, service name, operation and errno.
sudo tail -n 100 /var/log/nginx/error.log
sudo journalctl -u gunicorn.service -u gunicorn.socket -b
sudo systemctl status nginx gunicorn.service gunicorn.socket
These messages describe different failures:
| Message or symptom | What it usually means | First check |
|---|---|---|
connect() to unix:/run/gunicorn.sock failed (13: Permission denied) |
The Nginx worker cannot traverse the path or connect to the socket under its Unix permissions or a mandatory access-control policy. | Socket owner, group, mode and every parent directory; then SELinux or AppArmor records. |
No such file or directory |
The socket path does not exist, is in a different runtime directory, or the service created it elsewhere. | Unit configuration, service state and the actual path. |
Connection refused |
The socket exists but no process is accepting connections, or Gunicorn has stopped. | Gunicorn journal, listeners and service status. |
Nginx open() denial for /static/ or /media/ |
Nginx cannot read a Django-served file or traverse its directories. | The denied file and each parent directory, independently of the socket. |
Do not infer a socket problem from a generic 403 or 502. The error log identifies which object failed.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchConfirm which users are actually involved
Find the Nginx worker identity
Inspect the active Nginx configuration for its user directive and confirm the running processes:
grep -R "^[[:space:]]*user[[:space:]]" /etc/nginx/nginx.conf /etc/nginx/conf.d /etc/nginx/sites-enabled 2>/dev/null
ps -eo user,group,comm,args | grep '[n]ginx:'
Common accounts include www-data, nginx and http; none is universal across distributions. Use the account shown by your installation, not a name copied from another server.
Find Gunicorn’s service identity
For a systemd-managed service, inspect the effective unit and its status:
systemctl cat gunicorn.service
systemctl show gunicorn.service -p User -p Group -p WorkingDirectory
systemctl status gunicorn.service
Gunicorn also supports selecting worker credentials with --user and --group. Its settings documentation notes: “Note that this affects unix socket permissions.” The worker identity can therefore differ from the account that starts the master process.
Inspect the socket and every parent directory
For /run/gunicorn.sock, inspect both the socket and the complete path:
ls -l /run/gunicorn.sock
namei -l /run/gunicorn.sock
systemctl status gunicorn.service gunicorn.socket
journalctl -u gunicorn.service -u gunicorn.socket -b
ls -l shows the socket’s type, owner, group and mode. namei -l exposes each component: /, /run and the final socket. The connecting Nginx process needs execute (traversal) permission on every parent directory, plus the required permission on the socket itself. A socket mode that looks correct is not sufficient if a parent directory blocks traversal.
Repeat the same process for any path named in an open() denial. A directory can be readable yet not traversable, and a file can be readable while its parent directory is inaccessible.
Fix a systemd-created Unix socket
A systemd socket unit is usually the most durable arrangement for a runtime socket. Gunicorn’s deployment guide states that “systemd can create a UNIX socket and launch Gunicorn on demand.” The socket unit owns creation, so the mode and group are applied consistently after reboot.
Set the socket owner, group and mode
A Debian-style example is:
[Unit]
Description=Gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
SocketUser=www-data
SocketGroup=www-data
SocketMode=0660
[Install]
WantedBy=sockets.target
SocketUser and SocketGroup determine who owns the endpoint. 0660 permits the owner and group to read and write the socket while denying access to others. Substitute the actual Nginx account or a deliberately chosen shared group; do not assume www-data is correct everywhere.
Keep the service identity explicit
The service unit should separately declare the account that runs Gunicorn and the directory from which the application starts:
[Service]
User=django
Group=django
WorkingDirectory=/srv/example
ExecStart=/srv/example/venv/bin/gunicorn --workers 3 --bind unix:/run/gunicorn.sock example.wsgi:application
The socket owner does not have to be the same as the Gunicorn service user. What matters is that Gunicorn can use the endpoint and Nginx can connect to it. If the service writes through the socket or replaces it, verify that the chosen arrangement works with the unit dependencies and runtime directory.
Reload and verify instead of assuming
- Save the socket and service unit with the exact paths used by your Nginx upstream.
- Run
sudo systemctl daemon-reloadafter editing unit files. - Enable and restart the socket and service as appropriate:
sudo systemctl enable --now gunicorn.socket, thensudo systemctl restart gunicorn.service. - Check
systemctl status,ls -l /run/gunicorn.sockandnamei -l /run/gunicorn.sock. - Confirm that the resulting owner, group and mode match the values you intended.
A runtime directory such as /run is recreated at boot. Having systemd recreate the socket prevents a manually created endpoint with stale ownership from returning after a restart.
Rank #3
Fix a socket that Gunicorn creates itself
If the Gunicorn command binds directly to a Unix socket, review all credential and permission options:
/srv/example/venv/bin/gunicorn
--user django
--group webapps
--umask 007
--bind unix:/run/gunicorn.sock
example.wsgi:application
--user changes the worker user and --group selects the worker group. Gunicorn documents that --umask affects Unix-socket permissions. The resulting mode depends on the requested mode and the umask, so inspect the created socket rather than calculating from memory.
Make sure the account creating or replacing the socket can write to its parent directory. If the socket is under a directory owned by a different service, the next restart may fail even if the current socket is usable. A systemd socket unit is generally easier to make persistent because creation and ownership are kept outside the application command.
Test the connection as Nginx
Testing as your shell account can hide the problem because your account may have extra group membership or elevated privileges. Make the request as the real Nginx worker user:
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemssudo -u www-data curl --unix-socket /run/gunicorn.sock http://localhost/
Replace www-data with the account identified in your active Nginx configuration. Gunicorn’s deployment examples use this form on Debian-style systems. A successful HTTP response proves that the proxy identity can traverse the path and connect; it does not prove that every Django static or upload path is readable.
If the test still returns permission denied, rerun namei -l and check mandatory-access-control logs. If it returns connection refused, inspect whether Gunicorn is listening and healthy rather than changing modes.
Use a shared group instead of broad world permissions
When Nginx and Gunicorn need access to the same socket, a dedicated shared group is usually easier to maintain than making the socket world-accessible. Add only the required service accounts to that group, set the socket’s group and group-readable/group-writable mode, and ensure the parent directory permits traversal.
Direct ownership changes can be adequate for a single service, but they become fragile when deployments recreate the socket, rotate users or add another proxy. A shared group expresses the boundary explicitly and avoids granting unrelated local accounts access. Do not solve membership problems by using 0777 or by recursively changing an entire application tree.
Free tools Windows power users keep installed
One-click scans. No signup required.
Unix socket or loopback TCP?
Both transports can be valid. Choose based on the access-control and operational requirements of your host.
| Consideration | Unix socket | Loopback TCP |
|---|---|---|
| Access control | Uses filesystem owner, group, mode, parent-directory traversal and possibly SELinux/AppArmor labels. | Uses the bind address and port, firewall exposure and service policy; there is no socket pathname to traverse. |
| Proxy configuration | Nginx upstream points to a path such as unix:/run/gunicorn.sock. |
Nginx upstream points to an address such as 127.0.0.1:8000. |
| Runtime durability | Requires reliable recreation and ownership of a runtime file; systemd can manage that lifecycle. | Requires a stable port and a service that binds it on startup. |
| Typical failure surface | Missing path, denied traversal, wrong mode or stale socket. | Port conflict, refused connection or an unintended listening address. |
Switching to loopback TCP can remove a Unix-permission failure, but it is a configuration change, not a repair of the underlying filesystem policy. Bind only to loopback unless you intentionally need network access, and update both Gunicorn and Nginx to the same address and port.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Static files, media, logs and generated files need separate fixes
Django deployments commonly expose several filesystem trees. Diagnose each according to the process that reads or writes it:
Static files
Nginx must be able to traverse the static directory and read the collected files. Check the exact path named in the log:
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →namei -l /srv/example/static/css/site.css
ls -ld /srv/example/static /srv/example/static/css
ls -l /srv/example/static/css/site.css
Grant read and traversal access to the Nginx account or an appropriate group. The socket’s owner and mode have no effect on this path.
Media and uploads
Uploaded media often needs Nginx read access while Django needs write access to the upload directory. Use ownership and group permissions that provide those capabilities without making uploads writable by the proxy unless the application design requires it. Inspect both the directory and existing files; a writable directory does not automatically make every existing file writable.
Best Value
Logs
Gunicorn, Django and Nginx may write to different log locations. Check the service account, directory ownership, rotation configuration and parent traversal. A service can start successfully and later fail when it attempts to create a rotated file in a directory it cannot write.
Generated files
Reports, caches and export directories should be writable only by the process that generates them, with read access granted separately to Nginx if it serves the results. Fix the specific directory and operation shown by the error rather than changing the permissions of the whole project.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →When Unix permissions look correct: SELinux and AppArmor
Mandatory access-control systems can deny an operation even when owner, group and mode appear correct. On SELinux systems, inspect audit records and the active file contexts; on AppArmor systems, inspect profile denials in the journal or system logs. The correct adjustment depends on the distribution, profile, service confinement and path labels.
Do not copy a generic policy rule from an unrelated server. Capture the local denial, identify the confined process and grant only the operation required for the specific socket or file path. A policy change should be followed by a fresh test as the Nginx user.
A repeatable recovery checklist
- Copy the exact Nginx or Gunicorn error, including the path and errno.
- Classify it as permission denied, missing path, refused connection or a file denial.
- Identify the Nginx worker account and Gunicorn service/worker account.
- Inspect the target and every parent directory with
ls -landnamei -l. - For a socket, align owner, group and mode through a systemd socket unit or Gunicorn’s
--user,--groupand--umasksettings. - Reload systemd units or restart the relevant services, then verify the resulting runtime object.
- Test with
sudo -uusing the actual Nginx identity. - If the path is static, media, logs or generated output, repair that tree independently.
- Review SELinux or AppArmor denials when ordinary Unix permissions are not the explanation.
Frequently asked questions
Why can Gunicorn run Django while Nginx cannot access the socket?
Gunicorn and Nginx are separate processes with potentially different users and groups. Gunicorn can open its own socket while Nginx lacks permission to traverse a parent directory or connect under the socket’s mode. Compare both identities and the complete path.
Does changing the socket to mode 0660 always fix the error?
No. Mode 0660 works only when Nginx belongs to the socket’s group and every parent directory allows traversal. A mandatory-access-control policy can still deny the connection.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Why does the problem return after reboot?
A socket under /run is a runtime object. If it is recreated by a shell command or by Gunicorn with different credentials, its ownership can change on every boot. A systemd socket unit can recreate it with declared SocketUser, SocketGroup and SocketMode values.
Should I use chmod -R 777 on the project?
No. It grants broad access to files and directories, can expose secrets or permit unwanted writes, and may still leave the actual parent path or security policy wrong. Correct the specific identity, path and capability required by the failing operation.
Frequently Asked Questions
How do I fix Nginx’s “connect() to unix:/run/gunicorn.sock failed (13: Permission denied)” error?
Identify the Nginx worker user, inspect the socket and every parent directory with ls -l and namei -l, then align the socket owner/group/mode through systemd or Gunicorn settings. Verify with sudo -u <nginx-user> curl --unix-socket /run/gunicorn.sock http://localhost/.
What if the socket permissions are correct but Nginx is still denied?
Check SELinux or AppArmor audit and journal denials. Mandatory access-control policy can block a path despite ordinary Unix permissions; use the local denial and service profile to make a narrowly scoped adjustment.
Recommended Free Tools
Quick Recap
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.




