NGINX Gateway Fabric 2.7: Gateway API 1.6 conformance, external authentication, and fewer snippets

by

in , ,

NGINX Gateway Fabric 2.7 is a significant release, targeting Gateway API conformance, data plane improvements, and workflow enhancements. TCPRoute and UDPRoute graduate to v1 with Gateway API 1.6, TLS listeners gain Terminate mode, and a new HTTPExternalAuthFilter calls out to an authorization proxy without an NGINX snippet. Seven more NGINX settings move out of snippets and into the API, response compression becomes a field on NginxProxy, and useClusterIP routes traffic through a Service VIP at either the Gateway or an individual Service.

Production concerns got attention too: pod disruption budgets, a memory limit that lets the agent garbage collect, automatic IP family detection on the LoadBalancer Service, and a hardening pass across the controller and the agent that fixed two panics. Seventeen community members had work land in this release, and each is credited next to their change. Here’s what’s new.

Gateway API 1.6 conformance: TCPRoute and UDPRoute v1

What’s new: TCPRoute and UDPRoute move from v1alpha2 to v1, following their graduation out of experimental status in Gateway API 1.6. Multiple L4 routes can now be attached to the same listener (though only the oldest is used). TLS listeners also gain Terminate mode, alongside the existing passthrough. These changes ensure NGINX Gateway Fabric is fully conformant with the Gateway API 1.6 spec (v1 TCPRoute and UDPRoute nginx/nginx-gateway-fabric#5604, TLS Terminate nginx/nginx-gateway-fabric#5287).

In addition, the team developed two more changes to ensure Gateway API 1.6 conformance:

  • sessionPersistence.idleTimeout removed from HTTPRoute: The field was dropped from the Gateway API spec in 1.6.0 (nginx/nginx-gateway-fabric#5556).

  • Zero-weight backends omitted from stream maps: A UDP or TCP backend at 0.00% weight previously produced invalid stream configuration, which caused NGINX to fail when reloading (nginx/nginx-gateway-fabric#5601).

Why it matters: Plenty of platform teams wait for experimental APIs to stabilize before using them. Now that the TCPRoute and UDPRoute specs have become stable, your TCP and UDP routes sit at the same API maturity as your HTTP ones, and Terminate mode means a TLS listener can end the connection at the Gateway rather than passing it through untouched.

External authentication with HTTPExternalAuthFilter

What’s new: The new HTTPExternalAuthFilter is a Gateway API-conformant way to send a request to an external authorization proxy before it reaches your backend application. Under the hood, this adds support for NGINX’s http_auth_request module (nginx/nginx-gateway-fabric#5145).

Why it matters: An external authorization check in front of the application is a common requirement, and until now it required a custom NGINX snippet.

HTTP/2 to your upstreams

What’s new: NGINX Gateway Fabric can now speak HTTP/2 to upstream services, built on the HTTP/2 client capability recently added to NGINX (nginx/nginx-gateway-fabric#5308).

Why it matters: HTTP/2 to your upstreams lets a single connection carry many concurrent streams between NGINX Gateway Fabric and your backends, so you hold fewer connections open and get consistent protocol support end to end.

Least time load balancing in NGINX Open Source

What’s new: The least_time load balancing method, which routes each request to the upstream with the lowest average response time, moves to open source. Developed by community contributor yogarajalakshmi-s (nginx/nginx-gateway-fabric#5381).

Why it matters: If your backends have uneven response times, sending each request to whichever one is answering fastest beats round robin.

Tuning the data plane without reaching for snippets

What’s new: Seven settings move out of snippets and into the API, most of them contributed by our community:

  • Response compression on NginxProxy: A Compression field covering level, minLength, mimeTypes, and buffers, with gzip-specific settings for vary, httpVersion, disable, and proxied. Validation requires the gzip block when type is gzip. Developed by community contributor oyiz-michael (nginx/nginx-gateway-fabric#5064).
  • workerProcesses are now configurable between 1 and 1024 in addition to auto: Before this change, NGINX Gateway Fabric hardcoded worker_processes auto, which starts one worker per CPU core on the node and takes no notice of the container’s CPU limit. On a large node with a small limit that’s a lot of workers and a lot of memory, and it showed up as OOMKills. Developed by community contributor SidharathBansal (nginx/nginx-gateway-fabric#5519).
  • workerRlimitNofile and per-listener TLS session options: worker_rlimit_nofile applies process-wide and is an NginxProxy field, while ssl_session_cache, ssl_session_timeout, and ssl_ecdh_curve are per-server and go on Gateway Listener TLS options where each listener sets its own. NGINX Gateway Fabric builds the shared cache zone name from the Gateway and listener names so two listeners can’t collide. Also developed by community contributor SidharathBansal (nginx/nginx-gateway-fabric#5534).
  • allowedListeners in the Helm chart: Gateways created by the chart can set spec.allowedListeners to decide which namespaces may attach Listeners, which until now meant editing the rendered manifest by hand. Developed by community contributor sgavrylenko (nginx/nginx-gateway-fabric#5553).
  • automountServiceAccountToken as a chart value: On the control plane Deployment and the cert-generator Job, along with extraVolumes and extraVolumeMounts on the cert generator. Defaults are unchanged. Developed by community contributor dap0am (nginx/nginx-gateway-fabric#5242).
  • Native client_body_buffer_size: The buffer size for reading a client request body is now a ClientSettingsPolicy field (nginx/nginx-gateway-fabric#5700).
  • ZoneSize on NginxProxy: Sets the shared memory zone size for every HTTP upstream at once. It was only settable per-upstream on UpstreamSettingsPolicy before, with no global fallback. A policy still overrides the global setting for a specific Service, and if neither is set you get the default for your build (nginx/nginx-gateway-fabric#5708).

Why it matters: Every snippet in your manifests is a piece of NGINX configuration your Gateway API tooling can’t see or validate. Get a value wrong in a snippet and nothing catches it until NGINX tries to reload the configuration; get it wrong in a field and the API server rejects it the moment you apply. In addition, a snippet is raw configuration you need to maintain and ensure remains functional, while a field just records your intent and lets NGINX Gateway Fabric emit the right directive for the version underneath.

useClusterIP, at two levels

What’s new: Two ClusterIP improvements, both developed by community contributors:

  • At the Gateway or GatewayClass level: useClusterIP on NginxProxy covers every HTTP and gRPC upstream behind it, rather than Service by Service. Developed by community contributor SidharathBansal (nginx/nginx-gateway-fabric#5557).
  • For a single Service: useClusterIP on UpstreamSettingsPolicy points that Service’s HTTP and gRPC upstreams at its ClusterIP instead of every Pod IP, taking precedence over any Gateway-wide setting. Developed by community contributor oyiz-michael (nginx/nginx-gateway-fabric#5280).

Why it matters: A Service’s ClusterIP does not change as pods come and go, so the upstream address stays put and NGINX Gateway Fabric reloads less. It also means traffic reaches your backends through the Service rather than around it, so anything enforced at that layer still applies. Headless and ExternalName services fall back to resolving individual Pod IPs.

Policy status you can act on

What’s new: ClientSettingsPolicy, UpstreamSettingsPolicy, ObservabilityPolicy, ProxySettingsPolicy, RateLimitPolicy, and SnippetsPolicy now report GEP-713’s Programmed condition, separately for each Gateway or Route the policy is attached to rather than once for the policy as a whole. A policy that gets overridden by another now says so with an Overridden condition, alongside the existing Conflicted. Developed by community contributor somaz94 (nginx/nginx-gateway-fabric#5392).

Why it matters: Accepted only tells you a policy is valid; Programmed tells you it reached the data plane. This helps validate that a policy is applied when nothing seems different, and reporting it per target tells you which Gateway it reached.

Built for production

What’s new: Five changes aimed squarely at running NGINX Gateway Fabric at scale:

  • Pod disruption budgets: For the control plane and the data plane, through the Helm chart (nginx/nginx-gateway-fabric#5370).
  • A memory soft limit on the nginx-agent process: GOMEMLIMIT and GOGC are now set, giving the Go runtime a ceiling to collect against instead of letting the process grow. In longevity testing the agent had been starting around 40–60 MiB and climbing steadily; it now stays under 150 MiB (nginx/nginx-gateway-fabric#5349).
  • Configurable server TLS domain: A new --server-tls-domain flag sets the domain suffix used in the control plane’s server certificate and in the nginx-agent config, both of which hardcoded .svc (nginx/nginx-gateway-fabric#5380).
  • Automatic IP family detection on the LoadBalancer Service: ipFamily now defaults to auto, which reads the families off the kubernetes Service at startup rather than always requesting PreferDualStack. An explicit dual, ipv4, or ipv6 still takes precedence. Developed by community contributor tanayarun (nginx/nginx-gateway-fabric#5530).
  • Configurable leader election timeouts: leaseDuration, renewDeadline, and retryPeriod are now Helm values under nginxGateway.leaderElection, and CLI flags on the controller. Leave them empty and you get controller-runtime’s defaults of 15s, 10s, and 2s, exactly as before (nginx/nginx-gateway-fabric#5761).

Why it matters: Each of these features gives you more control over how you run NGINX Gateway Fabric. You can tell Kubernetes how many replicas must stay up through a node drain, cap the agent’s memory to improve garbage collection rates, choose the certificate domain instead of accepting .svc, let the LoadBalancer Service match the address families your cluster actually has, and stretch the leader election timeouts on a cluster whose API server is slow enough to cost you the lease.

Smaller enhancements

  • Turn off the X-Forwarded base headers: NGINX Gateway Fabric can be told to stop setting them, for deployments where a proxy in front already does (nginx/nginx-gateway-fabric#5288).
  • nameOverride and fullnameOverride documented: In values.yaml and the schema. Both were already wired into the chart’s helper templates, so the behavior existed — there was just no way to find it. Developed by community contributor Alja9 (nginx/nginx-gateway-fabric#5383).
  • Default upstream keepalive aligned with NGINX’s own: NGINX Gateway Fabric hardcoded 16 while NGINX’s default had moved on to 32, so the documented behavior and the real behavior diverged. Started by community contributor thebigbone (nginx/nginx-gateway-fabric#5328) and carried through by the team (nginx/nginx-gateway-fabric#5592).

Upgrade notes

  • The deprecated externalIPs Service field is no longer used: NGINX Gateway Fabric no longer sets it when provisioning the Service (nginx/nginx-gateway-fabric#5294).
  • The cert-generator RBAC gained hook-delete-policy and pre-upgrade: Its hook resources are now cleaned up after the Job succeeds and exist for upgrades as well as installs. They carried only pre-install, so Helm left them untracked and orphaned after an uninstall, which held Argo CD Applications at Degraded. Diagnosed and written by community contributor vtymchyshyn-devops (nginx/nginx-gateway-fabric#5466) and landed by the team (nginx/nginx-gateway-fabric#5663).
  • The idleTimeout field is gone from sessionPersistence on HTTPRoute: Gateway API 1.6.0 dropped it from the spec, so NGINX Gateway Fabric dropped it too. If your HTTPRoutes set spec.rules[].sessionPersistence.idleTimeout, remove it before you upgrade (nginx/nginx-gateway-fabric#5556).
  • Kubernetes 1.32 is now the minimum: Clusters below 1.32 are no longer supported (nginx/nginx-gateway-fabric#5602).

Stability and bug fixes

  • Hostname addresses survive dnsResolver configuration generation: A type: Hostname address was dropped, leaving an empty resolver ; that NGINX rejects — traffic stopped, a regression since 2.2.1. Developed by community contributor mzimry (nginx/nginx-gateway-fabric#5531).
  • Validation errors read the right way round: Four of the five regex validators had their format string and explanation swapped, so a bad rate like 100rs returned a regex. RateLimitPolicy and NginxProxy also gain bounds they lacked. Developed by community contributor hsdfat (nginx/nginx-gateway-fabric#5639).
  • Generated configuration is deterministic: Stream upstreams, L4 servers, and add-header maps are emitted in sorted order, so identical input no longer renders differently and costs a reload for nothing. Developed by community contributor ItsVigneshMurugan (nginx/nginx-gateway-fabric#5486).
  • SnippetsPolicy configuration generation is now deterministic: Policies are sorted by namespace and name, and include deduplication preserves first-seen order. Developed by community contributor theoilie (nginx/nginx-gateway-fabric#5625).
  • A data plane restart no longer triggers a second rollout: The kubectl.kubernetes.io/restartedAt annotation is preserved, so a later reconcile no longer changes the pod-template hash and rolls the workload again. Also developed by community contributor theoilie (nginx/nginx-gateway-fabric#5628).
  • Conflicting route policies are detected across overlapping hostnames: Routes were keyed by their whole hostname slice, so two could overlap with both policies reporting Accepted and only one taking effect. Developed by community contributor floze-the-genius (nginx/nginx-gateway-fabric#5605).
  • ExternalName Service updates reach the data plane: The Service predicate ignored updates to an ExternalName Service, so changing the name it pointed at left NGINX resolving the old one until something else forced a configuration update (nginx/nginx-gateway-fabric#5762).
  • InferencePool honors its fail-close setting: The generated map skipped the failure-mode-aware branch and fell back to the plain upstream, so a pool configured to fail closed kept serving when the endpoint picker returned nothing. Requests with an empty body now reach the endpoint picker too (nginx/nginx-gateway-fabric#5494).
  • The metrics port is declared on the Service: Whenever nginxGateway.metrics.enable is on. It was set on the pods but never the Service, so Prometheus scrapes failed silently. Developed by community contributor Alja9 (nginx/nginx-gateway-fabric#5326).
  • Two panics and a GRPCRoute path bug fixed: The panics hit on very long gateway or listener names, and on routes whose target namespace was deleted mid-reconcile. GRPCRoutes with multiple match rules also produced incorrect path configuration (nginx/nginx-gateway-fabric#5599).
  • Hardening in the controller’s lifecycle and state handling: Entrypoint PID and trap handling, nginx-agent startup checks, file conversion rejecting invalid metadata and permissions, empty versus missing agent files, secret revalidation, and mutable internal state no longer exposed through published getters (nginx/nginx-gateway-fabric#5623).
  • Hardening in agent authentication and config apply: Strict file-stream validation, a gRPC panic-recovery interceptor, steadier TLS watch and update behavior, less race-prone deployment store access, and stronger token-bound pod identity validation (nginx/nginx-gateway-fabric#5626).
  • Duplicate status conditions no longer accumulate on retry: Status setters mutated their captured struct before the equality check, so retries accumulated duplicate entries from other controllers (nginx/nginx-gateway-fabric#5678). The GatewayInvalidParametersRef condition now reports Accepted=False per the spec (nginx/nginx-gateway-fabric#5601).
  • Provisioning fixes for object ownership, static addresses, and leader handover: Annotations and owners on provisioned and managed objects (nginx/nginx-gateway-fabric#5631), a GatewayStaticAddress provisioning bug (nginx/nginx-gateway-fabric#5576), and live gateways queued before leader promotion no longer deprovisioned (nginx/nginx-gateway-fabric#5388).
  • Data plane fixes for Passthrough servers and resource validation: No proxy_protocol on Passthrough servers (nginx/nginx-gateway-fabric#5707), and extended validation across several resources (nginx/nginx-gateway-fabric#5469).
  • [Enterprise only] Stale WAF state no longer survives a poller shutdown: Stopping a poller cleared its entries, but a poll already in flight would finish and write them straight back. Developed by community contributor locker95 (nginx/nginx-gateway-fabric#5687).

Under the hood


Community recognition

From this release on, we’re crediting the community members who contribute to NGINX Gateway Fabric, and naming their work where it lands. Whether you contributed code, helped shape a feature, or reported major bugs, thank you!

Community contributions

Seventeen community members had work land in NGINX Gateway Fabric 2.7: nine bug fixes, three features, a refactor of 11 of the controller’s most complex functions, and clearer failure output in the system tests. Every one of them is credited above, next to the change they made. Thanks to Alja9, dap0am, floze-the-genius, hsdfat, ItsVigneshMurugan, locker95, mzimry, oyiz-michael, sankalpsthakur, sgavrylenko, SidharathBansal, somaz94, tanayarun, thebigbone, theoilie, vtymchyshyn-devops, and yogarajalakshmi-s. Your contributions in this release have helped shape NGINX Gateway Fabric 2.7!

Want your name in NGINX Gateway Fabric 2.8?

Pick up an open issue, contribute a feature you developed, or fix a bug you have run into. The contributing guide covers how to get started, from opening a discussion to sending your first pull request.


Head over to the GitHub release page for the full changelog, and the NGINX Gateway Fabric docs when you’re ready to upgrade to 2.7. If you’re moving over from NGINX Ingress Controller, the Kubernetes NGINX landing page covers the migration.

We’re proud of what the team and the community built here, and we hope it makes your clusters easier to run.