Functional BytesFunctional Bytes

Configuring Nginx


Configuration

Redirect HTTP to HTTPS via 301

server {
  listen *:80;

  server_name k3s.functionalbytes.com;
  server_tokens off; ## Don't show the nginx version number

  location / {
    return 301 https://k3s.functionalbytes.com:443$request_uri;
  }
}

Including SSL Certificate for HTTPS

ssl_certificate /secret/keys/proxy.functionalbytes.com.fullchain;
ssl_certificate_key /secret/keys/proxy.functionalbytes.com.key;

WebSocket Connections

See: [nginx Documentation] WebSocket proxying

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

Load Balancing

See: [nginx Documentation] HTTP Load Balancing

upstream k3s_servers {
  # Round Robin by default - can add "weight=#" after each server for weighting, e.g.:
  # server k3s-s1.functionalbytes.com:80 weight=3;
  # With this set and nothing on k3s-s2 (default = 1), 3 out of 4 requests will be sent to k3s-s1

  # Alternatively one of the following can be uncommented for other load balancing options

  # Uncomment the line below to use the one with the fewest connections
  # least_conn;

  # Uncomment to send requests to the server based on a hash of the first 3 octets of their IP (guaranteeing requests go to the same server unless it is unavailable)
  # ip_hash;
  server k3s-s1.functionalbytes.com:80;
  server k3s-s2.functionalbytes.com:80;
}

Basic Full Example

upstream k3s_servers {
  server k3s-s1.functionalbytes.com:80;
  server k3s-s2.functionalbytes.com:80;
}

server {
  listen *:80;

  server_name k3s.functionalbytes.com;
  server_tokens off; ## Don't show the nginx version number, a security best practice

  location / {
    return 301 https://k3s.functionalbytes.com:443$request_uri;
  }
}

server {
  listen 443 ssl http2;

  server_name k3s.functionalbytes.com;
  server_tokens off; ## Don't show the nginx version number, a security best practice

  ssl_certificate /secret/keys/proxy.functionalbytes.com.fullchain;
  ssl_certificate_key /secret/keys/proxy.functionalbytes.com.key;

  # Allow large attachments
  client_max_body_size 128M;

  location / {
    proxy_pass http://k3s_servers;
    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;
  }
}

Adding Custom Stream Configs

  1. Add an include directive to /config/nginx/nginx.conf

    File: /config/nginx/nginx.conf

    stream {
        include /config/nginx/stream-confs/*;
    }
    
  2. Add the stream configs in /config/nginx/stream-confs/, e.g.:

    File: /config/nginx/stream-confs/k3s.conf

    upstream k3s_servers {
      server k3s-s1.functionalbytes.com:6443;
      server k3s-s2.functionalbytes.com:6443;
    }
    
    server {
      listen *:6443;
    
      ssl_certificate /secret/keys/proxy.functionalbytes.com.fullchain;
      ssl_certificate_key /secret/keys/proxy.functionalbytes.com.key;
    
      proxy_pass k3s_servers;
    }
    

Commands

Reload the nginx config without any downtime (sends SIGHUP signal):

nginx -s reload

Verify configuration is valid:

nginx -t

PID Issue on Some Alpine Linux Docker Images

Note: I am currently using nginx:1.21-alpine (official) which does not have this issue, but do still see the issue with linuxserver/nginx:1.20.2 (linuxserver.io image that I was using this on Unraid)

There is an issue where the PID file isn't created in the alpine linux version of nginx docker image. Running the reload command will result in the following error:

> nginx -s reload
nginx: [error] open() "/run/nginx/nginx.pid" failed (2: No such file or directory)

Two options for fixing this:

  1. Create the required directory

    File: Dockerfile

    ...
    RUN mkdir -p /run/nginx
    ...
    
  2. Override the PID location
    nginx -g 'pid /tmp/nginx.pid; daemon off;'
    

References