Skip to content

nginx front

nginx fronts whichever port the current serve mode needs, since the edge itself binds only loopback or unprivileged ports. You rarely run this by hand: burrowee edge cli bootstrap and burrowee edge mode reconcile it for you automatically, and burrowee edge doctor --fix repairs it. This page is for understanding what's on disk and for manual repair.

sh
sudo burrowee edge nginx install|uninstall|apply|reconcile --mode frontier|lan

All four subcommands write /etc/nginx (or the Homebrew conf dir on macOS) and self-elevate under sudo when run unprivileged on Linux, so you don't need to prefix sudo yourself.

nginx install — the public SNI front

For a frontier edge. Writes an ssl_preread SNI-routing front: nginx inspects the TLS handshake's SNI name and proxies the raw bytes to the edge's loopback tls_listen port — it never terminates TLS and never touches a certificate. The edge's own certificates (this host's own cert plus every console-pushed custom-domain cert) all serve from behind this one front.

sh
sudo burrowee edge nginx install

Requires host_fqdn to already be set — burrowee edge cert and the bootstrap prompt both set it when they run. In order it:

  1. reads host_fqdn plus this edge's custom + random served domains from the signed console config, and builds the SNI match set (each domain as a literal, one ~^.+\.<apex>$ regex per random-domain apex),
  2. writes the map + a listen 443; ssl_preread on; stream server to the snippet path (below),
  3. wires the include into nginx.conf if it isn't already,
  4. reloads (or starts) nginx, then verifies the front actually answers on :443 — locally and through the public internet — instead of finishing silently. (doctor's own --no-external flag skips that second probe when you run it from there instead — see Operations.)
✓ host front config    :443 → 127.0.0.1:9443
✓ host front local      :443 listening
✓ host front external   https://edge.example.com reachable (426)

Flags: --home <dir> (required under sudo so it resolves the service user's config, not root's), --conf-dir <dir> (default /etc/nginx on Linux, /opt/homebrew/etc/nginx on macOS), --internal-port <port> (the loopback port nginx proxies to — defaults to whatever tls_listen currently resolves to, so a bare install reconciles rather than clobbering a console-pushed port).

When run under sudo on Linux, install also drops a narrow passwordless-sudo rule scoped to exactly the service user and the nginx-reconcile command, and prints:

installed passwordless nginx-reconcile rule for user "edge"

That rule is what lets the unprivileged edge daemon reconcile the root-owned front on every console-pushed domain/port change without a manual doctor --fix (see Operations → keeping the front in sync). Run install without sudo and it skips the rule (printing a note to re-run under sudo); nginx uninstall removes it again.

nginx reconcile --mode frontier|lan — the mode-aware form

This is what bootstrap and burrowee edge mode actually call. It installs the front the given mode needs and tears down the other mode's leftover front, so the two never coexist on disk:

  • --mode frontier — runs the equivalent of nginx install, then best-effort removes a stale LAN front.
  • --mode lan — sets up the LAN front below, then best-effort removes a stale frontier front.

With no --mode flag it derives the mode from the signed console config. doctor --fix and the daemon's own config-apply hook (when the console pushes a tls_listen/mode change) both shell out to this command — see Operations → keeping the front in sync.

The LAN front

A LAN edge needs nginx to own the externally-visible port and terminate TLS, since the edge itself only ever binds a plain loopback WebSocket. nginx reconcile --mode lan (or the legacy nginx apply, below) handles it:

  1. ensures the LAN certificate exists — a 10-year self-signed cert generated once at ~/.burrowee/edge/lan-cert/ and never touched again unless you delete it (see rotating it),
  2. writes an http-context server block — listen 8448 ssl; with the LAN cert, WebSocket-proxying to the edge's loopback lan_listen — into the snippet path (below),
  3. reloads (or starts) nginx.
LAN cert fingerprint: <sha256>
pin distributed via the next endpoint report

Gateways pick the pin up automatically from the edge's self-reported endpoints; CLI clients get it inside the relay blob they paste — nothing to distribute by hand for either.

nginx uninstall — tearing a front down

sh
sudo burrowee edge nginx uninstall            # remove the frontier stream front
sudo burrowee edge nginx uninstall --lan      # remove the LAN front instead

Removes the matching snippet file and reloads nginx (a missing snippet is a no-op). It leaves the include hook in nginx.conf in place — a clean, empty hook — and never touches tls_listen/host_fqdn/the host cert/lan_cert in the edge's own config.

File paths

The frontier front and the LAN front are two separate files, in different nginx include folders:

FrontPath
Frontier (SNI, install/reconcile --mode frontier, and the legacy apply)<conf-dir>/servers-stream/burrowee-edge-stream.conf
LAN (reconcile --mode lan, and the legacy apply's LAN block)<conf-dir>/servers/burrowee-edge-lan.conf (macOS) or <conf-dir>/conf.d/burrowee-edge-lan.conf (Linux)

The frontier file lives in a stream {} context — install idempotently ensures a top-level stream { include servers-stream/*.conf; } hook in nginx.conf (or just injects the include line if a stream {} block already exists there). The LAN file lives in the stock http {} include folder nginx already loads (servers/* on Homebrew, conf.d/*.conf on Debian/Ubuntu), so no extra hook is needed for it. (The classic trap on the frontier side: many distros also include conf.d/*.conf from inside http {}, where a stream {} block would be silently dead — the include must sit at the top level. Confirm nginx -V lists --with-stream.)

Rotating the LAN certificate

There's no rotate flag — the LAN cert is generated once and left alone, since clients pin its fingerprint. To force a new one: stop the service, delete ~/.burrowee/edge/lan-cert/ (or wherever lan_cert in the config points), and re-run sudo burrowee edge nginx reconcile --mode lan (or nginx apply) to regenerate it, then restart.

Treat this as a deliberate operator act, not routine hygiene — the cert is good for 10 years:

  • Gateways heal automatically — the new pin goes out in the edge's next endpoint report.
  • CLI clients do not. A CLI gets the pin inside the relay blob it pasted, and there is no push channel to update it — every CLI using this edge needs its relay blob re-pasted after a rotation.

The legacy apply command

sh
sudo burrowee edge nginx apply [--listen-tls <port>] [--listen-lan <port>] [--print]

Predates the install/reconcile split and still works for back-compat or manual use: it writes one combined stream file — a plain :443 TCP passthrough to the edge's TLS port (never terminating TLS itself; that still happens inside the edge, with the console-pushed certificates) alongside the LAN block, both in stream {} context — to the same frontier snippet path above. --print previews the rendered config on stdout with no writes and no certificate generation. Defaults: --listen-tls 443, --listen-lan 8448.

Prefer install/reconcile --mode for anything new — they're what bootstrap, mode, and doctor --fix use, and they keep the SNI-only frontier front (no certificate exposure to nginx) that apply's combined passthrough doesn't provide.

Port preflight

Before writing anything, apply probes its external ports (--listen-tls, --listen-lan). A port already held by nginx serving this same snippet is fine — re-applying is idempotent. A port held by anything else fails fast, naming the holder and the right flag to pick a different one, so a conflict never leaves a broken snippet on disk:

✗ port 8448 is already in use by another process (held by caddy pid 1234).
  Pick a different port and re-run, e.g.:
    burrowee edge nginx apply --listen-lan <nginx-port>
  (and set lan_listen=127.0.0.1:<edge-port> + lan_advertise_port=<nginx-port> in ~/.burrowee/edge/config)

QUIC is not fronted

If you enable a QUIC listener (quic_addr in the config), it is UDP — none of these fronts carry it. The edge binds it directly.