Migreren van ingress-nginx naar Gateway API

De ingress-nginx-repository is op 24 maart 2026 gearchiveerd. Geen beveiligingspatches meer, geen bugfixes, geen releases. Als je cluster nog op ingress-nginx draait voor L7-verkeer, is migratie naar Gateway API geen optie meer maar een noodzaak. Deze handleiding loopt de volledige migratie door: een implementatie kiezen, manifests converteren met ingress2gateway, beide controllers parallel draaien, cert-manager aansluiten, en DNS knippen zonder downtime.

Wat je aan het einde hebt

Een werkende Gateway API-setup (GatewayClass, Gateway, HTTPRoutes) die al het verkeer afhandelt dat ingress-nginx nu doet, met cert-manager die automatisch TLS-certificaten uitgeeft, en ingress-nginx volledig uitgefaseerd.

Vereisten

  • Een Kubernetes-cluster op versie 1.29 of hoger (Gateway API v1.0 werd GA in Kubernetes 1.29)
  • kubectl geconfigureerd met cluster-admin of vergelijkbare RBAC
  • Helm 3.x lokaal geinstalleerd
  • Een bestaande ingress-nginx-deployment die je wilt vervangen
  • cert-manager v1.16+ geinstalleerd (v1.20+ als je ListenerSet-ondersteuning nodig hebt voor multi-tenant TLS)
  • DNS-toegang om A/AAAA/CNAME-records voor je domeinen bij te werken
  • Kennis van Kubernetes Services en hoe L4/L7-verkeer in een cluster gerouteerd wordt

Waarom ingress-nginx is gestopt

SIG Network heeft de ingress-nginx-repository gearchiveerd op 24 maart 2026. Het project was sinds november 2025 in best-effort-onderhoud met slechts een of twee vrijwillige maintainers. De directe aanleiding was CVE-2025-1974 (IngressNightmare), een CVSS 9.8 unauthenticated RCE via de admission webhook die kon leiden tot volledige cluster-overname. In februari 2026 volgden nog vier HIGH-severity CVE's.

Nu de repository gearchiveerd is, worden toekomstige kwetsbaarheden simpelweg niet meer gepatcht. EOL-software in het L7-datapad levert bevindingen op bij SOC 2, PCI-DSS, ISO 27001 en HIPAA-audits. De officiele aanbeveling: migreer naar Gateway API.

Gateway API-concepten in 60 seconden

Gateway API vervangt de monolithische Ingress-resource door een rolgebaseerde hierarchie van drie resourcetypen:

Resource Scope Wie beheert het Doel
GatewayClass Cluster Infrastructuurprovider Bepaalt welke controller Gateways beheert
Gateway Namespace Clusteroperator Declareert listeners (poorten, protocollen, TLS), toegestane route-namespaces
HTTPRoute Namespace Applicatieontwikkelaar Definieert host/pad-routing, filters, backend-targets

Die scheiding zorgt ervoor dat operators de infrastructuur beheren (welke poorten open staan, welke TLS-certificaten gebruikt worden) en developers de routing (welke paden naar welke services gaan). Dezelfde HTTPRoute werkt op Envoy Gateway, NGINX Gateway Fabric, Cilium, Istio en andere conforme implementaties.

Stap 1: kies een Gateway API-implementatie

Je hebt een controller nodig die Gateway API-resources bewaakt en het data plane programmeert. Kies er een voor je begint met manifests converteren.

Implementatie Data plane Geschikt voor
Envoy Gateway Envoy Proxy Hoogste conformance (v1.4.0 full), meest actieve community, beste ingress2gateway-emitterondersteuning
NGINX Gateway Fabric NGINX OSS/Plus Bekend NGINX data plane; logische keuze als je team NGINX-internals kent
Cilium eBPF + Envoy Draai je al Cilium als CNI; sterke L4-prestaties
Istio Envoy (sidecar/ambient) Draai je al Istio; wil je gecombineerde ingress + service mesh

Voor de meeste teams die van ingress-nginx migreren zonder bestaande service mesh is Envoy Gateway de pragmatische keuze. Als je team diepgaande ervaring heeft met NGINX-configuratie en de kleinste gedragswijziging wil, is NGINX Gateway Fabric logischer.

