Back to blog
Cloud
AdvancedForPlatform EngineersDevOps EngineersBackend Engineers
7 min

GKE Gateway API vs Ingress 2026: When to Stop Using nginx

GKE Gateway API vs Ingress in 2026: the role-oriented model, the four GatewayClasses, the proxy-only subnet regional clusters need, and when to drop self-hosted nginx.

gke-gateway-apikubernetes-ingressgkegatewayclasskubernetesgoogle-cloudload-balancing
Cover image: GKE Gateway API vs Ingress 2026: When to Stop Using nginx
Contents

For years, exposing an application on Kubernetes meant one of two things: a cloud Ingress object stuffed with annotations, or a self-hosted nginx ingress controller running as pods in your cluster. Both work. Both are now the old way.

In 2026 the Kubernetes community’s answer is the Gateway API, and on GKE it is generally available, role-oriented, and backed directly by Google’s Cloud Load Balancing. This is a practical guide to what it is, the four GatewayClasses you actually get, the one thing regional clusters must configure, and the honest case for retiring nginx.

Why Ingress Ran Out of Road

Ingress was designed as a single resource that does everything: it defines the frontend (ports, TLS), the routing rules (host and path to Service), and — because the base spec is thin — a pile of controller-specific annotations for anything advanced. Rewrites, redirects, timeouts, CORS, body size: all annotations.

That creates two problems. First, one object owned by one team mixes platform concerns (TLS, IP addresses) with application concerns (routes). Second, every annotation is vendor-specific, so your config is not portable and not expressive in any standard way.

Ingress packs frontend, routing, TLS, and vendor annotations into one object; Gateway API splits the same job into GatewayClass, Gateway, and HTTPRoute, each owned by a different role.

The Gateway API Model: Three Roles, Not One Object

Gateway API is an evolution of Ingress that breaks the single object into three resources aligned to who actually owns each concern:

  • GatewayClass — a cluster-scoped template for a load balancer. On GKE, Google provides these; you don’t write them. Each GatewayClass maps to a specific Cloud Load Balancer type.
  • Gateway — created by the platform / cluster operator. It declares where and how to listen: ports, protocols, TLS, and IP addresses. It picks a GatewayClass.
  • HTTPRoute — created by application teams. It declares routing: which hostnames and paths map to which Services, plus header matching, traffic splitting, redirects, and rewrites — all native fields, no annotations.

Gateway and Route binding is bidirectional: a Gateway declares which namespaces may attach, and a Route declares which Gateway it attaches to. That is what makes a single load balancer safely shareable across teams and namespaces — the multi-tenancy Ingress never had.

The GKE Gateway controller itself is Google-hosted and runs out of band: it watches the Kubernetes API and reconciles Cloud Load Balancing resources. It is not a data plane, it does not sit in the request path, and it does not run on your control plane or in your project. That is a meaningful robustness and scale difference from an in-cluster nginx controller.

The Four GatewayClasses You Get by Default

Enable Gateway API and GKE installs the single-cluster GatewayClasses automatically. Four matter for day-to-day work:

The four single-cluster GatewayClasses: global-external-managed (recommended), regional-external-managed and rilb (both need a proxy-only subnet), and the legacy gxlb to avoid.

The Four GatewayClasses You Get by Default
GatewayClass Load balancer Use it for
gke-l7-global-external-managed Global external Application LB Recommended default for internet-facing apps; Anycast IP, Premium Tier, lowest global latency
gke-l7-regional-external-managed Regional external Application LB External traffic pinned to a single region
gke-l7-rilb Internal Application LB Private, VPC-only traffic
gke-l7-gxlb Classic Application LB Legacy — avoid; no HTTP-to-HTTPS redirect, no custom headers

Google explicitly recommends gke-l7-global-external-managed over the classic gke-l7-gxlb to get the advanced security and traffic-management features. There are also multi-cluster (-mc) variants for fleets and a service-mesh class (gke-td), but the four above cover the vast majority of single-cluster deployments.

Regional Clusters: The Proxy-Only Subnet Is Mandatory

This is the step that trips people up, so it gets its own section.

Both regional GatewayClasses — gke-l7-regional-external-managed and gke-l7-rilb — require a proxy-only subnet in every region where you deploy them. The global external class does not. The proxy-only subnet is where Cloud Load Balancing places its managed Envoy proxies; without it the Gateway controller fails with an error that a reserved managed proxy subnetwork with purpose REGIONAL_MANAGED_PROXY is required.

Create it once per region, before the Gateway:

Terminal window
gcloud compute networks subnets create proxy-only-subnet \
--purpose=REGIONAL_MANAGED_PROXY \
--role=ACTIVE \
--region=europe-west3 \
--network=my-vpc \
--range=10.129.0.0/23

A /23 is the recommended range so there are enough IPs for the proxies to scale; /26 (64 addresses) is the hard minimum. One ACTIVE proxy-only subnet serves all regional Gateways in that region and VPC — you don’t create one per Gateway.

If a proxy-only subnet already exists in the region with the older INTERNAL_HTTPS_LOAD_BALANCER purpose, migrate it to REGIONAL_MANAGED_PROXY; GKE Gateway only accepts the latter.

A Minimal Gateway + HTTPRoute

The platform team deploys the Gateway once:

kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: external-http
spec:
gatewayClassName: gke-l7-global-external-managed
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: store-example-com

An application team then attaches routes independently, in their own namespace:

kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: store
spec:
parentRefs:
- kind: Gateway
name: external-http
hostnames:
- "store.example.com"
rules:
- matches:
- path:
value: /de
backendRefs:
- name: store-german
port: 8080
- backendRefs:
- name: store-v1
port: 8080

Header matching, weighted traffic splitting, path redirects, and URL rewrites are first-class fields on the HTTPRoute — the things that were nginx annotations are now portable spec. For anything load-balancer-specific (health checks, backend policies) you attach a Policy resource (HealthCheckPolicy, GCPBackendPolicy) instead of a BackendConfig.

When to Stop Using nginx

Here is the honest version.

A self-hosted nginx ingress controller runs as Deployment pods inside your cluster. You own their autoscaling, their resource footprint, their upgrades, and every nginx and ingress-controller CVE that lands. In return you get a very capable in-cluster L7 proxy.

Decision flow: internal traffic goes to gke-l7-rilb; external single-region to regional-external-managed; external global to global-external-managed. In every branch the data plane is Google-managed, with no nginx pods to run.

GKE Gateway removes that operational surface entirely. The data plane is Google-managed Cloud Load Balancing. There are no ingress pods to run, scale, or patch; the controller is out of band; and the load balancer is Google’s global infrastructure, not three replicas you sized by guesswork.

Migrate to Gateway when:

  • You want the L7 data plane to be someone else’s operational problem — the common case.
  • You need global load balancing, Anycast IPs, or Cloud Armor / Cloud CDN integration that plugs straight into Cloud Load Balancing.
  • You want role separation: platform owns Gateways, app teams own Routes, across namespaces.
  • You are starting fresh — every existing Ingress maps cleanly to one Gateway plus one HTTPRoute.

Keep nginx only when:

  • You depend on in-cluster L7 features Cloud Load Balancing doesn’t offer (exotic Lua/rewrite logic, specific auth modules, protocols beyond HTTP/S).
  • You need TCP/UDP or TLS-passthrough routing — GKE Gateway currently supports HTTPRoute only; TCPRoute, UDPRoute, and TLSRoute are not supported.
  • You want one self-managed L7 data plane you fully control across clouds, not tied to each provider’s managed load balancer. Note the honest caveat: Gateway API is itself a portable standard, so a portable Gateway implementation (Envoy Gateway, NGINX Gateway Fabric) usually serves this better than sticking with classic nginx-ingress.

The Field Rule

Gateway API is where Kubernetes ingress is going, and on GKE it is GA, free with the platform, and backed by Google’s Cloud Load Balancing. Split the old Ingress into GatewayClass + Gateway + HTTPRoute, default to gke-l7-global-external-managed for internet-facing apps, drop to gke-l7-rilb for private traffic, and remember the one hard prerequisite: regional classes need a proxy-only subnet per region. For most teams, that combination is the moment self-hosted nginx stops being worth running.

For the cluster mode this all runs on, see GKE Autopilot vs Standard in 2026. To lock down what those workloads can reach once traffic is inside, see Kill Service Account Keys: Workload Identity Federation on GKE.

Frequently asked questions

What is the difference between GKE Ingress and Gateway API?

Ingress packs frontend configuration, routing, TLS, and vendor-specific behaviour into a single object, often controlled through annotations. Gateway API splits that into role-oriented resources: a GatewayClass (a Google-provided template for a load balancer), a Gateway (owned by the platform team, defining ports, addresses, and TLS), and an HTTPRoute (owned by application teams, defining host and path routing). Gateway is portable across implementations, expresses header matching and traffic weighting natively, and is the direction Google recommends for new workloads.

Should I stop using nginx ingress on GKE?

For most GKE workloads, yes. Self-hosted nginx ingress means you run, scale, monitor, and patch the ingress controller pods yourself, and you carry the CVE burden. GKE Gateway offloads the L7 data plane to Google-managed Cloud Load Balancing — the Gateway controller runs out of band and provisions Cloud Load Balancing for you, with no data plane pods in your cluster. You keep nginx only where you genuinely need in-cluster L7 features that Cloud Load Balancing does not offer.

Which GatewayClass should I use on GKE?

For internet-facing apps with a global audience, use gke-l7-global-external-managed — it provisions a global external Application Load Balancer with an Anycast IP and is Google's recommended default. For single-region external traffic, use gke-l7-regional-external-managed. For private, VPC-only traffic use gke-l7-rilb. Avoid the legacy gke-l7-gxlb (classic ALB), which lacks features like HTTP-to-HTTPS redirects and custom headers.

Do I need a proxy-only subnet for GKE Gateway?

Only for the regional GatewayClasses. Both gke-l7-regional-external-managed and gke-l7-rilb require a proxy-only subnet with purpose REGIONAL_MANAGED_PROXY in each region where you deploy them — a /23 range is recommended, /26 is the minimum. The global external class does not need one. Create the subnet before the Gateway, or the controller returns an error that a reserved managed proxy subnetwork is required.

Is GKE Gateway API generally available and does it cost extra?

Yes. The single-cluster GKE Gateway controller is generally available and is included at no additional charge as part of GKE Standard and Autopilot pricing. You pay only for the underlying Cloud Load Balancing resources it provisions. Gateway API is enabled by default on Autopilot clusters and can be enabled on Standard with a single gcloud flag. Only HTTPRoute is supported; TCPRoute, UDPRoute, and TLSRoute are not.

From the community

Discussion on the Fediverse

Replies from Mastodon and Bluesky — straight from the open web, no tracking.

Loading replies …

ENDE