Host-nginx-gateway is a Kubernetes Gateway API implementation that uses your host-side nginx to provide HTTP routing. Existing gateways spawn their own proxy instance and exclusively hold ports like 80/443. Host-nginx-gateway reuses your host nginx to serve additional Gateways. It is designed to co-exist with your existing host-side nginx configuration, which makes it convenient for homelab users and for progressively introducing Kubernetes into an existing bare-metal setup.
[!WARNING] Do Not Use In Production: Current version is preliminary and untested.
This is a preliminary version. We are working on conformance with the Gateway API.
This project is not affiliated with nginx or Kubernetes.
[!WARNING] One Controller per Node: The controller claims
/etc/nginx/conf.d/k8s-gw/exclusively; do not run two instances against the same nginx. Using DaemonSet can guarantee this.
# Install Gateway API CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yaml
Full Helm values are documented in charts/host-nginx-gateway/README.md.
nginx.confCAP_NET_BIND_SERVICE (the controller pod itself does not need them)| Field | Support | Notes |
|---|---|---|
listeners[].port |
✅ | Authoritative; rendered as nginx listen |
listeners[].protocol |
✅ | HTTP, HTTPS (ssl http2), TLS (SNI passthrough-style routing) |
listeners[].hostname |
✅ | server_name, exact and wildcard per spec |
listeners[].allowedRoutes |
✅ | Same (default), Selector, All; kinds → supportedKinds status |
listeners[].tls.certificateRefs |
✅ | Same-namespace, or cross-namespace with ReferenceGrant |
spec.addresses |
❌ | The controller reports status.addresses itself (--publish-addresses / node IP) |
| Field | Support | Notes |
|---|---|---|
hostnames |
✅ | Intersection with listener hostname drives server_name dispatch |
rules[].matches (path/method/headers/queryParams) |
✅ | Map chains, AND within a match, OR across matches |
rules[].filters RequestRedirect / URLRewrite |
✅ | return 30x / rewrite |
rules[].filters RequestHeaderModifier / ResponseHeaderModifier |
✅ | set/add/remove |
rules[].filters RequestMirror |
✅ | Percentage via split_clients; multiple mirrors per rule |
rules[].backendRefs weights |
✅ | nginx upstream weight= |
rules[].backendRefs filters (BackendRequestHeaderModifier) |
✅ | Single-backend rules |
rules[].timeouts |
✅ | GEP-1742 subset |
Cross-namespace parentRefs |
✅ | Governed by allowedRoutes (no ReferenceGrant needed, per spec) |
Cross-namespace backendRefs / TLS Secrets |
✅ | Requires ReferenceGrant |
k3s API ──watch──► Full Reconciler ──► graph (IR) ──► text/template
│
/etc/nginx/conf.d/k8s-gw/ ◄┘
temp main config + nginx -t → atomic publish → nginx -s reload
The controller runs as a DaemonSet pod, watches Gateway resources through its in-cluster ServiceAccount, and renders the desired nginx configuration from an internal IR. Publishing validates against a temporary copy of your real nginx.conf (your config is never modified), replaces files atomically, signals the host nginx through the host mount namespace, and verifies new listen sockets actually came up — rolling back automatically on failure.
TLS with BackendTLSPolicy (upstream encryption) is out of scopeApache License, Version 2.0