De rest van deze handleiding gebruikt Envoy Gateway in de voorbeelden. De Gateway- en HTTPRoute-manifests zijn identiek ongeacht de implementatie; alleen de installatiestap en implementatiespecifieke policies (rate limiting, auth) verschillen.

Stap 2: installeer Gateway API CRDs en de controller

Installeer eerst de Gateway API CRDs, dan de controller.

# Installeer Gateway API v1.5.0 CRDs (standard channel)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.0/standard-install.yaml

Verwachte output:

customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created
...

Installeer Envoy Gateway (v1.7.1):

helm install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.7.1 \
  -n envoy-gateway-system \
  --create-namespace

# Wacht tot de controller klaar is
kubectl wait --timeout=5m \
  -n envoy-gateway-system \
  deployment/envoy-gateway \
  --for=condition=Available

Controleer of de GatewayClass geaccepteerd is:

kubectl get gatewayclass

Verwachte output:

NAME            CONTROLLER                                      ACCEPTED   AGE
envoy-gateway   gateway.envoyproxy.io/gatewayclass-controller   True       30s

ACCEPTED: True bevestigt dat de controller actief is en klaarstaat.

Stap 3: inventariseer je bestaande Ingress-resources

Voordat je iets gaat converteren, breng je in kaart wat ingress-nginx op dit moment afhandelt.

# Lijst alle Ingress-resources in alle namespaces
kubectl get ingress -A -o wide

# Exporteer annotations (dit is wat ingress2gateway moet vertalen)
kubectl get ingress -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}: {.metadata.annotations}{"\n"}{end}'

Bekijk de annotations. Markeer alles dat configuration-snippet, server-snippet, auth-url, limit-rps of modsecurity-* gebruikt. Daar is geen standaard Gateway API-equivalent voor; dat vereist handwerk (zie veelvoorkomende valkuilen hieronder).

Stap 4: converteer manifests met ingress2gateway

ingress2gateway v1.0 is de officiele SIG Network-migratietool. Het leest je Ingress-resources en genereert equivalente Gateway API-manifests.

# Installeer via Homebrew
brew install ingress2gateway

# Of via Go
go install github.com/kubernetes-sigs/ingress2gateway@v1.0.0

Genereer Gateway API-manifests ter review:

# Alle namespaces, uit het live cluster
ingress2gateway print --providers=ingress-nginx -A > gateway-resources.yaml

# Enkele namespace
ingress2gateway print --providers=ingress-nginx --namespace production > prod-gateway.yaml

# Vanuit lokale bestanden (geen clustertoegang nodig)
ingress2gateway print \
  --input-file my-ingress.yaml,other-ingress.yaml \
  --providers=ingress-nginx > gateway-resources.yaml

# Met Envoy Gateway-specifieke extensies
ingress2gateway print --providers=ingress-nginx --emitter envoy-gateway > eg-resources.yaml

Lees de output goed door. De tool print WARNING-regels voor elke annotation die hij niet kan vertalen. Elke warning is een handmatige conversietaak.

Wat ingress2gateway automatisch afhandelt

De tool ondersteunt 30+ ingress-nginx annotations met geverifieerde gedragsgelijkheid:

ingress-nginx annotation Gateway API-equivalent
ssl-redirect HTTPRoute RequestRedirect-filter (scheme: https)
rewrite-target HTTPRoute URLRewrite-filter (ReplacePrefixMatch)
proxy-set-headers HTTPRoute RequestHeaderModifier-filter
enable-cors + gerelateerd HTTPRoute CORS-filter (GA in v1.5)
canary + canary-weight HTTPRoute backendRefs met weight
canary-header HTTPRoute header-based matches
proxy-read-timeout HTTPRoute timeouts.backendRequest
backend-protocol: "HTTPS" BackendTLSPolicy
backend-protocol: "GRPC" GRPCRoute
ssl-passthrough TLSRoute met Passthrough-listener
spec.tls[].secretName Gateway listener tls.certificateRefs

Wat handmatige conversie vereist

  • configuration-snippet / server-snippet: directe NGINX-directiveinjectie. Daar bestaat geen Gateway API-equivalent voor. Zoek een native HTTPRoute-filter of herontwerp op applicatieniveau.
  • limit-rps / limit-rpm / limit-connections: rate limiting is niet gestandaardiseerd. Gebruik een implementatiespecifieke policy (bijv. Envoy Gateway's BackendTrafficPolicy met rateLimit).
  • auth-url: externe authenticatie. Gebruik een implementatiespecifieke SecurityPolicy met ExtAuth.
  • modsecurity-*: WAF-regels. Vereist een aparte WAF-oplossing.

Stap 5: maak de Gateway-resource aan

De Gateway declareert je listeners. Dit is de resource die cert-manager annoteert voor automatische TLS.

# gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: envoy-gateway-system
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod  # activeert automatische TLS
spec:
  gatewayClassName: envoy-gateway
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: All
  - name: https
    hostname: "*.jouwbedrijf.nl"   # pas aan naar je eigen domein
    port: 443
    protocol: HTTPS
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: wildcard-jouwbedrijf-tls  # cert-manager maakt dit aan
    allowedRoutes:
      namespaces:
        from: All

Apply en controleer de status:

kubectl apply -f gateway.yaml

kubectl describe gateway production-gateway -n envoy-gateway-system
# Zoek naar: Programmed: True, Accepted: True

Haal het nieuwe externe IP op:

kubectl get gateway production-gateway -n envoy-gateway-system \
  -o jsonpath='{.status.addresses[*].value}{"\n"}'

Dit IP is je nieuwe load balancer-endpoint. Wijs DNS hier nog niet naartoe.

Stap 6: deploy HTTPRoutes

Apply de HTTPRoutes die ingress2gateway heeft gegenereerd (of je handmatige equivalenten). Een typische HTTPRoute:

# httproute-app.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app
  namespace: production
spec:
  parentRefs:
  - name: production-gateway
    namespace: envoy-gateway-system
    sectionName: https            # bind aan de HTTPS-listener
  hostnames:
  - app.jouwbedrijf.nl
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: my-app-service
      port: 8080

Controleer na het applyen of de route geaccepteerd is:

kubectl describe httproute my-app -n production

Beide conditions moeten True zijn:

  • Accepted: de Gateway-listener heeft deze route geaccepteerd
  • ResolvedRefs: alle backend Services en Secrets bestaan en zijn bereikbaar

Als Accepted op False staat met reden NotAllowedByListeners, matcht de hostname niet met een Gateway-listener of is de namespace niet toegestaan. Als ResolvedRefs op False staat met reden RefNotPermitted, heb je een ReferenceGrant nodig in de doelnamespace.

Cross-namespace-references vereisen ReferenceGrant

Als je HTTPRoute in namespace production een Service in namespace shared-infra referenceert, wordt de referentie stilletjes genegeerd zonder een ReferenceGrant in de doelnamespace:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-routes-from-production
  namespace: shared-infra
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: production
  to:
  - group: ""
    kind: Service

Stap 7: configureer cert-manager voor Gateway API

De Gateway API-integratie van cert-manager werkt anders dan de Ingress-integratie. Bij Ingress bewaakte cert-manager Ingress-resources. Bij Gateway API bewaakt het Gateway-resources omdat TLS op listerniveau geconfigureerd wordt.

Schakel Gateway API-ondersteuning in

Als cert-manager geinstalleerd is zonder de Gateway API-featurevlag, upgrade dan:

helm upgrade cert-manager oci://quay.io/jetstack/charts/cert-manager \
  --namespace cert-manager \
  --set config.enableGatewayAPI=true \
  --set crds.enabled=true

Heb je de Gateway API CRDs na cert-manager geinstalleerd? Herstart dan de deployment zodat de nieuwe CRD-types opgepikt worden:

kubectl rollout restart deployment/cert-manager -n cert-manager

Configureer de ACME-issuer voor Gateway API

De ClusterIssuer moet de gatewayHTTPRoute-solver gebruiken in plaats van de Ingress-gebaseerde solver:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: admin@jouwbedrijf.nl
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        gatewayHTTPRoute:
          parentRefs:
          - name: production-gateway
            namespace: envoy-gateway-system
            kind: Gateway

cert-manager maakt een tijdelijke HTTPRoute aan voor de ACME HTTP-01-challenge, valideert deze via de HTTP-listener van de Gateway, en verwijdert hem weer. De Gateway moet een HTTP-listener (poort 80) hebben die routes uit de cert-manager-namespace toestaat (of from: All).

Controleer of certificaten worden uitgegeven:

kubectl get certificates -n envoy-gateway-system
# READY-kolom moet True tonen

Stap 8: test tegen het Gateway-IP

Beide controllers draaien nu. ingress-nginx handelt nog steeds productieverkeer af; de nieuwe Gateway heeft een eigen IP. Test de Gateway zonder DNS aan te raken.

GATEWAY_IP=$(kubectl get gateway production-gateway -n envoy-gateway-system \
  -o jsonpath='{.status.addresses[0].value}')

# Basisrouting
curl -v --resolve "app.jouwbedrijf.nl:443:$GATEWAY_IP" \
  https://app.jouwbedrijf.nl/

# HTTPS-redirect (moet 301 teruggeven)
curl -v -H "Host: app.jouwbedrijf.nl" http://$GATEWAY_IP/ \
  -o /dev/null -w "%{http_code}\n"

# TLS-certificaatcheck
echo | openssl s_client -servername app.jouwbedrijf.nl \
  -connect $GATEWAY_IP:443 2>/dev/null | openssl x509 -noout -subject

Vergelijk responses tussen het oude en nieuwe endpoint:

INGRESS_IP=$(kubectl get svc ingress-nginx-controller -n ingress-nginx \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

diff \
  <(curl -s --resolve "app.jouwbedrijf.nl:443:$INGRESS_IP" https://app.jouwbedrijf.nl/) \
  <(curl -s --resolve "app.jouwbedrijf.nl:443:$GATEWAY_IP" https://app.jouwbedrijf.nl/)

Als diff geen output geeft, zijn de responses identiek. Test elke route, niet alleen de hoofdroute.

Stap 9: knip DNS over

Zodra parallelle tests zijn afgerond, wijs je DNS naar het nieuwe Gateway-IP.

24 uur voor de switch, verlaag je de DNS TTL naar 60 seconden:

# Controleer huidige TTL
dig app.jouwbedrijf.nl +short +ttlid

Pas de TTL aan via het controlepaneel van je DNS-provider. Wacht een volledige originele-TTL-cyclus tot de caches verlopen zijn.

Voer de cutover uit: werk het A/AAAA-record bij naar $GATEWAY_IP. Met 60 seconden TTL resolven de meeste clients het nieuwe IP binnen enkele minuten.

Monitor tijdens propagatie: verwacht verkeer op beide endpoints gedurende de TTL-drain. Let op:

  • HTTP 4xx/5xx-foutpercentage (moet op baseline blijven)
  • TLS-handshakefouten (certificaatmismatch als cert-manager nog niet klaar is)
  • Latentiepercentilen (p50, p95, p99)

Rollback

Gaat er iets mis, wijs DNS dan terug naar het ingress-nginx-IP. Met 60 seconden TTL is de rollback binnen een minuut klaar. De oude Ingress-resources zijn nog actief en ingress-nginx draait gewoon nog.

Stap 10: decommission ingress-nginx

Laat ingress-nginx minimaal 24 tot 48 uur draaien na de DNS-cutover als rollbackverzekering. Na de validatieperiode:

# Verwijder Ingress-resources (label ze tijdens de migratie voor makkelijke opruiming)
kubectl delete ingress -A -l migrated-to-gateway=true

# Verwijder ingress-nginx
helm uninstall ingress-nginx -n ingress-nginx

# Ruim de namespace op
kubectl delete namespace ingress-nginx

Veelvoorkomende valkuilen

Stille route-afwijzing. De meest verwarrende foutmodus. Een HTTPRoute wordt succesvol geapplied (kubectl apply geeft geen fout), maar er stroomt geen verkeer. Draai altijd kubectl describe httproute <naam> en check de statusconditions. Veelvoorkomende redenen: hostname matcht geen listener (NotAllowedByListeners), ontbrekende ReferenceGrant voor cross-namespace-referenties (RefNotPermitted), of een backend Service die niet bestaat (BackendNotFound).

cert-manager negeert Gateway-annotations. De Gateway API-ondersteuning van cert-manager vereist de config.enableGatewayAPI=true-vlag. Zonder die vlag negeert cert-manager stilletjes alle Gateway-annotations. Als je Gateway API CRDs na cert-manager hebt geinstalleerd, is ook een herstart van de cert-manager-deployment nodig.

Geen configuration-snippet-vervanging. Als je ingress-nginx-setup leunt op configuration-snippet voor directe NGINX-directiveinjectie, bestaat daar geen Gateway API-equivalent voor. Elk snippet moet individueel geanalyseerd worden. Sommige kun je vervangen door native HTTPRoute-filters (headermanipulatie, redirects, rewrites). Andere vereisen implementatiespecifieke policies zoals Envoy Gateway's EnvoyPatchPolicy. Sommige vereisen aanpassingen op applicatieniveau.

Rate limiting is niet portabel. De limit-rps-, limit-rpm- en limit-connections-annotations hebben geen standaard Gateway API-equivalent. Rate limiting is altijd implementatiespecifiek. Als je later van Gateway API-implementatie wisselt, moet je je rate limiting-configuratie herschrijven.

CRD-versieconflicten. Het installeren van Gateway API v1.5.x CRDs kan Istio 1.28/1.29 laten CrashLoopBackOffen door API-veldmismatches. Draai je die Istio-versies, gebruik dan Gateway API v1.4.0 CRDs en upgrade Istio eerst.

Multi-tenant TLS-selfservicegat. Bij ingress-nginx kon elk team hun eigen Ingress-resource annoteren om TLS-certificaten van cert-manager te krijgen. Met een gedeelde Gateway kunnen alleen operators listeners en TLS-configuratie aanpassen. ListenerSet (experimenteel in cert-manager 1.20, stabiel verwacht in 1.21/1.22) herstelt per-team TLS-autonomie. Plan voor meer operatorbetrokkenheid in de tussenperiode.

DNS-propagatie is niet instant. Zelfs met 60 seconden TTL cachen sommige resolvers agressief. Zowel ingress-nginx als de Gateway moeten productieverkeer correct afhandelen gedurende het overlapvenster. Verwijder nooit Ingress-resources of stop ingress-nginx voordat DNS-propagatie voltooid is.

Controleer of de migratie compleet is

Je weet dat de migratie geslaagd is als:

  1. kubectl get ingress -A geen resources meer teruggeeft (alles verwijderd)
  2. kubectl get httproute -A alle routes toont met Accepted: True en ResolvedRefs: True
  3. kubectl get certificates -n envoy-gateway-system alle certificaten toont met READY: True
  4. Applicatiemonitoring bevestigt dat latentie en foutpercentages op baseline zitten
  5. De ingress-nginx-namespace niet meer bestaat
  6. DNS-records naar het Gateway-IP wijzen met productie-TTL's hersteld

Wanneer escaleren

Als je vastloopt na het volgen van deze handleiding, verzamel dan het volgende voordat je om hulp vraagt:

  • Output van kubectl get gateway,httproute,referencegrant -A -o yaml
  • Output van kubectl describe gateway <naam> -n <namespace>
  • Output van kubectl describe httproute <naam> -n <namespace> (de statusconditions zijn doorslaggevend)
  • cert-manager-logs: kubectl logs -n cert-manager deployment/cert-manager --tail=100
  • Gateway-controllerlogs (voor Envoy Gateway: kubectl logs -n envoy-gateway-system deployment/envoy-gateway --tail=100)
  • De geinstalleerde Gateway API CRD-versie: kubectl get crd gateways.gateway.networking.k8s.io -o jsonpath='{.metadata.labels.gateway\.networking\.k8s\.io/bundle-version}'
  • Je Kubernetes-versie: kubectl version --short

Terugkerende server- of deploymentproblemen?

Ik help teams productie betrouwbaar maken met CI/CD, Kubernetes en cloud—zodat fixes blijven en deploys geen stress meer zijn.

Bekijk DevOps consultancy

Doorzoek deze site

Begin met typen om te zoeken, of blader door de kennisbank en blog.