Troubleshooting Nginx worker_connections Limit Reached on Ubuntu 20.04 LTS

Resolve Nginx 'worker_connections limit reached' errors on Ubuntu 20.04 LTS by optimizing server and Nginx configurations for high traffic. Boost performance!


Resolve Nginx 'worker_connections limit reached' errors on Ubuntu 20.04 LTS by optimizing server and Nginx configurations for high traffic. Boost performance!

When managing high-traffic web services, encountering the "Nginx workers connection limit reached" error can be a significant performance bottleneck, leading to service degradation or outright unavailability. This guide provides a comprehensive, technical walkthrough to diagnose and resolve this common issue on an Ubuntu 20.04 LTS system running Nginx, ensuring your web server can handle the anticipated load.

Symptom & Error Signature

Users attempting to access your website or web application may experience:

  • 502 Bad Gateway errors
  • 504 Gateway Timeout errors
  • Connection refused messages
  • Extremely slow page loading or incomplete content delivery

In your Nginx error logs (typically located at /var/log/nginx/error.log), you will observe entries similar to these:

2023/10/26 10:30:05 [crit] 12345#12345: *1234567 accept() failed (24: Too many open files)
2023/10/26 10:30:06 [alert] 12346#12346: *1234568 could not accept connection, worker_connections are not enough
2023/10/26 10:30:07 [crit] 12347#12347: *1234569 accept() failed (102: Network is unreachable) while accepting new connection on 0.0.0.0:80

System logs (e.g., dmesg or journalctl) might also show "kernel: VFS: file-max limit reached" or similar entries related to file descriptor exhaustion.

Root Cause Analysis

The "Nginx worker_connections limit reached" error signifies that an Nginx worker process has hit the maximum number of simultaneous connections it's configured to handle. This can stem from several underlying factors:

  1. Insufficient worker_connections: The primary cause is often a worker_connections directive in nginx.conf that is set too low for the current traffic demands. Nginx's default configuration is conservative and may not suit production environments.
  2. System-wide File Descriptor Limits (ulimit -n): Each connection (including client connections, upstream connections, and file handles) consumes a file descriptor. The operating system imposes a limit on the number of file descriptors an individual process or user can open. If this nofile (number of files) limit is lower than Nginx's worker_connections, Nginx cannot effectively utilize its configured capacity.
  3. Low worker_processes: While less direct, having too few worker_processes means the burden of all connections is distributed among fewer processes, potentially causing each to hit its worker_connections limit faster, especially on multi-core systems.
  4. Slow Upstream Servers or Application Issues: If your backend application servers (e.g., PHP-FPM, Node.js, Python Gunicorn) are slow to respond or frequently hang, Nginx worker processes will hold open connections longer, exhausting available worker_connections even with moderate traffic.
  5. Persistent Connections (Keepalives): While beneficial for performance, excessively long keepalive_timeout settings on high-traffic sites can tie up worker connections for clients that are no longer active, effectively reducing the number of available slots for new connections.
  6. High Traffic Volume: A sudden, legitimate surge in traffic (e.g., a viral event) or a Distributed Denial of Service (DDoS) attack can push a well-configured server beyond its capacity.

Step-by-Step Resolution

This section details how to systematically increase Nginx's connection handling capacity and address underlying system limits.

1. Assess Current Nginx Configuration

First, let's examine your current Nginx configuration to understand the baseline.

# Locate Nginx configuration files
sudo nginx -t

# Check current worker_connections value
grep -r "worker_connections" /etc/nginx/

# Check current worker_processes value
grep -r "worker_processes" /etc/nginx/

2. Increase worker_connections in Nginx Configuration

The worker_connections directive, located within the events block of your nginx.conf, defines the maximum number of simultaneous connections that a single worker process can open.

  1. Open the Nginx main configuration file:

    sudo nano /etc/nginx/nginx.conf
    
  2. Locate the events block and modify worker_connections:

    Find the events { ... } block. If worker_connections is present, increase its value. If not, add it. A common starting point is 4096, 8192, or even 16384 for high-traffic servers. The absolute theoretical maximum is 65535, but practical limits apply based on RAM and OS configuration.

    events {
        worker_connections 8192; # Or 4096, 16384, etc.
        # multi_accept on; # Consider uncommenting for very high traffic
    }
    

    Each connection consumes a small amount of RAM. While increasing worker_connections significantly improves concurrency, be mindful of your server's available memory. Overly aggressive values without sufficient RAM can lead to swapping and performance degradation.

  3. Test Nginx configuration for syntax errors:

    sudo nginx -t
    

    You should see syntax is ok and test is successful.

  4. Reload Nginx to apply changes:

    sudo systemctl reload nginx
    

3. Adjust System-Wide Open File Limits (ulimit -n)

