Ingress + Gateway API cheatsheet.

Why Ingress

LoadBalancer per service = expensive + scattered. Ingress = one LB + L7 routing.

ingress-nginx

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx -n ingress-nginx --create-namespace

Other options: Traefik, HAProxy, Istio, Contour.

Basic Ingress

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

Multiple paths

spec:
  rules:
    - host: example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend: { service: { name: api, port: { number: 80 } } }
          - path: /
            pathType: Prefix
            backend: { service: { name: web, port: { number: 80 } } }

TLS

spec:
  tls:
    - hosts: [example.com]
      secretName: web-tls
  rules:
    - host: example.com
      http: ...

Create TLS secret:

kubectl create secret tls web-tls --cert=cert.pem --key=key.pem

cert-manager (auto TLS)

helm install cert-manager jetstack/cert-manager -n cert-manager --create-namespace --set crds.enabled=true
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata: { name: letsencrypt-prod }
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: [email protected]
    privateKeySecretRef: { name: letsencrypt-prod }
    solvers:
      - http01:
          ingress: { class: nginx }

Annotate Ingress:

metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod

Cert-manager auto-creates and renews TLS secret.

Common annotations (ingress-nginx)

annotations:
  nginx.ingress.kubernetes.io/rewrite-target: /
  nginx.ingress.kubernetes.io/ssl-redirect: "true"
  nginx.ingress.kubernetes.io/proxy-body-size: "10m"
  nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
  nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
  nginx.ingress.kubernetes.io/limit-rps: "100"
  nginx.ingress.kubernetes.io/configuration-snippet: |
    more_set_headers "X-Frame-Options: DENY";

Default backend

For unmatched paths:

spec:
  defaultBackend:
    service:
      name: 404-page
      port: { number: 80 }

Wildcard host

rules:
  - host: "*.example.com"
    http: ...

Path types

  • Exact: exact match.
  • Prefix: path prefix match.
  • ImplementationSpecific: vendor-specific.

IngressClass

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata: { name: nginx }
spec:
  controller: k8s.io/ingress-nginx

Multiple ingress controllers can coexist via different IngressClasses.

Gateway API (modern replacement)

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata: { name: my-gateway }
spec:
  gatewayClassName: nginx
  listeners:
    - name: http
      protocol: HTTP
      port: 80
    - name: https
      protocol: HTTPS
      port: 443
      tls:
        certificateRefs:
          - { name: web-tls }
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: { name: web }
spec:
  parentRefs: [{ name: my-gateway }]
  hostnames: [example.com]
  rules:
    - matches:
        - path: { type: PathPrefix, value: /api }
      backendRefs:
        - { name: api, port: 80 }
    - matches:
        - path: { type: PathPrefix, value: / }
      backendRefs:
        - { name: web, port: 80 }

Gateway API is the modern successor. Supports cross-namespace, traffic splitting, header routing.

Traffic splitting (canary)

backendRefs:
  - name: web-stable
    port: 80
    weight: 90
  - name: web-canary
    port: 80
    weight: 10

10% to canary version.

Header / query routing

matches:
  - headers:
      - name: X-User-Tier
        value: premium
backendRefs: [{ name: web-premium, port: 80 }]

Rate limiting

ingress-nginx:

annotations:
  nginx.ingress.kubernetes.io/limit-rps: "10"
  nginx.ingress.kubernetes.io/limit-burst-multiplier: "2"

Or via service mesh / dedicated rate-limiting middleware.

CORS

annotations:
  nginx.ingress.kubernetes.io/enable-cors: "true"
  nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.example.com"
  nginx.ingress.kubernetes.io/cors-allow-credentials: "true"

external-dns

Auto-creates DNS records pointing to Ingress LB:

helm install external-dns oci://registry-1.docker.io/bitnamicharts/external-dns \
  --set provider=cloudflare \
  --set cloudflare.apiToken=...
metadata:
  annotations:
    external-dns.alpha.kubernetes.io/hostname: example.com

Common mistakes

  • Wrong ingressClassName → 404.
  • TLS secret in wrong namespace.
  • path: / without pathType (older API).
  • Missing rewrite annotations → backend gets wrong path.
  • Multiple ingress controllers without class differentiation.

Read this next

If you want my Ingress + cert-manager templates, they’re at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .