All Articles

How to Set Up Kubernetes Gateway API to Replace Ingress (2026 Guide)

The Kubernetes Ingress API is being replaced by the Gateway API. Here's a complete step-by-step guide to setting it up with Nginx Gateway Fabric and migrating from Ingress.

DevOpsBoysMar 12, 20266 min read
Share:Tweet

If you've been using Kubernetes Ingress to route traffic to your services, there's something you need to know: the Kubernetes community has moved on. The Gateway API is the official successor to Ingress, and it's been Generally Available since Kubernetes 1.24.

The Ingress API isn't going away tomorrow, but the Gateway API is where all new features are being built. It solves the biggest pain points with Ingress: lack of expressiveness, no multi-tenancy, and too many custom annotations.

In this guide, you'll learn what the Gateway API is, why it's better, and how to set it up from scratch on a real cluster.

Why Replace Ingress?

Before diving into the how, let's understand the problem Ingress has.

The standard Ingress resource looks like this:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80

Notice the annotations? Every advanced feature — canary routing, redirects, rate limiting, header manipulation — is implemented through custom annotations. These are not standardized. The nginx.ingress.kubernetes.io/ prefix tells you these only work with the NGINX Ingress Controller. Switch to Traefik or HAProxy, and your annotations change completely.

Problems with Ingress:

  1. Annotations chaos: no standard way to express advanced routing
  2. No role separation: cluster admins and app developers edit the same resource
  3. Limited expressiveness: can't do traffic splitting natively, no gRPC routing support
  4. Coupling: tied to specific controller implementations

What is the Gateway API?

The Gateway API is a collection of Kubernetes resources designed to replace Ingress. Instead of one Ingress resource, it splits responsibilities across multiple resource types:

GatewayClass  →  defines the controller (who handles traffic)
     ↓
Gateway       →  defines the entry point (load balancer, ports, TLS)
     ↓
HTTPRoute     →  defines routing rules (paths, headers, traffic splitting)

This separation enables role-based ownership:

  • Cluster admins manage GatewayClass and Gateway — the infrastructure layer
  • App developers manage HTTPRoute — their own routing rules

No more one big Ingress file that everyone on the team edits. Each team owns their routes.

Prerequisites

For this guide you need:

  • A Kubernetes cluster (1.24+). If you don't have one, DigitalOcean Kubernetes is the fastest way to get one running — $12/month for a single node cluster.
  • kubectl configured and connected to your cluster
  • Basic familiarity with Kubernetes services and namespaces

Step 1 — Install the Gateway API CRDs

The Gateway API custom resource definitions need to be installed first. These define the new resource types (Gateway, HTTPRoute, etc.):

bash
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml

Verify the CRDs are installed:

bash
kubectl get crds | grep gateway

You should see:

gatewayclasses.gateway.networking.k8s.io
gateways.gateway.networking.k8s.io
httproutes.gateway.networking.k8s.io
referencegrants.gateway.networking.k8s.io

Step 2 — Install a Gateway Controller

The Gateway API is just a specification. You need a controller to implement it. Options include:

  • NGINX Gateway Fabric — official NGINX implementation
  • Cilium — eBPF-native, recommended if you're using Cilium as CNI
  • Istio — if you're already using Istio
  • Traefik — popular alternative
  • Envoy Gateway — Envoy-based official implementation

We'll use NGINX Gateway Fabric as it's the most widely used:

bash
# Install NGINX Gateway Fabric
kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.4.0/deploy/default/deploy.yaml

Verify it's running:

bash
kubectl get pods -n nginx-gateway

Expected output:

NAME                             READY   STATUS    RESTARTS   AGE
nginx-gateway-7d8f9c46b9-xk2m9   2/2     Running   0          30s

Step 3 — Create a GatewayClass

GatewayClass tells Kubernetes which controller handles this class of gateways. NGINX Gateway Fabric creates one automatically, but here's what it looks like:

bash
kubectl get gatewayclass
NAME    CONTROLLER                          ACCEPTED   AGE
nginx   gateway.nginx.org/nginx-gateway     True       2m

You can also create a custom one:

yaml
# gatewayclass.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: nginx
spec:
  controllerName: gateway.nginx.org/nginx-gateway

Step 4 — Create a Gateway

The Gateway resource is the actual load balancer entry point. Cluster admins create this:

yaml
# gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: main-gateway
  namespace: nginx-gateway
