Ingress API

Ingress is Kubernetes’ original HTTP and HTTPS entry point. It is still supported and still useful, but Kubernetes now recommends Gateway API for new work. The Ingress API is stable and not planned for removal, but it is frozen and no longer receives new features.

The reason is simple: Ingress solved the common case well, but implementations quickly diverged. Anything beyond basic host and path routing often depended on controller-specific annotations or custom resources, so the same manifest could behave differently on NGINX, Traefik, cloud load balancers, or other controllers. Gateway API keeps the same purpose, but splits the model into role-oriented resources such as GatewayClass, Gateway, and HTTPRoute. That gives controllers a shared vocabulary and makes common routing configuration more portable, while still leaving room for implementation-specific extensions.

Ingress is still a good fit when you want the classic, minimal model and the controller you run is already the one you intend to target. Gateway API is the better default when you want a common API across implementations, cleaner separation between platform owner and application owner, and standard support for features such as header matching and traffic weighting. It does not remove implementation-specific behavior entirely; for example, a controller may still use its own CRDs for middleware, policy, or advanced proxy features.

A small comparison

An old-style Ingress keeps the routing rule in one resource, and controller-specific behavior is often expressed through annotations:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sampleapp
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: hello.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: sampleapp
            port:
              number: 8080

That works, but this particular rewrite annotation is understood by NGINX Ingress, not by every controller. Custom resources make the controller dependency even more explicit. For example, Traefik has an IngressRoute kind in its own API group, identified by apiVersion: traefik.io/v1alpha1:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: sampleapp
spec:
  entryPoints:
  - web
  routes:
  - match: Host(`hello.example.com`)
    kind: Rule
    services:
    - name: sampleapp
      port: 8080

The important detail is the full apiVersion plus kind, not just the kind name. Other projects may define resources with similar names, or even the same kind name in a different API group, and those resources are not interchangeable. That is powerful, but it also means manifests are controller-specific by design.

Gateway API keeps the same routing intent, but the routing resources belong to the shared gateway.networking.k8s.io API group. The GatewayClass selects the controller implementation, while the route itself can stay closer to a controller-neutral shape for common HTTP routing:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: sampleapp
spec:
  gatewayClassName: traefik
  listeners:
  - name: web
    protocol: HTTP
    port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: sampleapp
spec:
  parentRefs:
  - name: sampleapp
  hostnames:
  - hello.example.com
  rules:
  - backendRefs:
    - name: sampleapp
      port: 8080

That last example is close to the shape used in the Gateway API exercise chapter.

Info

Gateway API is the newer default in this training because it improves portability for common routing use cases and has a clearer extension model than classic Ingress. Controller-specific features still exist, but they are easier to spot when they live in their own API groups or policy resources instead of being hidden in annotations.