Main Configuration File

Posted on 18 2026

The main nginx configuration file lives at /etc/nginx/nginx.conf. It sets the global parameters that apply to the entire nginx process: how many workers run, how many connections each can handle, where the PID file lives, and what else to include.

The packaged nginx on Ubuntu ships with a reasonable default nginx.conf. This page replaces the default with a configuration suited to the February homelab server and the modular file structure established in the configuration introduction.

Worker processes and connections

The Ryzen 7 5700X has eight cores. Worker processes should match the core count for optimal performance:

# nginx.conf
# /etc/nginx/nginx.conf

user www-data;

# One worker per CPU core.
# "auto" detects the core count at startup.
worker_processes auto;

# Bind workers to CPU sets for better cache utilisation.
worker_cpu_affinity auto;

# Maximum simultaneous connections per worker.
# Total maximum connections = worker_processes * worker_connections
# For 8 cores at 1024 connections each = 8192 max connections
events {
    worker_connections 1024;

    # Accept multiple connections per worker in one go
    multi_accept on;

    # Use epoll for efficient connection handling on Linux
    use epoll;
}

# Maximum open file descriptors per worker process.
# Must be greater than worker_connections.
# Set to double worker_connections as a safe upper bound.
worker_rlimit_nofile 2048;

The source material sets worker_connections 2048 and worker_rlimit_nofile 4098. The worker_rlimit_nofile value should be meaningfully higher than worker_connections since each connection uses at least one file descriptor plus additional descriptors for upstream connections and log files. A value of 2 * worker_connections is a safe minimum; 4 * worker_connections is more conservative.

For a homelab server serving a small number of users, worker_connections 1024 is more than adequate. Scale up if the server is under sustained load.

Error logging

# Error log location and severity level
# Levels: debug, info, notice, warn, error, crit, alert, emerg
# Use "warn" in production; "debug" only when actively troubleshooting
error_log /var/log/nginx/error.log warn;

# PID file location
pid /var/run/nginx.pid;

Do not leave debug logging enabled in production. It generates enormous log volume and can expose sensitive information including request bodies and headers.

HTTP block and includes

The http block contains all HTTP-level configuration and the include directives that bring in the modular configuration files:

http {

    # ============================================================
    # Core HTTP includes
    # ============================================================

    # MIME types mapping
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Global HTTP settings (TLS, compression, logging, rate limits)
    include /etc/nginx/conf.d/*.conf;

    # Active virtual server configurations
    include /etc/nginx/sites-enabled/*.conf;

}

The standard Ubuntu nginx package uses conf.d/ for global HTTP settings and sites-enabled/ for virtual servers. This matches the layout established in the installation page and the configuration introduction.

Complete nginx.conf

The complete replacement for /etc/nginx/nginx.conf:

#
# /etc/nginx/nginx.conf
# Main nginx configuration file
# Roll Your Own Network
#

# Run as www-data for security
user www-data;

# Worker processes: auto-detect CPU count
worker_processes auto;

# CPU affinity: auto-bind workers to CPUs
worker_cpu_affinity auto;

# File descriptor limit per worker
# Must exceed worker_connections significantly
worker_rlimit_nofile 8192;

# Process ID file
pid /var/run/nginx.pid;

# Error log (production: warn; debugging: debug)
error_log /var/log/nginx/error.log warn;

# Connection settings
events {
    worker_connections 1024;
    multi_accept on;
    use epoll;
}

# HTTP settings
http {

    # MIME type mappings
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Global HTTP configuration
    # Includes TLS, compression, logging, rate limits, security headers
    include /etc/nginx/conf.d/*.conf;

    # Active virtual server configurations
    include /etc/nginx/sites-enabled/*.conf;

}

Replacing the default configuration

Back up the original and replace it:

sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.original
sudo tee /etc/nginx/nginx.conf << 'EOF'
user www-data;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 8192;
pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log warn;

events {
    worker_connections 1024;
    multi_accept on;
    use epoll;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*.conf;
}
EOF

Test the configuration:

sudo nginx -t

Expected output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Handling the default site

The packaged nginx includes a default site at /etc/nginx/sites-enabled/default or as conf.d/default.conf. This serves the nginx welcome page and should be removed once real virtual servers are configured:

# Remove the default site symlink if it exists
sudo rm -f /etc/nginx/sites-enabled/default

# Or remove the default.conf from conf.d if that is where it lives
sudo rm -f /etc/nginx/conf.d/default.conf

A catch-all default server block is added in the global HTTP settings to handle connections to unknown hostnames, returning 444 (no response) rather than serving the nginx default page.

Verifying worker configuration

After reloading, verify the correct number of worker processes are running:

ps aux | grep nginx

Expected output shows one master process (nginx: master process) and one worker process per CPU core (nginx: worker process).

Check the effective file descriptor limit:

# Find a worker process PID
NGINX_WORKER=$(pgrep -o -x nginx -u www-data)

# Check its file descriptor limit
cat /proc/$NGINX_WORKER/limits | grep "open files"

The soft limit should match worker_rlimit_nofile.

The system file descriptor limit

For worker_rlimit_nofile to take effect, the system-level file descriptor limit must be at least as high. On Ubuntu 24.04 with systemd, nginx manages this automatically via the service unit. Verify:

sudo systemctl show nginx | grep LimitNOFILE

If the value is lower than worker_rlimit_nofile, override it:

sudo mkdir -p /etc/systemd/system/nginx.service.d
sudo tee /etc/systemd/system/nginx.service.d/limits.conf << 'EOF'
[Service]
LimitNOFILE=65536
EOF

sudo systemctl daemon-reload
sudo systemctl restart nginx

worker_rlimit_nofile must always be greater than worker_connections. The source material sets them to 4098 and 2048 respectively, a ratio of approximately 2:1. For a server also handling proxy connections to backends (each of which uses additional file descriptors), a ratio of 4:1 or 8:1 is safer. The file descriptor limit is cheap to increase and expensive to hit unexpectedly.