spec:
  gatewayClassName: nginx
  listeners:
  - name: http
    protocol: HTTP
    port: 80
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: tls-secret
        namespace: nginx-gateway

Apply it:

bash
kubectl apply -f gateway.yaml

Check it's ready:

bash
kubectl get gateway -n nginx-gateway
NAME           CLASS   ADDRESS          PROGRAMMED   AGE
main-gateway   nginx   203.0.113.42     True         1m

The ADDRESS is your external load balancer IP. Point your DNS records here.

Step 5 — Deploy a Sample Application

Let's deploy a simple application to route traffic to:

yaml
# app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web
        image: nginx:alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-app-svc
  namespace: default
spec:
  selector:
    app: web-app
  ports:
  - port: 80
    targetPort: 80
bash
kubectl apply -f app.yaml

Step 6 — Create an HTTPRoute

This is where app developers work. The HTTPRoute defines routing rules:

yaml
# httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web-app-route
  namespace: default
spec:
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  hostnames:
  - "app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: web-app-svc
      port: 80

Apply it:

bash
kubectl apply -f httproute.yaml

Test it:

bash
curl -H "Host: app.example.com" http://203.0.113.42/

You should see the NGINX welcome page.

Step 7 — Advanced Routing (No Annotations Needed)

This is where Gateway API really shines. Everything is native YAML — no controller-specific annotations.

Traffic splitting (canary deployments)

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-route
  namespace: default
spec:
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  hostnames:
  - "app.example.com"
  rules:
  - backendRefs:
    - name: web-app-svc        # stable version (80% traffic)
      port: 80
      weight: 80
    - name: web-app-canary-svc # canary version (20% traffic)
      port: 80
      weight: 20

Header-based routing

yaml
rules:
- matches:
  - headers:
    - name: X-Beta-User
      value: "true"
  backendRefs:
  - name: web-app-beta-svc
    port: 80
- backendRefs:
  - name: web-app-svc
    port: 80

Beta users (identified by header) go to the beta service. Everyone else goes to stable.

HTTP to HTTPS redirect

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: http-redirect
  namespace: nginx-gateway
spec:
  parentRefs:
  - name: main-gateway
    sectionName: http   # only the HTTP listener
  hostnames:
  - "app.example.com"
  rules:
  - filters:
    - type: RequestRedirect
      requestRedirect:
        scheme: https
        statusCode: 301

Migrating from Ingress to Gateway API

If you have existing Ingress resources, migration is a phased process:

Phase 1: Install Gateway API CRDs and controller alongside existing Ingress controller.

Phase 2: Create equivalent HTTPRoutes for each Ingress resource. Run both in parallel.

Phase 3: Update DNS/load balancer to point to Gateway. Test thoroughly.

Phase 4: Remove old Ingress resources and Ingress controller.

Here's a mapping of common Ingress annotations to Gateway API equivalents:

Ingress AnnotationGateway API Equivalent
nginx.ingress.kubernetes.io/ssl-redirectHTTPRoute RequestRedirect filter
nginx.ingress.kubernetes.io/canary-weightHTTPRoute weight on backendRefs
nginx.ingress.kubernetes.io/rewrite-targetHTTPRoute URLRewrite filter
nginx.ingress.kubernetes.io/rate-limitHTTPRoute with RequestCount policy

Verify Everything is Working

bash
# Check Gateway status
kubectl describe gateway main-gateway -n nginx-gateway
 
# Check HTTPRoute status
kubectl describe httproute web-app-route -n default
 
# Check route is attached to gateway
kubectl get httproute -A

In the HTTPRoute description, look for:

Status:
  Parents:
  - Conditions:
    - Message: Route is accepted
      Reason:  Accepted
      Status:  True
      Type:    Accepted

Accepted: True means your route is working.

Summary

The Kubernetes Gateway API gives you:

  • Role separation: admins control Gateways, developers control HTTPRoutes
  • Native traffic splitting: no annotations, works across controllers
  • Expressive routing: headers, methods, weights — all in standard YAML
  • Future-proof: all Kubernetes networking innovation is here, not in Ingress

The migration path from Ingress is straightforward and you can run both in parallel during the transition.


Want to go deeper on Kubernetes networking — from CNI plugins and NetworkPolicy to the Gateway API and service meshes? KodeKloud's Kubernetes Networking course covers all of this with hands-on labs. It's the fastest way to build real fluency with how traffic actually moves in a Kubernetes cluster.

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments