Your Kubernetes app is running. Services expose it inside the cluster. But how does external traffic reach it?
This used to be solved by Kubernetes Ingress and the popular ingress-nginx controller. But in March 2026, ingress-nginx moved to maintenance-only mode. No new features. Best-effort support only.
If you are starting a new project today, use the Kubernetes Gateway API instead. It is the official, actively developed successor — built by the same SIG Network team that built Ingress.
This tutorial covers both: what Ingress was, why it is being replaced, and how Gateway API works.
What is an Ingress Controller?
A Kubernetes Service can route traffic inside the cluster. But to route external HTTP/HTTPS traffic to the right service based on the URL path or hostname, you need an additional layer.
That is what Ingress does.
External traffic
↓
Ingress / Gateway
↓
┌──────────────────────────────┐
│ /api → api-service │
│ /web → web-service │
│ api.example.com → api-service │
└──────────────────────────────┘
Without Ingress or Gateway, you would need a separate LoadBalancer Service for every service — which creates many cloud load balancers and costs money.
The Old Way: Kubernetes Ingress + ingress-nginx
The Kubernetes Ingress object was the original solution. You defined routing rules in an Ingress resource, and an ingress controller (like ingress-nginx) implemented them.
# Old Ingress approach (still works, but no longer recommended for new projects)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
This worked, but had problems:
- Annotations overload — advanced features required nginx-specific annotations (
nginx.ingress.kubernetes.io/...). Not portable across controllers. - Weak expressiveness — traffic splitting, header-based routing, and canary deployments required hacky annotations.
- Single responsibility — one team (infrastructure) managed Gateways; another team (devs) needed route control. Ingress mixed these concerns.
Why ingress-nginx Moved to Maintenance Mode
In March 2026, the ingress-nginx project announced maintenance-only mode:
- No new features
- Critical security patches only
- Existing deployments continue to work (no forced migration)
- New projects should use Gateway API
This is not a sudden change. The Kubernetes community has been building Gateway API since 2020 as the long-term replacement. Ingress2Gateway v1.0.0-rc1, released in March 2026, provides a migration tool from Ingress to Gateway API resources.
If you have an existing ingress-nginx setup, it still works. Migrate when you are ready. If you are starting fresh, use Gateway API from day one.
The New Way: Kubernetes Gateway API
Gateway API was designed to fix all of Ingress’s problems. It is part of the official Kubernetes project (SIG Network) and is now stable (v1.2+).
Core Concepts
Gateway API uses three objects:
| Object | Who manages it | What it does |
|---|---|---|
GatewayClass | Infrastructure team | Defines the type of gateway (which controller) |
Gateway | Infrastructure team | Creates the actual gateway, configures TLS, ports |
HTTPRoute | Application team | Defines routing rules — path, headers, traffic splitting |
This role-based model is one of Gateway API’s key improvements. Infrastructure teams own the Gateway. Development teams own the HTTPRoute. No shared annotation conflicts.
Installing Envoy Gateway
You need a Gateway API controller. We use Envoy Gateway for this tutorial.
# Install Envoy Gateway
kubectl apply -f https://github.com/envoyproxy/gateway/releases/latest/download/install.yaml
# Wait for it to be ready
kubectl wait --timeout=5m -n envoy-gateway-system \
deployment/envoy-gateway --for=condition=Available
Step 1: Create a GatewayClass
# gatewayclass.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy-gateway
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
kubectl apply -f gatewayclass.yaml
Step 2: Create a Gateway
# gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: myapp-gateway
namespace: default
spec:
gatewayClassName: envoy-gateway
listeners:
- name: http
protocol: HTTP
port: 80
kubectl apply -f gateway.yaml
kubectl get gateway
NAME CLASS ADDRESS PROGRAMMED AGE
myapp-gateway envoy-gateway 172.18.0.2 True 30s
Step 3: Create an HTTPRoute
Now the application team creates routing rules with an HTTPRoute:
# httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: myapp-route
spec:
parentRefs:
- name: myapp-gateway # Which Gateway to attach to
hostnames:
- "myapp.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 80
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-service
port: 80
kubectl apply -f httproute.yaml
That is it. External traffic to myapp.example.com/api goes to api-service. Everything else goes to web-service.
Advanced Features: Traffic Splitting
One of Gateway API’s killer features is built-in traffic splitting. This is impossible to do cleanly with old Ingress annotations.
Canary Deployment (Blue-Green)
Send 10% of traffic to a new version for gradual rollout:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: myapp-canary
spec:
parentRefs:
- name: myapp-gateway
rules:
- backendRefs:
- name: myapp-v1 # Stable version
port: 80
weight: 90 # 90% of traffic
- name: myapp-v2 # Canary version
port: 80
weight: 10 # 10% of traffic
No annotations. No custom nginx config. Just clean YAML.
Header-Based Routing
Route requests based on HTTP headers. Useful for A/B testing:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: myapp-abtest
spec:
parentRefs:
- name: myapp-gateway
rules:
- matches:
- headers:
- name: X-Version
value: "beta"
backendRefs:
- name: myapp-beta
port: 80
- backendRefs:
- name: myapp-stable
port: 80
Users with the X-Version: beta header get the beta version.
TLS Termination
Configure TLS on the Gateway (not per-HTTPRoute):
# First create the TLS secret
kubectl create secret tls myapp-tls \
--cert=tls.crt \
--key=tls.key
# Gateway with TLS
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: myapp-gateway-tls
spec:
gatewayClassName: envoy-gateway
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: myapp-tls # The TLS secret
Migrating from Ingress: Ingress2Gateway Tool
If you have existing Ingress resources, the Ingress2Gateway tool (v1.0.0-rc1, March 2026) converts them to Gateway API resources automatically. Note: this is still a release candidate — test before using in production.
# Install Ingress2Gateway
go install sigs.k8s.io/ingress2gateway@v1.0.0
# Convert existing Ingress resources
ingress2gateway print --providers=ingress-nginx
The tool reads your existing Ingress YAML and outputs equivalent Gateway API YAML. Review the output, test it, then swap over.
Gateway API Implementations
Gateway API is just a specification. You need a controller that implements it. Popular options:
| Controller | Type | Good for |
|---|---|---|
| Envoy Gateway | Standalone | Simple setup, recommended for beginners |
| Cilium Gateway | eBPF-based | High performance, includes NetworkPolicy |
| nginx Gateway Fabric | nginx-based | Familiar to nginx users |
| Istio | Service mesh | Advanced traffic management, mTLS |
For local development with minikube, Envoy Gateway is the easiest to get started with.
Common Mistakes
Starting a new project with ingress-nginx in 2026
ingress-nginx is in maintenance mode. Use Gateway API for all new projects. It is stable, actively developed, and supported by major cloud providers.
Expecting 1:1 Ingress annotation mapping
Gateway API has a different routing model. Nginx annotations like nginx.ingress.kubernetes.io/... do not exist in Gateway API. Many features now have native YAML support (traffic splitting, header routing) — no annotations needed.
Forgetting the controller
Gateway API resources are just YAML. Nothing happens until you install a controller that implements them (Envoy Gateway, Cilium, etc.). If HTTPRoutes are not routing traffic, check if the controller is installed and the GatewayClass is correct.
What’s Next?
Your app is running with proper routing. The next step is managing complex deployments with Helm — Kubernetes’ package manager.
Next: Kubernetes Tutorial #7: Helm Charts — The Kubernetes Package Manager