Web Server Advanced

Fixing Nginx worker_connections Limit Reached on Ubuntu 22.04 LTS

Troubleshoot and resolve Nginx 'worker_connections limit reached' errors on Ubuntu 22.04 LTS by adjusting Nginx configuration and system limits for high-traffic web applications.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Troubleshoot and resolve Nginx 'worker_connections limit reached' errors on Ubuntu 22.04 LTS by adjusting Nginx configuration and system limits for high-traffic web applications.

Nginx is renowned for its high performance and efficiency as a web server and reverse proxy. However, even the most robust systems can encounter bottlenecks under extreme load or misconfiguration. One common issue for high-traffic websites is the "Nginx workers connection limit reached" error, directly related to the worker_connections directive. This guide will walk you through diagnosing and resolving this critical performance limitation on an Ubuntu 22.04 LTS server.

Symptom & Error Signature

When the Nginx worker_connections limit is reached, users typically experience:

  • Slow website loading times.
  • Browser errors such as "Connection refused," "502 Bad Gateway," or "504 Gateway Timeout."
  • Inability to connect to the website during peak traffic.

The most definitive sign is an alert message in your Nginx error logs (commonly located at /var/log/nginx/error.log), indicating that the connection limit for a worker process has been hit.

2023/10/26 14:35:01 [alert] 1234#1234: *123456789 worker_connections limit reached, client: 192.168.1.100, server: example.com, request: "GET /index.html HTTP/1.1", host: "example.com"

In some configurations, especially with older Nginx versions or specific log levels, you might see [warn] or [info] instead of [alert], but the core message remains the same.

Root Cause Analysis

The "Nginx worker_connections limit reached" error fundamentally means that an individual Nginx worker process has exhausted its allocated capacity for managing concurrent client connections. Understanding the interplay of several Nginx and system-level directives is crucial for a complete resolution:

  1. worker_connections Directive: This Nginx directive, defined within the events block of nginx.conf, specifies the maximum number of simultaneous connections that a single Nginx worker process can open. This includes both incoming client connections and outgoing connections to upstream servers (e.g., PHP-FPM, Node.js, database). The total theoretical maximum connections Nginx can handle is worker_processes * worker_connections.

  2. File Descriptors (ulimit -n): Every connection Nginx handles consumes a file descriptor (FD). The worker_connections value must not exceed the maximum number of open file descriptors allowed for the Nginx process by the operating system. Linux systems have a default ulimit -n (max open files) that is often 1024 or 4096, which is frequently insufficient for busy Nginx servers. If worker_connections is set higher than ulimit -n, Nginx will still be limited by the operating system's constraint.

  3. worker_processes Directive: This directive determines how many Nginx worker processes run. Each worker process is an independent entity capable of handling worker_connections. If worker_processes is set too low (e.g., the default of 1), Nginx cannot fully utilize the server's CPU cores, leading to saturation of the single worker even if worker_connections is high.

  4. Backend Bottlenecks: While Nginx itself might be well-tuned, slow responses from backend applications (e.g., PHP-FPM, Gunicorn, databases) can cause Nginx connections to remain open longer than necessary. This prolonged connection state can prematurely consume worker_connections slots, even if Nginx's processing capacity is not the primary issue.

  5. Traffic Spikes or DDoS Attacks: A sudden and unexpected surge in legitimate traffic or a malicious Distributed Denial of Service (DDoS) attack can overwhelm even a perfectly tuned Nginx configuration, quickly exhausting connection limits.

Step-by-Step Resolution

Resolving this issue involves a combination of adjusting Nginx configuration directives and increasing system-level resource limits.

1. Verify Current Nginx Configuration and System Limits

First, gather information about your current Nginx setup and system limits.

  • Check Nginx worker_processes and worker_connections:

    grep -E "worker_processes|worker_connections" /etc/nginx/nginx.conf
    

    Typical output might look like:

    worker_processes auto;
        worker_connections 768;
    
  • Check ulimit -n for the Nginx process: Find an Nginx master or worker process ID:

    PID=$(ps aux | grep "[n]ginx: master process" | awk '{print $2}')
    # Or for a worker process:
    # PID=$(ps aux | grep "[n]ginx: worker process" | head -n 1 | awk '{print $2}')
    echo "Nginx Master PID: $PID"
    

    Now, check its file descriptor limits:

    sudo cat /proc/$PID/limits | grep "Max open files"
    

    This will show you the Soft and Hard limits for file descriptors. Alternatively, if Nginx runs as www-data user:

    sudo -u www-data bash -c 'ulimit -n'
    

    This shows the soft limit for the user.

2. Adjust worker_connections in Nginx Configuration

Increase the worker_connections value in your main Nginx configuration file, typically /etc/nginx/nginx.conf.

  1. Open the Nginx configuration file:

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

    # /etc/nginx/nginx.conf
    user www-data;
    worker_processes auto; # Or specify number of CPU cores, e.g., 4 or 8
    pid /run/nginx.pid;
    include /etc/nginx/modules-enabled/*.conf;
    
    events {
        # Increase this value. Common values are 1024, 2048, 4096, 8192, 16384, 32768.
        # Start with 4096 or 8192 and monitor.
        worker_connections 8192;
        # multi_accept on; # Uncomment for higher throughput if connections are short-lived
    }
    

The worker_connections value defines the maximum connections per worker process. The ideal value depends on your server's hardware, expected traffic, and the nature of connections (short-lived vs. long-lived). Do not set it excessively high without also increasing the system's file descriptor limits, as Nginx will still be constrained by the OS.

3. Increase System File Descriptor Limits for Nginx

This is a critical step. The worker_connections value cannot exceed the system's file descriptor limit for the Nginx process. We'll use the recommended Systemd override method.

  • Method A: Systemd Service Unit (Recommended for Ubuntu 22.04 LTS)

    Create a Systemd override file for the Nginx service. This ensures that your changes persist updates and specifically apply to the Nginx service.

    1. Create an override file for the Nginx service:

      sudo systemctl edit nginx
      

      This command will open an editor (usually nano or vi) for a new file /etc/systemd/system/nginx.service.d/override.conf.

    2. Add or modify the LimitNOFILE directive within the [Service] section:

      # /etc/systemd/system/nginx.service.d/override.conf
      [Service]
      LimitNOFILE=65536 # Or higher, e.g., 1048576 (1M)
      

      The LimitNOFILE value should be at least double your worker_connections setting to account for upstream connections, log files, etc. For high-traffic servers, values like 65536, 524288 (512K), or 1048576 (1M) are common.

    3. Save the file and exit the editor.

    4. Reload the Systemd manager configuration and restart Nginx:

      sudo systemctl daemon-reload
      sudo systemctl restart nginx
      

The LimitNOFILE value is the maximum number of file descriptors the Nginx service can open. This value must be greater than or equal to your worker_connections setting. A good rule of thumb is to set LimitNOFILE to at least 2 * worker_connections, or even higher (e.g., 4x) for busy proxy servers.

  • Method B: /etc/security/limits.conf (Less precise for specific services)

    While less granular than Systemd overrides for specific services, you can also modify /etc/security/limits.conf. This affects all processes launched by the specified user.

    1. Open the limits.conf file:

      sudo nano /etc/security/limits.conf
      
    2. Add or modify lines for the Nginx user (commonly www-data or nginx):

      # /etc/security/limits.conf
      # <domain>      <type>  <item>         <value>
      www-data        soft    nofile         65536
      www-data        hard    nofile         65536
      

      Replace www-data with your Nginx user if it's different.

    3. Save the file. For these changes to take effect, you typically need to reboot the server or at least log out and back in for the user, and then restart Nginx. The Systemd method is generally preferred for immediate, service-specific application.

Modifying limits.conf affects all processes run by the specified user. Ensure you understand the implications for other applications running under the same user account.

4. Adjust worker_processes (if necessary)

The worker_processes directive dictates how many worker processes Nginx will spawn. Each process independently handles connections up to its worker_connections limit.

  1. Open the Nginx configuration file:

    sudo nano /etc/nginx/nginx.conf
    
  2. Set worker_processes to auto or to the number of CPU cores available on your server.

    # /etc/nginx/nginx.conf
    worker_processes auto; # Recommended: Nginx will determine optimal number based on CPU cores
    

    If you prefer to manually specify, you can count your CPU cores with nproc or grep -c ^processor /proc/cpuinfo. For example, for a 4-core CPU:

    worker_processes 4;
    

worker_processes auto; is generally the best setting as Nginx will dynamically determine the optimal number of worker processes based on the server's CPU capabilities. Manual specification might be useful in niche cases where you want to reserve cores for other services.

5. Test Configuration and Restart Nginx

After making any changes to Nginx configuration or Systemd units, always test before applying.

  1. Test Nginx configuration syntax:

    sudo nginx -t
    

    You should see syntax is ok and test is successful. If there are errors, correct them before proceeding.

  2. Restart Nginx service:

    sudo systemctl restart nginx
    
  3. Verify the new limits for Nginx worker processes: After restarting, get the PID of a worker process:

    PID=$(ps aux | grep "[n]ginx: worker process" | head -n 1 | awk '{print $2}')
    sudo cat /proc/$PID/limits | grep "Max open files"
    

    Confirm that the Max open files (both soft and hard) reflects the LimitNOFILE you set in Systemd.

6. Monitor Performance and Logs

After implementing the changes, continuous monitoring is crucial to ensure the issue is resolved and to identify any new bottlenecks.

  • Nginx Error Logs: Keep an eye on /var/log/nginx/error.log for any recurrence of the worker_connections limit reached alert.

    sudo tail -f /var/log/nginx/error.log
    
  • System Resource Monitoring: Use tools like htop, atop, nmon, or netdata to monitor CPU utilization, memory usage, and network I/O.

    htop
    
  • Active Connections: Check the number of active network connections:

    sudo ss -tuna | grep ":80|:443" | wc -l
    
  • Nginx Status (if configured): If you have stub_status enabled, you can monitor Nginx's internal metrics:

    curl http://127.0.0.1/nginx_status
    

    (Ensure stub_status on; is configured in your Nginx server block, usually at location /nginx_status { ... }.)

Additional Considerations

  • Keepalive Timeout: For HTTP/1.x, if connections are held open for too long due to high keepalive_timeout settings and clients are not reusing them, connections can be tied up unnecessarily. Consider reducing keepalive_timeout if applicable.
  • Upstream Connection Pooling: If Nginx is acting as a reverse proxy, ensure your upstream servers (e.g., PHP-FPM, Gunicorn) can handle the increased load. Also, consider using keepalive directives in your upstream blocks to reuse connections to backend servers.
  • DDoS Protection & Rate Limiting: If the connection limit issues are due to malicious traffic or overwhelming traffic spikes, consider implementing Nginx's built-in limit_req and limit_conn modules for rate limiting, or deploy a Web Application Firewall (WAF) or dedicated DDoS mitigation service.
  • Horizontal Scaling: For extremely high-traffic scenarios that exceed the capacity of a single server, consider scaling horizontally by distributing traffic across multiple Nginx servers using a load balancer (e.g., HAProxy, AWS ELB, Nginx Plus).

By systematically applying these steps, you can effectively resolve the "Nginx workers connection limit reached" error, ensuring your Ubuntu 22.04 LTS server remains performant and responsive under heavy loads.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.