bundlewrap/bundles/nginx
CroneKorkN 524ad6e89b
nginx: SSE-friendly proxy_pass + unconditional $connection_upgrade map
Two coupled changes that let every proxy_pass vhost serve both WS and
SSE without per-vhost flags or template conditionals:

1) nginx.conf: $connection_upgrade map is now always defined (drop
   the % if has_websockets: gate), and the '' branch returns "" instead
   of "close". With "" + proxy_http_version 1.1, nginx maintains
   keep-alive to upstream for non-WS clients — which is what SSE
   requires. WS clients still get Connection: upgrade as before.

2) data/nginx/proxy_pass.conf: drop the % if websockets: conditional.
   Always set proxy_http_version 1.1 + Upgrade + Connection via the
   map, plus proxy_buffering off and proxy_read_timeout 1h for SSE.

Effects on existing vhosts:
- home.server's Proxmox WS vhost: unchanged behavior (the WS branch
  was already setting these headers). Gains the ability to also
  serve SSE if ever needed.
- All other proxy_pass vhosts (Nextcloud, Freescout, YOURLS, Gitea,
  etc.): get keep-alive to upstream (minor latency win) and unbuffered
  pass-through (slight throughput cost on huge responses, neutral
  for typical web app traffic).

Dead but harmless: bundles/nginx/metadata.py still defaults
nginx/has_websockets to False, and proxmox-ve/grafana still set it
to True. The flag is now a no-op; clean up in a separate pass.
2026-05-10 22:12:03 +02:00
..
files nginx: SSE-friendly proxy_pass + unconditional $connection_upgrade map 2026-05-10 22:12:03 +02:00
items.py nginx: move 80.conf to sites-available so it's actually included 2026-05-10 19:59:17 +02:00
metadata.py yurlls fix monitoring and use dehydrated certs 2025-06-29 14:46:39 +02:00
README.md nginx/README: how port 80 is served + vm/cores requirement 2026-05-10 20:47:47 +02:00

nginx

Webserver. Per-node vhosts in nginx/vhosts; per-vhost templates in data/nginx/*.conf.

How port 80 is served

The bundle ships a fixed 80.conf to /etc/nginx/sites-available/80.conf (picked up by the sites-enabled/ symlink) that handles all port-80 traffic across vhosts:

  1. ACME HTTP-01 challenges (/.well-known/acme-challenge/) are served from /var/lib/dehydrated/acme-challenges/.
  2. All other port-80 requests are 301-redirected to https://$host$request_uri.

Per-vhost templates only declare listen 443 ssl http2;, so they don't need their own port-80 server blocks. If you need vhost- specific port-80 behaviour (e.g. plain-HTTP without redirect), override 80.conf or add a per-vhost block.

Required metadata

  • vm/cores — read directly by items.py for worker_processes. No default; bw items <node> raises at item-build time if missing. Typically supplied by the vm bundle / hetzner-vm group; double- check on bare-metal hosts.
  • nginx/vhosts — dict of vhost-name → vhost-config.
  • nginx/modules — list of dynamic modules to load.

Cross-namespace

items.py reads letsencrypt/domains to skip emitting a per-vhost HTTPS block when LE hasn't declared the domain yet — keeps the bundle loadable on a node where letsencrypt isn't fully wired up.