Files
ansible-role-nginx/README.md
T
2026-06-24 17:29:12 -04:00

8.0 KiB

nginx (Ansible role)

Installs and configures nginx for simple homelab / local-network setups, where most sites are "proxy a domain to a backend over HTTPS" and share a single wildcard cert.

This role exists because most of my nginx configs were nearly identical — same SSL params, same proxy headers, same redirect-to-HTTPS pattern. The role captures that shared shape, so each new service is a few lines of YAML instead of a copy-pasted server block.

Not for production. No Let's Encrypt automation, no per-vhost cert provisioning, no cleanup for sites removed from inventory, snakeoil cert as default. Override what you need.

Tested on Debian (bookworm/trixie) and Ubuntu (jammy/noble).


Quick start

- hosts: nginx
  become: true
  roles:
    - role: nginx
      vars:
        # Override the default snakeoil cert with your real one
        nginx_ssl_certificate:     /etc/nginx/ssl/lab.local/fullchain.pem
        nginx_ssl_certificate_key: /etc/nginx/ssl/lab.local/privkey.pem

        nginx_sites:
          - name: default                                     # keep the catch-all
            template_path: site-configs/default.conf.j2

          - name: vaultwarden                                 # your service
            domain_names:
              - vault.lab.local
            upstream_url: http://192.168.1.10:8080

That's it. vault.lab.local now serves over HTTPS, redirects plain HTTP → HTTPS, uses the shared cert, and proxies everything to 192.168.1.10:8080.


Per-site options (nginx_sites[*])

Key Type Default Notes
name string required Filename in sites-available/.
domain_names list required * Values for server_name.
upstream_url string required * proxy_pass target (e.g. http://host:port).
ssl bool true Enable HTTPS listener + cert.
allow_http bool false Also serve content over plain HTTP. By default HTTP → 301 → HTTPS.
websockets bool false Include websockets.conf (upgrade headers).
max_upload_size string unset client_max_body_size value, e.g. "50m".
extra_parameters string unset Freeform nginx directives injected into the server block.
https_port int 443 Override HTTPS listen port.
http_port int 80 Override HTTP listen port.
ssl_certificate path nginx_ssl_certificate Per-site cert override.
ssl_certificate_key path nginx_ssl_certificate_key Per-site key override.
enabled bool nginx_site_enabled_by_default Create the sites-enabled/ symlink. Setting false removes it.
template_path string nginx_site_config_template Use a custom template (path relative to templates/).
conf_file path unset Use a static file instead of the template (path relative to playbook).

* Required when using the built-in template. If you supply conf_file or a custom template_path, only name is required.


Role variables

Variable Default Purpose
nginx_ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem Default cert path for all sites.
nginx_ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key Default key path.
nginx_resolver 127.0.0.53 valid=300s Global DNS resolver (used by OCSP stapling). Override for LAN DNS.
nginx_resolver_timeout 5s
nginx_site_enabled_by_default true Whether sites without an explicit enabled get symlinked.
nginx_delete_default_site_config false Delete Debian's sites-available/default (only matters if you remove the default entry from nginx_sites).
nginx_site_config_template nginx-site.conf.j2 Default template used when a site has no template_path/conf_file.
nginx_sites one entry (the catch-all default site) Your list of sites.
nginx_snippets [proxy-headers, ssl-params, websockets, fastcgi-php] Static snippets dropped into /etc/nginx/snippets/.
nginx_conf_d_templates [resolver.conf] Templated files rendered into /etc/nginx/conf.d/.

Path variables (nginx_site_config_path, nginx_site_enabled_path, nginx_snippets_path, nginx_conf_d_path) exist for completeness and default to Debian's canonical locations. You usually shouldn't touch them.


The default catch-all site

The shipped default entry (name: default) renders templates/site-configs/default.conf.j2, which:

  • Listens on :80 with default_server, 301-redirects any unknown Host: to HTTPS.
  • Listens on :443 ssl http2 with default_server, returns 404.

It exists so unknown-host traffic doesn't leak one of your real sites' certs/content via SNI scans. Keep it if you have more than one site; remove it (and set nginx_delete_default_site_config: true) if you have exactly one.


Custom static configs

For sites that don't fit the template — e.g. a static file server, a non-proxy site, an upstream block — write the full nginx config yourself and point at it:

- name: media
  conf_file: site-configs/media.conf   # path relative to your playbook

The role copies it verbatim into sites-available/.


Caveats

  • Snakeoil default cert. The role works out of the box but presents an untrusted self-signed cert. Set nginx_ssl_certificate(_key) to your real cert before serving anything real.
  • One shared cert by default. A single wildcard is the assumed homelab pattern. Per-site override via ssl_certificate / ssl_certificate_key works for the exceptions.
  • No removal cleanup. If you delete a site from nginx_sites, the existing sites-available/<name> and sites-enabled/<name> are not removed. Clean up manually or set enabled: false and let the symlink task remove the link.
  • Debian/Ubuntu only. Paths assume the Debian-style sites-available/ + sites-enabled/ split. Won't work on Alpine, RHEL, etc. without surgery.
  • No Let's Encrypt / ACME. Bring your own certs.
  • Resolver default assumes systemd-resolved. Override nginx_resolver if you use a different local resolver (e.g. "dns.home valid=300s").