Nginx worker processes are constrained by the operating system's per-process file descriptor limits. If this limit (often referred to as nofile) is lower than your worker_connections setting, Nginx won't be able to reach its configured capacity.

  1. Check the current nofile limit for the Nginx process:

    First, find the PID of an Nginx worker process:

    ps aux | grep nginx | grep worker
    

    Then, check its limits:

    cat /proc/<Nginx_Worker_PID>/limits | grep "Max open files"
    

    Replace <Nginx_Worker_PID> with an actual PID from the ps aux command.

  2. Modify /etc/security/limits.conf:

    Edit the file /etc/security/limits.conf to set a higher nofile limit for the user Nginx runs as (typically www-data on Ubuntu).

    sudo nano /etc/security/limits.conf
    

    Add the following lines at the end of the file, replacing www-data if your Nginx user is different:

    # Nginx limits
    www-data        soft    nofile          65535
    www-data        hard    nofile          65535
    
    • soft: The current limit, which can be increased by the user up to the hard limit.
    • hard: The maximum limit that can be set.

    Incorrect entries in limits.conf can prevent users or services from logging in or starting. Ensure the format is correct. The 65535 value should be equal to or higher than your worker_connections setting.

  3. Ensure pam_limits.so is enabled:

    For limits.conf to take effect, the pam_limits.so module must be enabled in PAM. Check the following files:

    sudo nano /etc/pam.d/common-session
    sudo nano /etc/pam.d/common-session-noninteractive
    

    Ensure that the line session required pam_limits.so is present and uncommented in both.

  4. Configure Systemd for Nginx service:

    For services managed by Systemd (like Nginx), limits.conf might not be fully effective without additional configuration. It's best practice to set the LimitNOFILE directly in a Systemd override file.

    Create or edit the override file for Nginx:

    sudo systemctl edit nginx.service
    

    This will open a new editor session for /etc/systemd/system/nginx.service.d/override.conf. Add the following content:

    [Service]
    LimitNOFILE=65535
    

    Save and exit the editor.

  5. Reload Systemd and restart Nginx:

    sudo systemctl daemon-reload
    sudo systemctl restart nginx
    
  6. Verify the new nofile limit:

    After restarting, check the Nginx worker process limits again:

    cat /proc/$(sudo systemctl show --property MainPID nginx | cut -d'=' -f2)/limits | grep "Max open files"
    

    The Max open files value should now reflect your new setting (e.g., 65535).

4. Optimize worker_processes

worker_processes defines how many Nginx worker processes will run. A good rule of thumb is to set this to the number of CPU cores available on your server.

  1. Count your CPU cores:

    grep processor /proc/cpuinfo | wc -l
    
  2. Edit nginx.conf:

    sudo nano /etc/nginx/nginx.conf
    

    Find the worker_processes directive at the top of the file. You can either set it to auto (recommended for Nginx 1.8+) or a specific number (e.g., 4 for a quad-core CPU).

    worker_processes auto; # Nginx will automatically determine the optimal number
    # worker_processes 4; # Or set a specific number like 1, 2, 4, etc.
    
  3. Test configuration and reload Nginx:

    sudo nginx -t
    sudo systemctl reload nginx
    

5. Fine-tune TCP Connection Recycling (Ephemeral Ports)

When Nginx acts as a reverse proxy, it establishes outbound connections to upstream servers. If your server is under extremely heavy load, it might exhaust the available ephemeral ports for outbound connections, even if Nginx itself has enough worker_connections for inbound client traffic.

  1. Increase the ephemeral port range:

    sudo nano /etc/sysctl.conf
    

    Add or modify the following line to expand the range of local ports available for outgoing connections:

    net.ipv4.ip_local_port_range = 1024 65000
    
  2. Apply sysctl changes:

    sudo sysctl -p
    

6. Review Nginx Keepalive Settings (Optional, but Recommended)

Nginx's keepalive_timeout and keepalive_requests settings affect how long Nginx keeps a connection open after a request and how many requests can be served over a single connection, respectively. While generally good for performance, excessively high values on very busy sites can tie up worker connections.

  1. Inspect Nginx configuration for keepalive settings:

    grep -r "keepalive_timeout" /etc/nginx/
    grep -r "keepalive_requests" /etc/nginx/
    
  2. Consider adjustments in http, server, or location blocks:

    • keepalive_timeout 60s;: A value between 15-75 seconds is common. If users are very interactive, a higher value is good; for mostly static content, a lower value might free up connections faster.
    • keepalive_requests 100;: Default is 100. For APIs or highly interactive sites, increasing this to 1000 or more can be beneficial.

    Adjust these values based on your application's specific traffic patterns and client behavior. Always test changes thoroughly.

    http {
        # ... other http settings
        keepalive_timeout 60s;
        keepalive_requests 200;
        # ...
    }
    
  3. Test configuration and reload Nginx:

    sudo nginx -t
    sudo systemctl reload nginx
    

By following these steps, you will significantly enhance Nginx's capacity to handle simultaneous connections, resolving the "worker_connections limit reached" error and improving the overall stability and performance of your web server on Ubuntu 20.04 LTS. Remember to monitor your server's resources (CPU, RAM, open files) after making changes to ensure optimal performance.