How Do Ingress Annotations Work?

intermediate|ingressdevopssrebackend developerCKACKAD
TL;DR

Ingress annotations are controller-specific key-value pairs in the Ingress metadata that configure advanced features beyond the core Ingress spec — such as rate limiting, CORS, authentication, URL rewriting, and proxy timeouts. They are not standardized and vary between controllers.

Detailed Answer

The Kubernetes Ingress spec is intentionally minimal — it only defines hosts, paths, backends, and TLS. For features like rate limiting, authentication, CORS, and custom headers, you use annotations specific to your Ingress controller.

How Annotations Work

Annotations are key-value pairs in the Ingress metadata. The controller watches for Ingress objects and reads these annotations to configure its proxy:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  annotations:
    nginx.ingress.kubernetes.io/rate-limit: "100"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    nginx.ingress.kubernetes.io/cors-allow-origin: "https://myapp.com"
spec:
  ingressClassName: nginx
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80

Common Nginx Ingress Annotations

TLS and HTTPS

annotations:
  # Redirect HTTP to HTTPS
  nginx.ingress.kubernetes.io/ssl-redirect: "true"
  # Force HTTPS even behind a load balancer
  nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
  # Enable TLS passthrough
  nginx.ingress.kubernetes.io/ssl-passthrough: "true"

Rate Limiting

annotations:
  # Limit requests per second per IP
  nginx.ingress.kubernetes.io/limit-rps: "50"
  # Limit connections per IP
  nginx.ingress.kubernetes.io/limit-connections: "10"
  # Return 503 when rate limit is exceeded
  nginx.ingress.kubernetes.io/limit-rate-after: "1m"

URL Rewriting

annotations:
  # Rewrite /api/v1/users to /users
  nginx.ingress.kubernetes.io/rewrite-target: /$2
  # Used with regex path: /api(/|$)(.*)

Proxy Settings

annotations:
  # Backend connection timeout
  nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
  # Backend read timeout
  nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
  # Backend send timeout
  nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
  # Max request body size
  nginx.ingress.kubernetes.io/proxy-body-size: "50m"

CORS

annotations:
  nginx.ingress.kubernetes.io/enable-cors: "true"
  nginx.ingress.kubernetes.io/cors-allow-origin: "https://myapp.com"
  nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE"
  nginx.ingress.kubernetes.io/cors-allow-headers: "Authorization, Content-Type"
  nginx.ingress.kubernetes.io/cors-max-age: "3600"

Authentication

annotations:
  # Basic auth
  nginx.ingress.kubernetes.io/auth-type: basic
  nginx.ingress.kubernetes.io/auth-secret: basic-auth-secret
  nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"

  # External OAuth2 proxy
  nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/oauth2/auth"
  nginx.ingress.kubernetes.io/auth-signin: "https://auth.example.com/oauth2/start"

Custom Headers

annotations:
  # Add response headers
  nginx.ingress.kubernetes.io/configuration-snippet: |
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
    add_header Strict-Transport-Security "max-age=31536000";

Portability Problem

The main drawback of annotations is that they are controller-specific:

| Feature | Nginx Annotation | Traefik Annotation | |---|---|---| | Rate limit | nginx.ingress.kubernetes.io/limit-rps | traefik.ingress.kubernetes.io/rate-limit | | Redirect | nginx.ingress.kubernetes.io/ssl-redirect | traefik.ingress.kubernetes.io/redirect-regex | | Body size | nginx.ingress.kubernetes.io/proxy-body-size | traefik.ingress.kubernetes.io/buffering |

Switching controllers requires rewriting all annotations. This is a key motivation behind Gateway API, which standardizes these features in the spec.

Best Practices

  1. Document your annotations: Maintain a reference of which annotations are in use and why
  2. Validate annotations: Wrong annotations are silently ignored — use CI/CD validation
  3. Minimize annotation use: Prefer defaults and use annotations only when needed
  4. Consider Gateway API: For new deployments, evaluate whether Gateway API can meet your needs without annotations

Why Interviewers Ask This

Interviewers ask this to test your practical experience with Ingress configuration in production, where annotations are the primary mechanism for customizing routing behavior.

Common Follow-Up Questions

Why are Ingress annotations controller-specific?
The Ingress spec intentionally keeps the core API minimal. Advanced features are implemented differently by each controller, so they use their own annotation namespaces.
What happens if you use the wrong controller's annotations?
They are silently ignored. The Ingress resource is still created but the annotations have no effect, which can be a subtle source of bugs.
How does Gateway API address the annotation problem?
Gateway API replaces annotations with typed, validated fields in the API spec. Features like traffic splitting and header matching are built into the route resources.

Key Takeaways

  • Annotations extend the Ingress spec with controller-specific features.
  • They are not portable between controllers — switching controllers requires rewriting annotations.
  • Common annotation categories include TLS, rate limiting, authentication, proxying, and CORS.

Related Questions

You Might Also Like