Understanding Nginx on NixOS

Nginx is my preferred reverse proxy solution, which I use extensively in both Kubernetes clusters as an ingress controller and on traditional servers. As a reverse proxy, it excels at:

  • Routing traffic to different services on the same machine
  • Terminating TLS connections efficiently
  • Managing and automating TLS certificate lifecycle
  • Load balancing and high availability
  • Providing robust caching capabilities

While setting up Nginx can be complex on traditional Linux distributions, NixOS makes it remarkably straightforward with its declarative configuration approach.

Basic Nginx Configuration with ACME

Here’s a basic example from my server’s configuration.nix that demonstrates setting up Nginx with Let’s Encrypt certificate automation:

security.acme.acceptTerms = true;
security.acme.certs = {
    "nix-ish.xyz".email = "alexander@holte-davidsen.no";
};

services.nginx = {
    enable = true;
    recommendedProxySettings = true;
    recommendedTlsSettings = true;
    virtualHosts."nix-ish.xyz" = {
        enableACME = true;
        forceSSL = true;
        locations."/" = {
            proxyPass = "http://127.0.0.1:3030";
            proxyWebsockets = false;
            extraConfig =
                "proxy_ssl_server_name on;" +
                "proxy_pass_header Authorization;";
        };
    };
};

Understanding Each Configuration Component

Let’s break down the key components of this configuration:

ACME (Let’s Encrypt) Setup

The ACME configuration handles automatic SSL certificate management:

security.acme.acceptTerms = true;  # Required for Let's Encrypt
security.acme.certs = {
    "nix-ish.xyz".email = "alexander@holte-davidsen.no";  # Contact email for certificate alerts
};

Nginx Core Settings

The basic Nginx service configuration enables important security features:

services.nginx = {
    enable = true;
    recommendedProxySettings = true;  # Enables secure proxy headers
    recommendedTlsSettings = true;    # Enables secure TLS configuration

Virtual Host Configuration

Virtual hosts allow you to serve multiple domains from one server:

virtualHosts."nix-ish.xyz" = {
    enableACME = true;    # Enable automatic certificate management
    forceSSL = true;      # Redirect HTTP to HTTPS

Advanced Configuration Examples

Multiple Services on Different Paths

Here’s how to route different services on the same domain:

virtualHosts."nix-ish.xyz" = {
    enableACME = true;
    forceSSL = true;
    locations = {
        "/" = {
            proxyPass = "http://127.0.0.1:3000";  # Main application
        };
        "/api/" = {
            proxyPass = "http://127.0.0.1:8080";  # API server
        };
        "/static/" = {
            root = "/var/www";  # Static file serving
            extraConfig = "expires 30d;";  # Enable caching
        };
    };
};

Load Balancing Configuration

For high-availability setups:

upstream backend {
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    keepalive 32;
}

virtualHosts."nix-ish.xyz" = {
    locations."/" = {
        proxyPass = "http://backend";
        extraConfig = ''
            proxy_next_upstream error timeout invalid_header http_500;
            proxy_connect_timeout 2;
        '';
    };
};

Security Considerations

Always implement these security measures:

  1. HTTP/2 Support
services.nginx = {
    enableHTTP2 = true;
    # ... other settings
};
  1. Security Headers
virtualHosts."nix-ish.xyz" = {
    extraConfig = ''
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-Content-Type-Options "nosniff";
        add_header X-XSS-Protection "1; mode=block";
    '';
};

Monitoring and Maintenance

Logging Configuration

Enable detailed logging for better monitoring:

services.nginx = {
    appendHttpConfig = ''
        log_format detailed '$remote_addr - $remote_user [$time_local] '
                          '"$request" $status $body_bytes_sent '
                          '"$http_referer" "$http_user_agent" '
                          '$request_time';
        access_log /var/log/nginx/access.log detailed;
    '';
};

Automatic Certificate Renewal

Let’s Encrypt certificates are automatically renewed by the ACME service, but you can check the status:

systemctl status acme-nix-ish.xyz.service

Conclusion

NixOS makes Nginx configuration clear and maintainable through its declarative approach. This setup provides a solid foundation for serving web applications with automatic HTTPS support. Remember to:

  • Keep your NixOS configuration in version control
  • Regularly test your Nginx configuration
  • Monitor certificate renewal status
  • Review logs for security issues
  • Keep your NixOS system updated

The beauty of NixOS is that once configured, you can replicate this exact setup on any other NixOS system with confidence that it will work the same way.