Nginx error: permission denied while connecting to upstream
Encountering "permission denied while connecting to upstream" means Nginx cannot reach its backend service socket; this guide explains how to fix it by checking filesystem permissions and SELinux contexts.
What This Error Means
When Nginx reports permission denied while connecting to upstream, it indicates that the Nginx worker process, acting as a reverse proxy, is unable to establish a connection with its designated backend application. This connection typically occurs via a Unix domain socket (e.g., /run/php-fpm/php-fpm.sock for PHP-FPM, or a similar socket for Node.js or Python applications). The "permission denied" part specifically means that the operating system is blocking Nginx's access to this socket file or the directory containing it, based on configured access controls.
Unlike "connection refused" (which implies the backend isn't listening or reachable), "permission denied" points directly to a security or access control mechanism preventing Nginx from even attempting to communicate with the socket. This is a critical distinction for effective troubleshooting.
Why It Happens
At its core, this error occurs because the user account under which Nginx is running lacks the necessary permissions to interact with the upstream Unix socket. Linux-based systems employ robust access control mechanisms, and Nginx adheres to these. The root causes generally fall into two categories:
- Standard Filesystem Permissions: The owner, group, or mode of the socket file (or its parent directory) does not grant the Nginx user read/write access. Nginx typically runs as a non-privileged user like
nginxorwww-data. If the socket is owned byphp-fpm:php-fpmwith660permissions, and the Nginx user isn't part of thephp-fpmgroup, Nginx will be denied access. - SELinux or AppArmor Policies: Even if standard
ls -lpermissions appear correct, security enhancement modules like SELinux (Security-Enhanced Linux, common on RHEL/CentOS/Fedora) or AppArmor (common on Ubuntu/Debian) can enforce additional, mandatory access controls. These policies can prevent Nginx from accessing files in certain contexts or making network connections, overriding seemingly permissive standard permissions. In my experience, SELinux is often the silent culprit when traditional permission checks yield no obvious answers.
Understanding these two layers of access control is crucial for resolving the issue.
Common Causes
Let's break down the most frequent scenarios that lead to this error:
- Incorrect Unix Socket Permissions:
- The Unix socket file itself (e.g.,
/var/run/php-fpm/php-fpm.sock) has restrictive permissions (e.g.,600) that only allow the owner to access it, and Nginx is not the owner. - The parent directory of the socket (e.g.,
/var/run/php-fpm/) has permissions that prevent the Nginx user from traversing or listing its contents.
- The Unix socket file itself (e.g.,
- Incorrect Unix Socket Ownership:
- The socket file is owned by the backend service's user/group (e.g.,
php-fpm:php-fpm), but the Nginx user (nginxorwww-data) is not a member of that group, and the socket's group permissions are notrw(e.g.,660).
- The socket file is owned by the backend service's user/group (e.g.,
- SELinux Context Mismatch:
- This is a very common one. SELinux assigns a "context" (a label) to every file, directory, and process. If the Nginx process's context (
httpd_t) is not permitted to access the socket file's context (e.g.,var_run_torunlabeled_t), SELinux will block the connection. This often happens if the socket is created in a non-standard location or if files are copied without preserving their SELinux contexts.
- This is a very common one. SELinux assigns a "context" (a label) to every file, directory, and process. If the Nginx process's context (
- Backend Application Configuration:
- While less directly a permission denied error from Nginx, the backend application (e.g., PHP-FPM) might be configured to create the socket with incorrect permissions or ownership, even if its own process has the necessary rights. This is effectively an upstream misconfiguration that manifests as a Nginx permission error.
- Filesystem Mount Options:
- Occasionally, the filesystem where the socket resides might be mounted with options that restrict access, though this is rarer for typical
/runor/var/runpaths.
- Occasionally, the filesystem where the socket resides might be mounted with options that restrict access, though this is rarer for typical
Step-by-Step Fix
Here's a practical, step-by-step approach to diagnose and resolve the "permission denied while connecting to upstream" error.
-
Identify the Upstream Socket Path:
First, locate the exact path to the Unix socket that Nginx is trying to connect to. This information is typically found in your Nginx server block configuration (e.g.,/etc/nginx/sites-enabled/your_site.confornginx.confitself). Look for directives likeproxy_pass unix:/path/to/your.sock;orfastcgi_pass unix:/path/to/your.sock;.
```nginx
# Example Nginx configuration snippet
server {
listen 80;
server_name example.com;location / { # For a general reverse proxy proxy_pass http://unix:/run/app/app.sock:/; # For PHP-FPM # fastcgi_pass unix:/run/php-fpm/php-fpm.sock; # fastcgi_index index.php; # include fastcgi_params; }}
`` Note down the full path, e.g.,/run/app/app.sock`. -
Determine Nginx User and Backend User:
Nginx typically runs its worker processes as a non-root user. Check yournginx.conffor theuserdirective, usually found at the top.
nginx user nginx; # or www-data; worker_processes auto;
Also, identify the user and group under which your backend application (e.g., PHP-FPM, Gunicorn) is running and creating the socket. For PHP-FPM, this is often configured in/etc/php-fpm.d/www.conf(or similar):
ini ; In php-fpm.d/www.conf listen = /run/php-fpm/php-fpm.sock listen.owner = php-fpm listen.group = nginx # Or the Nginx user's group listen.mode = 0660
This step helps you understand which users need access to what. -
Verify Socket Existence:
Ensure the socket file actually exists. If the backend service isn't running or isn't configured to create the socket, Nginx will fail to connect.
bash ls -l /run/app/app.sock
If it doesn't exist, start your backend service first (systemctl start <your-backend-service>). If it exists, proceed. -
Check Filesystem Permissions and Ownership:
This is often the first place to look.- Check the socket file itself:
bash ls -l /run/app/app.sock # Example output: srw-rw----. 1 php-fpm php-fpm 0 Jun 1 10:00 /run/app/app.sock
In this example, the owner isphp-fpmand the group isphp-fpm. Permissionssrw-rw----(or660) mean the owner and group have read/write access. If Nginx runs asnginx, and thenginxuser is not in thephp-fpmgroup, access will be denied. - Check the parent directory: Nginx also needs
execute(x) permission on the parent directories to traverse to the socket file.
bash ls -ld /run/app/ # Example output: drwxr-xr-x. 2 php-fpm php-fpm 60 Jun 1 10:00 /run/app/
This directory allows anyone to traverse it (xfor others), which is fine. If it weredrwx------(700), Nginx would be blocked.
To fix filesystem permissions:
* Add Nginx user to the backend group: This is often the cleanest solution.
bash sudo usermod -a -G php-fpm nginx # Replace php-fpm with backend group # Restart Nginx and backend for changes to take effect sudo systemctl restart nginx sudo systemctl restart php-fpm
* Change socket ownership and permissions (often configured in backend service):
Modify the backend service's configuration (e.g.,/etc/php-fpm.d/www.conf) to ensure the socket is created with appropriate ownership and permissions. For PHP-FPM:
ini listen.owner = php-fpm listen.group = nginx # Set to Nginx's primary group listen.mode = 0660 # Owner and group can read/write
After changing, restart the backend service.
bash sudo systemctl restart php-fpm
* Manually change ownership/permissions (temporary or if backend cannot configure):
bash sudo chown php-fpm:nginx /run/app/app.sock # Owner php-fpm, Group nginx sudo chmod 660 /run/app/app.sock
Caution: Manual changes often revert when the backend service restarts and recreates the socket. Prefer configuring the backend service itself. - Check the socket file itself:
-
Check SELinux Status and Troubleshoot:
If standard permissions look fine, SELinux is the next most likely culprit.- Check SELinux status:
bash sestatus
If it showsSELinux status: disabledorpermissive, then SELinux is not the cause. If it'senabledandenforcing, proceed. - Check for SELinux denials: The
audit.logis where SELinux denial messages are recorded.
bash sudo grep "AVC" /var/log/audit/audit.log | grep "nginx" | tail -n 20 # Or using ausearch for more specific filtering sudo ausearch -c nginx --raw | audit2allow -l
You'll likely see a denial message indicatingnginxattempting to access a socket with an incorrect context, e.g.,access denied { connectto } for pid=... comm="nginx" path="/run/app/app.sock" scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_run_t:s0 tclass=sock_file. - Relabel the socket: If the socket was created or moved incorrectly, its SELinux context might be wrong.
restoreconcan fix this if a policy already exists for the location.
bash sudo restorecon -Rv /run/app/app.sock - Apply correct SELinux context persistently: For custom locations or if
restorecondoesn't help, you might need to tell SELinux what context a file in a specific path should have. For Nginx sockets,httpd_var_run_torhttpd_socket_tare common.
bash sudo semanage fcontext -a -t httpd_var_run_t "/run/app/app.sock" sudo restorecon -Rv /run/app/app.sock
If the socket is in a non-standard directory, you might need to label the directory too:
bash sudo semanage fcontext -a -t httpd_var_run_t "/path/to/custom_socket_dir(/.*)?" sudo restorecon -Rv /path/to/custom_socket_dir/ - Set SELinux booleans (use with caution): Sometimes, Nginx needs broader network access.
bash sudo setsebool -P httpd_can_network_connect on sudo setsebool -P httpd_can_network_connect_db on # If connecting to a database socket
httpd_can_network_connectallows Nginx to initiate outgoing network connections. For Unix sockets specifically, it's often more about the file context, but this boolean can sometimes resolve issues in less common setups.
- Check SELinux status:
-
Restart Services:
After making any changes to configuration, permissions, or SELinux, always restart both Nginx and your backend service.
bash sudo systemctl restart nginx sudo systemctl restart <your-backend-service> # e.g., php-fpm, gunicorn -
Test:
Attempt to access your application through Nginx and check the Nginx error logs (/var/log/nginx/error.log) for any new errors.
Code Examples
Here are common commands and configuration snippets you might use.
1. Nginx proxy_pass configuration:
# For a generic application (e.g., Node.js, Python app)
location / {
proxy_pass http://unix:/run/app/myapp.sock:/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
2. Nginx fastcgi_pass configuration for PHP-FPM:
# For PHP-FPM
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
3. Checking socket file details:
ls -l /run/php-fpm/php-fpm.sock
# Expected output similar to: srw-rw----. 1 php-fpm nginx 0 Jun 1 10:00 /run/php-fpm/php-fpm.sock
4. Changing PHP-FPM socket configuration (e.g., /etc/php-fpm.d/www.conf):
; Set the socket path
listen = /run/php-fpm/php-fpm.sock
; Set the owner and group for the socket
listen.owner = php-fpm
listen.group = nginx ; Ensure Nginx user (e.g., 'nginx' or 'www-data') is in this group or this group matches Nginx's primary group
; Set file permissions for the socket (0660 means owner/group can read/write)
listen.mode = 0660
5. Adding Nginx user to a group:
sudo usermod -a -G php-fpm nginx
6. Checking SELinux status and denials:
sestatus
sudo grep "AVC" /var/log/audit/audit.log | grep "nginx"
7. Correcting SELinux file contexts:
# To apply standard contexts based on policy (e.g., after copying a file)
sudo restorecon -Rv /run/php-fpm/php-fpm.sock
# To define a new persistent context for a path
sudo semanage fcontext -a -t httpd_var_run_t "/var/www/my-app/sockets(/.*)?"
sudo restorecon -Rv /var/www/my-app/sockets/
8. Setting SELinux booleans:
sudo setsebool -P httpd_can_network_connect on
9. Restarting services:
sudo systemctl restart nginx
sudo systemctl restart php-fpm # Replace with your backend service name
Environment-Specific Notes
The troubleshooting steps remain largely consistent across environments, but certain aspects become more prominent depending on where your Nginx server is deployed.
-
Cloud (AWS EC2, GCP, Azure VMs): If you're running on CentOS, RHEL, or Fedora-based cloud instances, SELinux is almost certainly enabled and enforcing. This is where you'll most frequently encounter SELinux context issues. Always check
sestatusandaudit.logearly in your troubleshooting process. Also, ensure that if your backend is on a different server (though less common for Unix sockets), any cloud provider firewalls (security groups, network security groups) allow traffic on the relevant TCP ports. For Unix sockets, this is contained within the same host. -
Docker/Container Environments:
- Inside the container: Permissions inside the container still matter. The user Nginx runs as within the container needs access to the socket path, also within the container. If you're using
USERdirectives in your Dockerfile, ensure Nginx's user has access. - Host-mounted sockets: If you're mounting a Unix socket from the host into a container (e.g.,
docker run -v /host/path/socket:/container/path/socket), both host permissions (including SELinux on the host) and container permissions are critical. The user inside the container (which Nginx uses) must have access to the host's socket file. This can be tricky due to UID/GID mismatches between host and container. I've often seen this require carefulchownandchmodon the host, or ensuring the container user is part of the correct group on the host if group access is leveraged. - Docker Compose/Kubernetes: Similar principles apply. Ensure your application creates its socket in a path accessible by Nginx, and that any persistent volume claims (for sockets, if applicable) respect permissions.
- Inside the container: Permissions inside the container still matter. The user Nginx runs as within the container needs access to the socket path, also within the container. If you're using
-
Local Development Environments:
- On personal machines (macOS, Windows with WSL2, or Linux desktops), SELinux or AppArmor are often disabled or run in permissive mode, making permission errors less common unless you've explicitly configured restrictive settings.
- You might be running Nginx and your backend as your own user, which simplifies permissions. However, it's good practice to emulate production settings by using dedicated users. If running Nginx as root locally (for quick testing, never in prod!), this error is unlikely unless the socket itself has
chmod 000.
Frequently Asked Questions
Q: What if I see "connection refused" instead of "permission denied"?
A: "Connection refused" indicates that Nginx successfully attempted to connect to the upstream, but the backend service actively refused the connection. This usually means the backend service is not running, is listening on a different address/port/socket than Nginx is configured for, or a firewall is blocking the TCP port (if using TCP instead of Unix sockets). It's not a permission issue on the socket file itself.
Q: Can I just disable SELinux to fix this?
A: While setting SELINUX=disabled in /etc/selinux/config or running setenforce 0 (permissive mode) will likely resolve the error, it's a significant security compromise and strongly discouraged for production environments. SELinux provides an essential layer of security. Always aim to correctly configure SELinux policies rather than disabling it. Use disabling only as a last-resort diagnostic step, and re-enable it immediately.
Q: My Nginx is running as www-data, but my PHP-FPM socket is owned by php-fpm. How do I allow Nginx access?
A: The most robust solution is to configure PHP-FPM to create the socket with appropriate group ownership and permissions. In your php-fpm.d/www.conf (or similar pool configuration), set listen.group = www-data (or whatever Nginx's primary group is) and listen.mode = 0660. After restarting PHP-FPM, the socket will be created with php-fpm:www-data ownership and permissions allowing both php-fpm and www-data to read/write. Alternatively, you can add the www-data user to the php-fpm group using sudo usermod -a -G php-fpm www-data.
Q: What is the httpd_can_network_connect SELinux boolean?
A: This SELinux boolean controls whether processes with the httpd_t context (which Nginx typically runs as) are allowed to initiate outgoing network connections. While it sounds generic, it can sometimes be relevant for Unix sockets if the socket is in a non-standard location or if the SELinux policy is particularly strict. However, for Unix sockets, explicitly labeling the socket file with an appropriate context like httpd_var_run_t using semanage fcontext and restorecon is often a more precise and preferred solution.
Related Errors
(none)