NGINX

NGINX is a high-performance web server and reverse proxy that serves as the front door to your self-hosted services. It handles incoming requests and routes them to the appropriate backend services.

Why Use NGINX?

Single Entry Point: One IP/port serves multiple services through different domains or paths.

SSL/TLS Termination: Handle HTTPS encryption in one place instead of configuring it for each service.

Load Balancing: Distribute traffic across multiple backend instances.

Caching: Improve performance by caching static content.

Security: Add authentication, rate limiting, and access controls.

Core Concepts

Reverse Proxy

A reverse proxy sits between clients and your backend services:

Client → NGINX → Backend Service

NGINX receives requests and forwards them to the appropriate service based on the domain name or URL path.

Server Blocks

Server blocks (similar to Apache's virtual hosts) define how NGINX handles requests for different domains.

server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://localhost:8080;
    }
}

Basic Configuration

Installation

# Ubuntu/Debian
sudo apt update
sudo apt install nginx

# Start and enable
sudo systemctl start nginx
sudo systemctl enable nginx

Directory Structure

/etc/nginx/
├── nginx.conf              # Main configuration
├── sites-available/        # Available site configs
├── sites-enabled/          # Active site configs (symlinks)
└── conf.d/                 # Additional configs

Simple Reverse Proxy

Create /etc/nginx/sites-available/myservice:

server {
    listen 80;
    server_name myservice.local;
    
    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Enable the site:

sudo ln -s /etc/nginx/sites-available/myservice /etc/nginx/sites-enabled/
sudo nginx -t  # Test configuration
sudo systemctl reload nginx

Common Patterns

WebSocket Support

Required for services like Home Assistant:

location / {
    proxy_pass http://localhost:8123;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Large File Uploads

For services like Immich (photo uploads):

server {
    client_max_body_size 50000M;  # 50GB max upload
    
    location / {
        proxy_pass http://localhost:2283;
    }
}

Increased Timeouts

For long-running requests (LLM responses):

location / {
    proxy_pass http://localhost:3000;
    proxy_read_timeout 600s;
    proxy_connect_timeout 150s;
}

Path-Based Routing

Serve multiple services from one domain:

server {
    listen 80;
    server_name myserver.local;
    
    location /jellyfin/ {
        proxy_pass http://localhost:8096/;
    }
    
    location /homeassistant/ {
        proxy_pass http://localhost:8123/;
    }
}

Static File Serving

Serve files directly without proxying:

server {
    listen 80;
    server_name files.local;
    
    root /var/www/files;
    index index.html;
    
    location / {
        try_files $uri $uri/ =404;
    }
}

SSL/TLS Configuration

Using Let's Encrypt

# Install Certbot
sudo apt install certbot python3-certbot-nginx

# Obtain certificate
sudo certbot --nginx -d example.com

# Auto-renewal is configured automatically

Manual SSL Configuration

server {
    listen 443 ssl http2;
    server_name example.com;
    
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    
    # Modern SSL configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    location / {
        proxy_pass http://localhost:8080;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Security Headers

Add security headers to all responses:

server {
    # ... other config ...
    
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
    # HSTS (only after testing HTTPS works)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}

Authentication

Basic Authentication

# Create password file
sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd username
server {
    location / {
        auth_basic "Restricted Access";
        auth_basic_user_file /etc/nginx/.htpasswd;
        
        proxy_pass http://localhost:8080;
    }
}

IP Whitelist

location / {
    allow 192.168.1.0/24;  # Local network
    deny all;
    
    proxy_pass http://localhost:8080;
}

Performance Tuning

Caching

# Define cache path
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;

server {
    location / {
        proxy_cache my_cache;
        proxy_cache_valid 200 60m;
        proxy_cache_use_stale error timeout http_500 http_502 http_503;
        
        proxy_pass http://localhost:8080;
    }
}

Compression

gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss;

Connection Limits

# Limit connections per IP
limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
    limit_conn addr 10;  # Max 10 connections per IP
}

Useful Commands

# Test configuration
sudo nginx -t

# Reload configuration (no downtime)
sudo systemctl reload nginx

# Restart NGINX
sudo systemctl restart nginx

# View error log
sudo tail -f /var/log/nginx/error.log

# View access log
sudo tail -f /var/log/nginx/access.log

# Check NGINX status
sudo systemctl status nginx

Troubleshooting

502 Bad Gateway

Cause: Backend service is down or unreachable

Solutions:

# Check if backend is running
curl http://localhost:8080

# Check NGINX error log
sudo tail /var/log/nginx/error.log

# Verify proxy_pass URL is correct

413 Request Entity Too Large

Cause: Upload size exceeds limit

Solution:

client_max_body_size 100M;

Permission Denied

Cause: NGINX can't access files or sockets

Solutions:

# Check file permissions
ls -la /path/to/file

# Check SELinux (if enabled)
sudo setsebool -P httpd_can_network_connect 1

Best Practices

Separate Config Files: One file per service in sites-available/

Test Before Reload: Always run nginx -t before reloading

Use Includes: Break complex configs into reusable snippets

Log Rotation: Configure logrotate to prevent disk fill

Monitor Logs: Regularly check error logs for issues

Keep Updated: Apply security updates promptly

Example: Complete Service Configuration

# /etc/nginx/sites-available/jellyfin
server {
    listen 80;
    server_name jellyfin.example.com;
    
    # Redirect to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name jellyfin.example.com;
    
    # SSL configuration
    ssl_certificate /etc/letsencrypt/live/jellyfin.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/jellyfin.example.com/privkey.pem;
    
    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Strict-Transport-Security "max-age=63072000" always;
    
    # Large file support for media
    client_max_body_size 0;
    
    location / {
        proxy_pass http://localhost:8096;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Buffering off for streaming
        proxy_buffering off;
    }
    
    # Logging
    access_log /var/log/nginx/jellyfin.access.log;
    error_log /var/log/nginx/jellyfin.error.log;
}

Related Topics: