NGINX Ingress Migration Tool

Warning: The community-maintained kubernetes/ingress-nginx project reaches end of maintenance at the end of March 2026. It will receive no further releases, bugfixes, or security patches. The F5 NGINX Ingress Controller (nginx/kubernetes-ingress) is actively maintained and is the recommended migration target.

Overview

This interactive guide helps you migrate from the community-maintained Kubernetes Ingress NGINX (Ingress-NGINX) controller (kubernetes/ingress-nginx) to the F5 NGINX Ingress Controller (nginx/kubernetes-ingress). It covers 130+ annotation mappings, ConfigMap key translations, and CRD migration examples across three tabs:

  • Getting Started — Background on why to migrate, CRD overviews, installation, and a migration checklist
  • Ingress NGINX Config Analyzer — Paste an Ingress manifest and get automatic migration suggestions with ready-to-use YAML
  • Reference Guide — Complete annotation and ConfigMap mapping tables with side-by-side examples

Why Migrate?

The two controllers take fundamentally different approaches to configuring NGINX in Kubernetes.

  • Kubernetes Ingress NGINX (Ingress-NGINX) is annotation-driven — it relies on annotations and ConfigMap keys to customize NGINX. These are opaque strings with no schema validation — a single Ingress resource may carry dozens of annotations as unstructured key-value pairs.
  • F5 NGINX Ingress Controller is CRD-native — it uses Custom Resource Definitions (VirtualServer, Policy, TransportServer) as its primary config model, providing structured, schema-validated YAML with IDE autocompletion.

While the F5 NGINX Ingress Controller still supports annotations for common settings, CRDs unlock capabilities that annotations cannot express:

  • Schema validation — Kubernetes validates CRD fields at apply time, catching misconfigurations before they reach NGINX
  • Structured configuration — Nested YAML objects replace long annotation strings, making complex routing, traffic splitting, and security policies readable and maintainable
  • Reusability — Policies (rate limiting, auth, WAF) are defined once and attached to any number of routes
  • Layer 4 support — TransportServer CRDs handle TCP/UDP workloads that annotations cannot express
  • IDE and tooling support — CRD schemas enable autocompletion, linting, and documentation in editors and CI pipelines

CRDs Summary

The F5 NGINX Ingress Controller uses Custom Resource Definitions (CRDs) to extend Kubernetes with NGINX-specific configuration. CRDs provide type-safe, validated configuration that goes beyond what standard Ingress annotations can offer.

VirtualServer CRD

The primary CRD for configuring HTTP/HTTPS load balancing. It replaces Ingress resources and provides advanced traffic management capabilities with full validation.

  • Traffic Splitting: Route percentages of traffic to different backends for canary/blue-green deployments
  • Content-Based Routing: Route based on headers, cookies, arguments, or variables
  • Path Rewriting: Modify request URIs with regex support before forwarding to backends
  • Error Pages: Custom responses for specific HTTP error codes
  • Request/Response Headers: Add, modify, or remove headers in transit
  • Health Checks: Active health monitoring of upstream servers
  • Session Persistence: Cookie-based sticky sessions (Plus)

VirtualServerRoute CRD

Enables delegation of route configuration to different namespaces. Allows teams to manage their own routing rules while maintaining centralized control over the domain.

  • Cross-Namespace Routing: Reference services in different namespaces
  • Path Delegation: Delegate path prefixes to other teams/namespaces
  • Independent Updates: Teams update routes without modifying the parent VirtualServer
  • Access Control: Limit which namespaces can define routes for a host

Policy CRD

Reusable security and traffic policies that can be attached to VirtualServers or specific routes. Policies enable consistent security controls across multiple applications.

  • Rate Limiting: Limit request rates by IP, header, or custom key
  • Access Control: IP allowlists and denylists
  • Basic Auth: Username/password authentication via htpasswd
  • JWT Validation: Validate JSON Web Tokens (Plus)
  • OIDC: Native OpenID Connect integration (Plus)
  • API Key Auth: Authenticate via API keys in headers/query (Plus)
  • mTLS: Client certificate verification (ingress) and backend TLS (egress)
  • WAF: F5 WAF for NGINX web application firewall (Plus)

TransportServer CRD

Configures TCP/UDP load balancing for non-HTTP protocols. Essential for databases, message queues, gaming servers, and other Layer 4 applications.

  • TCP Load Balancing: MySQL, PostgreSQL, Redis, custom TCP services
  • UDP Load Balancing: DNS, RADIUS, gaming protocols
  • TLS Passthrough: Pass encrypted traffic directly to backends
  • Health Checks: TCP/UDP health monitoring
  • Session Persistence: IP hash-based session affinity

GlobalConfiguration CRD

Cluster-wide configuration for the F5 NGINX Ingress Controller. Defines global listeners for TCP/UDP and TLS passthrough that TransportServers can reference.

  • TCP/UDP Listeners: Define ports for non-HTTP traffic
  • TLS Passthrough Listeners: Configure SNI-based routing
  • Global Settings: Controller-wide configuration options

Installing CRDs

CRDs can be installed via Helm or manually using manifests, depending on your deployment method. See the F5 NGINX Ingress Controller installation docs for full instructions.

Helm (example)

If you install the F5 NGINX Ingress Controller via Helm, CRDs are included automatically. To ensure they are enabled:

helm install nginx-ingress oci://ghcr.io/nginx/charts/nginx-ingress --version 2.1.0 --set controller.enableCRDs=true

The controller.enableCRDs flag is true by default. If you previously disabled it, re-enable it to use CRD resources. See the Helm installation guide for details.

Manifests (example)

If you deploy using manifests, install CRDs manually before deploying the controller:

kubectl apply -f https://raw.githubusercontent.com/nginx/kubernetes-ingress/v5.3.4/deploy/crds.yaml

Replace the version number with your target controller version. See the manifest installation guide for the full step-by-step process.

Note: The crds.yaml file contains all CRDs (VirtualServer, VirtualServerRoute, Policy, TransportServer, GlobalConfiguration) in a single manifest.
Note: Replace version numbers with your target controller version.

Migration Checklist

  • Install CRDs via Helm (controller.enableCRDs=true) or manifests (deploy/crds.yaml)
  • Review annotation mappings — not all nginx.ingress.kubernetes.io/ annotations have a direct nginx.org/ equivalent (see NGINX Mappings)
  • Map rewrite-target annotation to nginx.org/rewrite-target (not nginx.org/rewrites)
  • Migrate ConfigMap keys that differ between controllers (see ConfigMap Mappings)
  • Convert HSTS ConfigMap keys to nginx.org/hsts annotations for per-Ingress control
  • Convert server-tokens ConfigMap key to nginx.org/server-tokens annotation
  • Convert access control annotations to Policy CRD (accessControl)
  • Convert authentication annotations to Policy CRD (basicAuth, jwt, oidc, apiKey)
  • Convert canary/traffic splitting to VirtualServer splits/matches
  • Convert mTLS annotations to ingressMTLS/egressMTLS policies
  • Convert CORS annotations to location-snippets or server-snippets
  • Convert custom error pages to VirtualServer errorPages
  • Convert header manipulation to VirtualServer requestHeaders/responseHeaders
  • Convert rate limiting annotations to Policy CRD (rateLimit)
  • Convert redirect annotations to VirtualServer action.redirect
  • Convert session affinity annotations to VirtualServer upstream sessionCookie
  • Convert SSL passthrough to TransportServer CRD
  • Convert TCP/UDP services to TransportServer CRD
  • Replace ssl-redirect with nginx.org/ssl-redirect (not the deprecated ingress.kubernetes.io/ssl-redirect)
  • Verify snippets (configuration-snippet, server-snippet) use F5 NGINX Ingress Controller equivalents
  • Test in staging environment
  • Update monitoring and alerting

Phased Migration Strategy

Rather than performing a "big bang" cutover, F5 recommends a phased migration that allows you to validate each workload before decommissioning the community controller. Both controllers can run side-by-side in the same cluster using separate IngressClass resources.

  1. Deploy side-by-side — Install the F5 NGINX Ingress Controller alongside the community controller using a distinct IngressClass (e.g., nginx-nic). This ensures existing traffic is unaffected.
  2. Migrate service-by-service — Update each Ingress resource's ingressClassName to point to the new controller. Convert annotations as described in the Reference Guide, validate routing and TLS behavior, and promote to production before moving on to the next service.
  3. Decommission — After all workloads have been migrated and validated, remove the community controller deployment, its IngressClass, and any unused ConfigMaps.
Tip: Use the Ingress NGINX Config Analyzer tab to process each Ingress manifest during step 2. It will flag unsupported annotations and generate the equivalent F5 NGINX Ingress Controller configuration.

Additional Resources

Warning: The community-maintained kubernetes/ingress-nginx project reaches end of maintenance at the end of March 2026. It will receive no further releases, bugfixes, or security patches. The F5 NGINX Ingress Controller (nginx/kubernetes-ingress) is actively maintained and is the recommended migration target.

Ingress NGINX Config Analyzer

Paste a Kubernetes Ingress YAML manifest that uses Kubernetes Ingress NGINX (Ingress-NGINX) annotations to get migration suggestions for the F5 NGINX Ingress Controller.

Drop YAML file here
0 lines
Ctrl+Enter to analyze
Warning: The community-maintained kubernetes/ingress-nginx project reaches end of maintenance at the end of March 2026. It will receive no further releases, bugfixes, or security patches. The F5 NGINX Ingress Controller (nginx/kubernetes-ingress) is actively maintained and is the recommended migration target.

Key Differences

AspectKubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Repositorykubernetes/ingress-nginxnginx/kubernetes-ingress
Annotation Prefixnginx.ingress.kubernetes.io/nginx.org/ or nginx.com/ or appprotect.f5.com/
CRD SupportLimitedVirtualServer, VirtualServerRoute, Policy, TransportServer, GlobalConfiguration
NGINX Plus SupportNoYes (with nginx.com/ annotations)

NGINX Mappings

These features are available in both NGINX Open Source and NGINX Plus editions. All items are annotations unless otherwise indicated with a badge (e.g. ConfigMap, Policy CRD, VirtualServer CRD). Community annotations use the nginx.ingress.kubernetes.io/ prefix; F5 NGINX Ingress Controller annotations use nginx.org/.

How to Read the Mappings
  • Annotation-to-annotation rows : Direct mappings where you change the annotation name/prefix. Click to expand and see before/after Ingress YAML examples.
  • CRD-based rows : Annotations that map to a CRD (VirtualServer, Policy, or TransportServer). Click to expand for full CRD examples with installation instructions.
  • Grouped annotations: Related annotations that work together are grouped in a single expandable row, showing how they map to a unified configuration.
  • F5 NGINX Ingress Controller-only rows: Rows marked No direct equivalent on the left are features unique to the F5 NGINX Ingress Controller with no community equivalent.
Note: Many Kubernetes Ingress NGINX (Ingress-NGINX) capabilities that do not have direct F5 NGINX Ingress Controller annotations or CRDs can still be achieved through NGINX configuration snippets (via either snippet annotations or VirtualServer/VirtualServerRoute CRDs). We recommend that users proceed with caution when using snippets, as they inject raw NGINX configuration and are intended for more advanced use cases.

Access Control

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/denylist-source-range: "192.168.1.100"
  nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8,172.16.0.0/12"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: access-control
spec:
  accessControl:
    allow:  # whitelist-source-range
      - 10.0.0.0/8
      - 172.16.0.0/12
    deny:  # denylist-source-range
      - 192.168.1.100
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/satisfy: "any"
F5 NGINX Ingress Controller
annotations:
  nginx.org/location-snippets: |
    satisfy any;
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/satisfy: "any"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |  # satisfy
        satisfy any;
      action:
        pass: backend

Authentication (Basic)

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-realm: "Protected Area"
  nginx.ingress.kubernetes.io/auth-secret: "basic-auth"
  nginx.ingress.kubernetes.io/auth-secret-type: "auth-file"
  nginx.ingress.kubernetes.io/auth-type: "basic"
F5 NGINX Ingress Controller
# auth-secret-type → Secret type
apiVersion: v1
kind: Secret
metadata:
  name: basic-auth
type: nginx.org/htpasswd  # auth-secret-type
data:
  htpasswd: dXNlcjokYXByMS4uLg==
---
annotations:
  nginx.org/basic-auth-realm: "Protected Area"  # auth-realm
  nginx.org/basic-auth-secret: "basic-auth"  # auth-secret
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-realm: "Protected Area"
  nginx.ingress.kubernetes.io/auth-secret: "basic-auth"
  nginx.ingress.kubernetes.io/auth-secret-type: "auth-file"
  nginx.ingress.kubernetes.io/auth-type: "basic"
F5 NGINX Ingress Controller
apiVersion: v1
kind: Secret
metadata:
  name: basic-auth
type: nginx.org/htpasswd
data:
  htpasswd: dXNlcjokYXByMS4uLg==
---
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: basic-auth-policy
spec:
  basicAuth:
    realm: "Protected Area"  # auth-realm
    secret: basic-auth  # auth-secret
Note: The community auth-secret-type annotation (auth-file or auth-map) has no F5 NGINX Ingress Controller equivalent. The F5 NGINX Ingress Controller only supports htpasswd format, specified via the Secret's type: nginx.org/htpasswd field. The auth-type annotation is also implicit — the F5 NGINX Ingress Controller only supports basic auth via these annotations.

Buffering

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/client-body-buffer-size: "16k"
F5 NGINX Ingress Controller
annotations:
  nginx.org/client-body-buffer-size: "16k"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/client-body-buffer-size: "16k"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      client-body-buffer-size: "16k"  # client-body-buffer-size
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-body-size: "10m"
F5 NGINX Ingress Controller
annotations:
  nginx.org/client-max-body-size: "10m"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-body-size: "10m"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      client-max-body-size: "10m"  # proxy-body-size
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-buffer-size: "8k"
F5 NGINX Ingress Controller
annotations:
  nginx.org/proxy-buffer-size: "8k"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-buffer-size: "8k"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      buffer-size: "8k"  # proxy-buffer-size
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-buffering: "on"
F5 NGINX Ingress Controller
annotations:
  nginx.org/proxy-buffering: "on"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-buffering: "on"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      buffering: true  # proxy-buffering
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
F5 NGINX Ingress Controller
annotations:
  nginx.org/proxy-buffers: "4 8k"
Note: The F5 NGINX Ingress Controller annotation uses a combined format: "number size" (e.g., "4 8k").
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      buffers:  # proxy-buffers-number
        number: 4
        size: "8k"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-busy-buffers-size: "16k"
F5 NGINX Ingress Controller
annotations:
  nginx.org/proxy-busy-buffers-size: "16k"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-busy-buffers-size: "16k"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      busy-buffers-size: "16k"  # proxy-busy-buffers-size
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-max-temp-file-size: "1024m"
F5 NGINX Ingress Controller
annotations:
  nginx.org/proxy-max-temp-file-size: "1024m"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
F5 NGINX Ingress Controller
annotations:
  nginx.org/location-snippets: |
    proxy_request_buffering off;
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |  # proxy-request-buffering
        proxy_request_buffering off;
      action:
        pass: backend
Note: The F5 NGINX Ingress Controller does not have a dedicated proxy-request-buffering annotation or ConfigMap key. Use location-snippets to set the proxy_request_buffering directive.

Canary / Traffic Splitting

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
# Canary Ingress
annotations:
  nginx.ingress.kubernetes.io/canary: "true"
  nginx.ingress.kubernetes.io/canary-by-cookie: "canary"
  nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
  nginx.ingress.kubernetes.io/canary-by-header-pattern: "^(test|staging)$"
  nginx.ingress.kubernetes.io/canary-by-header-value: "always"
  nginx.ingress.kubernetes.io/canary-weight: "20"
  nginx.ingress.kubernetes.io/canary-weight-total: "100"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: main
      service: main-svc
      port: 80
    - name: canary
      service: canary-svc
      port: 80
  routes:
    - path: /
      matches:
        - conditions:
            - header: X-Canary  # canary-by-header
              value: always  # canary-by-header-value
          action:
            pass: canary
        - conditions:
            - header: X-Canary  # canary-by-header
              value: "~^(test|staging)$"  # canary-by-header-pattern (regex)
          action:
            pass: canary
        - conditions:
            - cookie: canary  # canary-by-cookie
              value: "true"
          action:
            pass: canary
      splits:  # canary-weight / canary-weight-total
        - weight: 80
          action: { pass: main }
        - weight: 20
          action: { pass: canary }

Configuration Snippets

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/configuration-snippet: |
    add_header X-Custom "value";
F5 NGINX Ingress Controller
annotations:
  nginx.org/location-snippets: |
    add_header X-Custom "value";
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/configuration-snippet: |
    add_header X-Custom "value";
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |  # configuration-snippet
        add_header X-Custom "value";
      action:
        pass: backend
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/server-snippet: |
    location /health { return 200; }
F5 NGINX Ingress Controller
annotations:
  nginx.org/server-snippets: |
    location /health { return 200; }
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/server-snippet: |
    location /health { return 200; }
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  server-snippets: |  # server-snippet
    location /health { return 200; }
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      action:
        pass: backend
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/stream-snippet: |
    server { listen 3306; proxy_pass mysql; }
F5 NGINX Ingress Controller
# stream-snippet → GlobalConfiguration + TransportServer CRD
apiVersion: k8s.nginx.org/v1
kind: GlobalConfiguration
metadata:
  name: nginx-configuration
  namespace: nginx-ingress
spec:
  listeners:
    - name: mysql-tcp
      port: 3306  # from stream-snippet listen directive
      protocol: TCP
---
apiVersion: k8s.nginx.org/v1
kind: TransportServer
metadata:
  name: mysql-tcp
spec:
  listener:
    name: mysql-tcp  # references GlobalConfiguration listener
    protocol: TCP
  upstreams:
    - name: mysql
      service: mysql  # from stream-snippet proxy_pass
      port: 3306
  action:
    pass: mysql

CORS / Header Manipulation

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
  nginx.ingress.kubernetes.io/cors-allow-headers: "Authorization, Content-Type"
  nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
  nginx.ingress.kubernetes.io/cors-allow-origin: "https://frontend.example.com"
  nginx.ingress.kubernetes.io/cors-expose-headers: "X-Request-ID, X-Custom-Header"
  nginx.ingress.kubernetes.io/cors-max-age: "86400"
  nginx.ingress.kubernetes.io/enable-cors: "true"
F5 NGINX Ingress Controller
annotations:
  nginx.org/server-snippets: |
    set $cors_origin "";
    if ($http_origin ~* "^https://frontend\.example\.com$") {
        set $cors_origin $http_origin;
    }
    add_header Access-Control-Allow-Origin $cors_origin always;  # cors-allow-origin
    add_header Access-Control-Allow-Credentials "true" always;  # cors-allow-credentials
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;  # cors-allow-methods
    add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;  # cors-allow-headers
    add_header Access-Control-Expose-Headers "X-Request-ID, X-Custom-Header" always;  # cors-expose-headers
    add_header Access-Control-Max-Age 86400 always;  # cors-max-age
    if ($request_method = OPTIONS) {  # enable-cors
        return 204;
    }
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
  nginx.ingress.kubernetes.io/cors-allow-headers: "Authorization, Content-Type"
  nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
  nginx.ingress.kubernetes.io/cors-allow-origin: "https://frontend.example.com"
  nginx.ingress.kubernetes.io/cors-expose-headers: "X-Request-ID, X-Custom-Header"
  nginx.ingress.kubernetes.io/cors-max-age: "86400"
  nginx.ingress.kubernetes.io/enable-cors: "true"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: cors-app
spec:
  host: api.example.com
  upstreams:
    - name: api-backend
      service: api-service
      port: 80
  routes:
    - path: /
      action:
        proxy:
          upstream: api-backend
          responseHeaders:  # declarative header manipulation
            add:
              - always: true
                name: Access-Control-Allow-Origin  # cors-allow-origin
                value: "https://frontend.example.com"
              - always: true
                name: Access-Control-Allow-Credentials  # cors-allow-credentials
                value: "true"
              - always: true
                name: Access-Control-Allow-Methods  # cors-allow-methods
                value: "GET, POST, PUT, DELETE, OPTIONS"
              - always: true
                name: Access-Control-Allow-Headers  # cors-allow-headers
                value: "Authorization, Content-Type"
              - always: true
                name: Access-Control-Expose-Headers  # cors-expose-headers
                value: "X-Request-ID, X-Custom-Header"
              - always: true
                name: Access-Control-Max-Age  # cors-max-age
                value: "86400"
Recommended: The responseHeaders approach provides a fully declarative way to manage CORS and other response headers without raw NGINX snippets. Headers are set per-route via action.proxy.responseHeaders, and the always flag ensures headers are added regardless of response status code. See the VirtualServer CRD docs for additional options like hide, ignore, and pass.
Note: The F5 NGINX Ingress Controller does not have dedicated CORS annotations. Two approaches are available: (1) server-snippets / location-snippets via Ingress annotations for a quick migration, or (2) responseHeaders on VirtualServer routes for a fully declarative, snippet-free approach. The responseHeaders approach is recommended for new deployments as referenced in the official migration guide.

Error Handling

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/custom-http-errors: "404,500,502"
  nginx.ingress.kubernetes.io/default-backend: "error-svc"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
    - name: error-backend  # default-backend
      service: error-svc
      port: 80
  routes:
    - path: /
      action:
        pass: backend
      errorPages:  # custom-http-errors
        - codes: [404, 500, 502]
          pass: error-backend

Headers

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/connection-proxy-header: "keep-alive"
F5 NGINX Ingress Controller
annotations:
  nginx.org/location-snippets: |
    proxy_set_header Connection "keep-alive";
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/connection-proxy-header: "keep-alive"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  routes:
    - path: /
      action:
        proxy:
          upstream: my-backend
          requestHeaders:  # connection-proxy-header
            set:
              - name: Connection
                value: "keep-alive"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/custom-headers: "default/custom-headers-configmap"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  routes:
    - path: /
      action:
        proxy:
          upstream: my-backend
          responseHeaders:  # custom-headers
            add:
              - name: X-Frame-Options
                value: "DENY"
              - name: X-Content-Type-Options
                value: "nosniff"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/upstream-vhost: "backend.internal"
  nginx.ingress.kubernetes.io/x-forwarded-prefix: "/api"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  routes:
    - path: /api
      action:
        proxy:
          upstream: my-backend
          requestHeaders:
            set:
              - name: Host
                value: "backend.internal"  # upstream-vhost
              - name: X-Forwarded-Prefix
                value: "/api"  # x-forwarded-prefix

HSTS

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
# ConfigMap settings only
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  hsts: "true"
  hsts-max-age: "31536000"
  hsts-include-subdomains: "true"
  hsts-preload: "true"
F5 NGINX Ingress Controller
annotations:
  nginx.org/hsts: "true"
  nginx.org/hsts-behind-proxy: "true"
  nginx.org/hsts-include-subdomains: "true"
  nginx.org/hsts-max-age: "31536000"
Note: HSTS annotations are F5 NGINX Ingress Controller-only and provide per-Ingress control. The community controller handles HSTS via ConfigMap only. Note that hsts-preload from the community ConfigMap does not have a direct annotation equivalent in the F5 NGINX Ingress Controller.

Load Balancing

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/load-balance: "ewma"
F5 NGINX Ingress Controller
annotations:
  nginx.org/lb-method: "least_conn"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/load-balance: "ewma"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      lb-method: "least_conn"  # load-balance
Note: Supported methods differ. The F5 NGINX Ingress Controller supports: round_robin, least_conn, ip_hash, hash, random. The F5 NGINX Ingress Controller's default is random two least_conn (not round_robin), so load balancing behavior may change even without explicit configuration.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/service-upstream: "true"
F5 NGINX Ingress Controller
annotations:
  nginx.org/use-cluster-ip: "true"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/service-upstream: "true"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      use-cluster-ip: true  # service-upstream
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri"
F5 NGINX Ingress Controller
annotations:
  nginx.org/lb-method: "hash $request_uri consistent"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri"
  nginx.ingress.kubernetes.io/upstream-hash-by-subset: "true"
  nginx.ingress.kubernetes.io/upstream-hash-by-subset-size: "3"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      lb-method: "hash $request_uri consistent"  # upstream-hash-by
Note: The F5 NGINX Ingress Controller has no dedicated upstream-hash-by equivalent. Instead, the hash key is specified as part of the lb-method value (e.g., hash $request_uri consistent). Subset hashing (upstream-hash-by-subset) is not supported; use consistent hashing for similar distribution.

mTLS (Client Certificate Verification)

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-tls-error-page: "https://example.com/cert-error"
  nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
  nginx.ingress.kubernetes.io/auth-tls-secret: "ns/ca-secret"
  nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
  nginx.ingress.kubernetes.io/auth-tls-verify-depth: "2"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: ingress-mtls
spec:
  ingressMTLS:
    clientCertSecret: ca-secret  # auth-tls-secret
    verifyClient: "on"  # auth-tls-verify-client
    verifyDepth: 2  # auth-tls-verify-depth
---
# auth-tls-pass-certificate-to-upstream → location-snippets
# auth-tls-error-page → server-snippets
annotations:
  nginx.org/location-snippets: |
    proxy_set_header ssl-client-cert $ssl_client_escaped_cert;  # auth-tls-pass-certificate-to-upstream
  nginx.org/server-snippets: |
    error_page 495 496 = https://example.com/cert-error;  # auth-tls-error-page
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-tls-match-cn: "CN=client-[0-9]+"
F5 NGINX Ingress Controller
annotations:
  nginx.org/location-snippets: |
    if ($ssl_client_s_dn !~ "CN=client-[0-9]+") {  # auth-tls-match-cn
        return 403;
    }
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-tls-match-cn: "CN=client-[0-9]+"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |  # auth-tls-match-cn
        if ($ssl_client_s_dn !~ "CN=client-[0-9]+") {
            return 403;
        }
      action:
        pass: backend
Note: The F5 NGINX Ingress Controller's ingressMTLS Policy does not have a built-in CN matching feature. Use location-snippets to add custom CN validation logic.

mTLS (Backend/Egress)

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-ssl-ciphers: "HIGH:!aNULL:!MD5"
  nginx.ingress.kubernetes.io/proxy-ssl-name: "backend.example.com"
  nginx.ingress.kubernetes.io/proxy-ssl-protocols: "TLSv1.2 TLSv1.3"
  nginx.ingress.kubernetes.io/proxy-ssl-secret: "ns/client-cert"
  nginx.ingress.kubernetes.io/proxy-ssl-server-name: "on"
  nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
  nginx.ingress.kubernetes.io/proxy-ssl-verify-depth: "2"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: egress-mtls
spec:
  egressMTLS:
    ciphers: "HIGH:!aNULL:!MD5"  # proxy-ssl-ciphers
    protocols: "TLSv1.2 TLSv1.3"  # proxy-ssl-protocols
    serverName: true  # proxy-ssl-server-name
    sslName: "backend.example.com"  # proxy-ssl-name
    tlsSecret: client-cert  # proxy-ssl-secret
    trustedCertSecret: ca-cert
    verifyDepth: 2  # proxy-ssl-verify-depth
    verifyServer: true  # proxy-ssl-verify

Miscellaneous

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/enable-access-log: "false"
F5 NGINX Ingress Controller
# Set via ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  access-log-off: "true"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/enable-rewrite-log: "true"
F5 NGINX Ingress Controller
annotations:
  nginx.org/location-snippets: |
    rewrite_log on;
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/enable-rewrite-log: "true"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |
        rewrite_log on;
      action:
        pass: backend
Note: The F5 NGINX Ingress Controller does not have a dedicated rewrite-log ConfigMap key. Use location-snippets to enable the rewrite_log directive. Rewrite logging outputs to the error log at notice level.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/enable-opentelemetry: "true"
F5 NGINX Ingress Controller
# Set via ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  otel-exporter-endpoint: "otel-collector:4317"
  otel-trace-in-http: "true"
  otel-service-name: "nginx-ingress"
Note: The F5 NGINX Ingress Controller uses otel-exporter-endpoint, otel-trace-in-http, otel-service-name, otel-exporter-header-name, and otel-exporter-header-value ConfigMap keys for OpenTelemetry. Set otel-trace-in-http: "false" to disable global tracing and control it per-route via snippets instead.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/opentelemetry-trust-incoming-span: "true"
F5 NGINX Ingress Controller
# OTel trust-incoming-span is configured globally via ConfigMap.
# Per-Ingress control is not available.
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  otel-exporter-endpoint: "otel-collector:4317"
  otel-trace-in-http: "true"
Note: The community opentelemetry-trust-incoming-span annotation enables per-Ingress control over whether to trust incoming trace context. The F5 NGINX Ingress Controller configures OpenTelemetry globally via ConfigMap keys (otel-trace-in-http, otel-exporter-endpoint). Per-Ingress OTel toggling is not supported; tracing applies to all routes when enabled globally.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/http2-push-preload: "true"
F5 NGINX Ingress Controller
# Not supported — HTTP/2 Server Push was deprecated
# by major browsers and removed from NGINX 1.25.1.
# No migration action needed.
Note: HTTP/2 Server Push (http2_push_preload) was removed from NGINX in version 1.25.1 after major browsers deprecated the feature. This annotation can be safely removed during migration.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/mirror-host: "mirror.example.com"
  nginx.ingress.kubernetes.io/mirror-request-body: "on"
  nginx.ingress.kubernetes.io/mirror-target: "https://mirror.example.com$request_uri"
F5 NGINX Ingress Controller
annotations:
  nginx.org/location-snippets: |
    mirror /mirror;  # mirror-target
    mirror_request_body on;  # mirror-request-body
  nginx.org/server-snippets: |
    location /mirror {
      internal;
      proxy_set_header Host mirror.example.com;  # mirror-host
      proxy_pass https://mirror.example.com$request_uri;  # mirror-target
    }
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/mirror-host: "mirror.example.com"
  nginx.ingress.kubernetes.io/mirror-request-body: "on"
  nginx.ingress.kubernetes.io/mirror-target: "https://mirror.example.com$request_uri"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  server-snippets: |
    location /mirror {
      internal;
      proxy_set_header Host mirror.example.com;
      proxy_pass https://mirror.example.com$request_uri;
    }
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |
        mirror /mirror;
        mirror_request_body on;
      action:
        pass: backend
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
F5 NGINX Ingress Controller
annotations:
  nginx.org/location-snippets: |
    proxy_http_version 1.1;
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |
        proxy_http_version 1.1;
      action:
        pass: backend
Note: The F5 NGINX Ingress Controller does not have a dedicated annotation for HTTP version. Use location-snippets to set proxy_http_version directly.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/server-alias: "alias.example.com"
F5 NGINX Ingress Controller
# server-alias → separate VirtualServer per host
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: alias-app
spec:
  host: alias.example.com  # server-alias
  routes:
    - path: /
      action:
        pass: main-backend
Note: The community server-alias annotation adds an additional hostname to the same NGINX server block. The F5 NGINX Ingress Controller does not have an equivalent annotation — instead, create a separate VirtualServer resource for each hostname, all pointing to the same backend service.
Kubernetes Ingress NGINX (Ingress-NGINX)
# ConfigMap setting only
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  server-tokens: "false"
F5 NGINX Ingress Controller
annotations:
  nginx.org/server-tokens: "false"
Note: The Community controller configures server tokens via ConfigMap only. The F5 NGINX Ingress Controller provides a per-Ingress annotation.
Kubernetes Ingress NGINX (Ingress-NGINX)
# WebSocket support is automatic
# Just ensure proxy-http-version: "1.1"
# and proper Upgrade/Connection headers
F5 NGINX Ingress Controller
annotations:
  nginx.org/websocket-services: "ws-service"
Note: The Community controller handles WebSocket upgrades automatically. The F5 NGINX Ingress Controller provides an explicit annotation to enable WebSocket support for specific services.
Kubernetes Ingress NGINX (Ingress-NGINX)
# No direct equivalent
# Port configuration is handled at the
# controller/service level
F5 NGINX Ingress Controller
annotations:
  nginx.org/listen-ports: "8080,8443"
  nginx.org/listen-ports-ssl: "8443,9443"
Note: These annotations configure custom HTTP and HTTPS listen ports for the Ingress resource. Useful when the controller needs to listen on non-standard ports.
Kubernetes Ingress NGINX (Ingress-NGINX)
# No direct equivalent
# Canary/traffic-splitting uses separate
# canary annotations
F5 NGINX Ingress Controller
# Master Ingress (server-level config)
annotations:
  nginx.org/mergeable-ingress-type: "master"
---
# Minion Ingress (route-level config)
annotations:
  nginx.org/mergeable-ingress-type: "minion"
Note: Mergeable Ingress types allow splitting configuration across multiple Ingress resources for the same host. The master handles server-level settings while minion resources define individual routes.
Kubernetes Ingress NGINX (Ingress-NGINX)
# Custom headers set via ConfigMap
# proxy-set-headers in controller ConfigMap
F5 NGINX Ingress Controller
annotations:
  nginx.org/proxy-set-headers: "my-custom-headers"
---
# ConfigMap with custom headers
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-custom-headers
data:
  X-Custom-Header: "my-value"
  X-Forwarded-Proto: "$scheme"
Note: References a ConfigMap containing header key-value pairs that will be added to proxied requests. The annotation value is the ConfigMap name.
Kubernetes Ingress NGINX (Ingress-NGINX)
# No direct equivalent
# Redirect code is not configurable
# per Ingress resource
F5 NGINX Ingress Controller
annotations:
  nginx.org/http-redirect-code: "308"
  nginx.org/redirect-to-https: "true"
Note: Customizes the HTTP redirect status code used when redirecting to HTTPS (default: 301). Common values: 301 (permanent), 302 (temporary), 307 (temporary, preserves method), 308 (permanent, preserves method).
Note: nginx.org/listen-ports and nginx.org/listen-ports-ssl configure custom HTTP/HTTPS ports. nginx.org/mergeable-ingress-type enables master/minion Ingress merging for shared server-level config. nginx.org/proxy-set-headers sets custom proxy headers. nginx.org/http-redirect-code customizes the HTTP redirect status code (default: 301).

ModSecurity / WAF

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/enable-modsecurity: "true"
  nginx.ingress.kubernetes.io/enable-owasp-core-rules: "true"
  nginx.ingress.kubernetes.io/modsecurity-snippet: |
    SecRuleEngine On
    SecRule ARGS "@contains <script>" "id:1,deny,status:403"
  nginx.ingress.kubernetes.io/modsecurity-transaction-id: "$request_id"
F5 NGINX Ingress Controller
# No direct OSS replacement
# For WAF capabilities, see the Plus WAF section
# which uses the F5 WAF for NGINX (App Protect)
No OSS Equivalent: The F5 NGINX Ingress Controller does not include ModSecurity or any built-in WAF module. For WAF capabilities, F5 WAF for NGINX is available with NGINX Plus — see the WAF section in Plus Mappings for migration examples.
Note: The community controller's ModSecurity support relies on a third-party module compiled into the controller image — it is not a native Kubernetes-integrated WAF solution. Neither controller provides an open-source WAF alternative.

Passive Health Checks

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
# ConfigMap keys (global):
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  upstream-fail-timeout: "30s"
  upstream-max-fails: "3"
F5 NGINX Ingress Controller
# Per-Ingress annotations:
annotations:
  nginx.org/fail-timeout: "30s"  # upstream-fail-timeout
  nginx.org/max-fails: "3"  # upstream-max-fails
Note: The community controller configures passive health checks globally via ConfigMap keys. The F5 NGINX Ingress Controller uses per-Ingress annotations (nginx.org/max-fails, nginx.org/fail-timeout), giving you finer-grained control. The proxy-next-upstream annotations (see Proxy Settings) complement passive health checks by controlling retry behavior when an upstream fails.
Kubernetes Ingress NGINX (Ingress-NGINX)
# ConfigMap keys (global):
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  upstream-fail-timeout: "30s"
  upstream-max-fails: "3"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      fail-timeout: 30s  # upstream-fail-timeout
      max-fails: 3  # upstream-max-fails
Note: The VirtualServer CRD allows passive health check settings per upstream. For active health checks that proactively probe endpoints, see Active Health Checks in Plus Mappings.

Proxy Settings

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-next-upstream: "error timeout"
  nginx.ingress.kubernetes.io/proxy-next-upstream-timeout: "30"
  nginx.ingress.kubernetes.io/proxy-next-upstream-tries: "3"
F5 NGINX Ingress Controller
annotations:
  nginx.org/proxy-next-upstream: "error timeout"
  nginx.org/proxy-next-upstream-timeout: "30s"
  nginx.org/proxy-next-upstream-tries: "3"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-next-upstream: "error timeout"
  nginx.ingress.kubernetes.io/proxy-next-upstream-timeout: "30"
  nginx.ingress.kubernetes.io/proxy-next-upstream-tries: "3"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      next-upstream: "error timeout"
      next-upstream-timeout: 30s
      next-upstream-tries: 3

Rate Limiting

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/limit-burst-multiplier: "5"
  nginx.ingress.kubernetes.io/limit-connections: "5"
  nginx.ingress.kubernetes.io/limit-rate: "100k"
  nginx.ingress.kubernetes.io/limit-rate-after: "1m"
  nginx.ingress.kubernetes.io/limit-rpm: "300"
  nginx.ingress.kubernetes.io/limit-rps: "10"
  nginx.ingress.kubernetes.io/limit-whitelist: "10.0.0.0/8,172.16.0.0/12"
F5 NGINX Ingress Controller
annotations:
  nginx.org/limit-req-burst: "50"  # limit-rps × limit-burst-multiplier
  nginx.org/limit-req-delay: "5"
  nginx.org/limit-req-rate: "10r/s"  # limit-rps
  # limit-connections, limit-rate, limit-rate-after, limit-rpm,
  # limit-whitelist → use location-snippets:
  nginx.org/location-snippets: |
    limit_conn addr 5;  # limit-connections
    limit_rate 100k;  # limit-rate
    limit_rate_after 1m;  # limit-rate-after
Additional rate limiting annotations: nginx.org/limit-req-key (default: ${binary_remote_addr}), nginx.org/limit-req-zone-size, nginx.org/limit-req-no-delay, nginx.org/limit-req-dry-run, nginx.org/limit-req-log-level, nginx.org/limit-req-reject-code (default: 429), nginx.org/limit-req-scale (divides rate by pod count for consistency during autoscaling).
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/limit-burst-multiplier: "5"
  nginx.ingress.kubernetes.io/limit-connections: "5"
  nginx.ingress.kubernetes.io/limit-rate: "100k"
  nginx.ingress.kubernetes.io/limit-rate-after: "1m"
  nginx.ingress.kubernetes.io/limit-rpm: "300"
  nginx.ingress.kubernetes.io/limit-rps: "10"
  nginx.ingress.kubernetes.io/limit-whitelist: "10.0.0.0/8,172.16.0.0/12"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: rate-limit
spec:
  rateLimit:
    burst: 50  # limit-rps × limit-burst-multiplier
    key: ${binary_remote_addr}
    rate: 10r/s  # limit-rps
    rejectCode: 429
    zoneSize: 10M
Note: The rateLimit Policy CRD covers limit-rps and limit-burst-multiplier. The community annotations limit-connections, limit-rate, limit-rate-after, limit-rpm, and limit-whitelist have no Policy CRD equivalents — use location-snippets for those (see the Annotation Approach).

Redirects

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/permanent-redirect: "https://new.example.com"
  nginx.ingress.kubernetes.io/permanent-redirect-code: "308"
  # Or for temporary redirects:
  # nginx.ingress.kubernetes.io/temporal-redirect: "https://maintenance.example.com"
  # nginx.ingress.kubernetes.io/temporal-redirect-code: "307"
  # Or for www redirect:
  # nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  host: old.example.com
  routes:
    - path: /
      action:
        redirect:
          url: https://new.example.com${request_uri}  # permanent-redirect
          code: 308  # permanent-redirect-code
  # temporal-redirect + temporal-redirect-code:
  # - path: /maintenance
  #   action:
  #     redirect:
  #       url: https://maintenance.example.com
  #       code: 307
  # from-to-www-redirect → separate VirtualServer:
  # server-snippets: |
  #   return 301 https://www.example.com$request_uri;

Rewrites

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/app-root: "/dashboard"
  nginx.ingress.kubernetes.io/rewrite-target: /$2
  nginx.ingress.kubernetes.io/use-regex: "true"
# path: /api(/|$)(.*)
F5 NGINX Ingress Controller
annotations:
  nginx.org/app-root: "/dashboard"
  nginx.org/path-regex: "case_sensitive"
  nginx.org/rewrite-target: /$2
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/app-root: "/dashboard"
  nginx.ingress.kubernetes.io/rewrite-target: /$2
  nginx.ingress.kubernetes.io/use-regex: "true"
# path: /api(/|$)(.*)
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  routes:
    - path: /
      action:
        redirect:
          url: /dashboard  # app-root
          code: 302
    - path: ~ ^/api(/|$)(.*)  # use-regex
      action:
        proxy:
          upstream: api
          rewritePath: /$2  # rewrite-target
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-redirect-from: "http://internal/"
  nginx.ingress.kubernetes.io/proxy-redirect-to: "https://external/"
F5 NGINX Ingress Controller
annotations:
  nginx.org/server-snippets: |
    proxy_redirect http://internal/ https://external/;
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-redirect-from: "http://internal/"
  nginx.ingress.kubernetes.io/proxy-redirect-to: "https://external/"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  server-snippets: |  # proxy-redirect-from / proxy-redirect-to
    proxy_redirect http://internal/ https://external/;
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      action:
        pass: backend
Note: The F5 NGINX Ingress Controller also supports a proxy-redirect ConfigMap key for global proxy redirect behavior. Use server-snippets for per-Ingress control.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-cookie-domain: "internal.example.com external.example.com"
  nginx.ingress.kubernetes.io/proxy-cookie-path: "/internal/ /external/"
F5 NGINX Ingress Controller
annotations:
  nginx.org/location-snippets: |
    proxy_cookie_domain internal.example.com external.example.com;
    proxy_cookie_path /internal/ /external/;
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-cookie-domain: "internal.example.com external.example.com"
  nginx.ingress.kubernetes.io/proxy-cookie-path: "/internal/ /external/"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |  # proxy-cookie-domain / proxy-cookie-path
        proxy_cookie_domain internal.example.com external.example.com;
        proxy_cookie_path /internal/ /external/;
      action:
        pass: backend
Note: The F5 NGINX Ingress Controller does not have dedicated annotations for proxy cookie rewriting. Use location-snippets to configure proxy_cookie_domain and proxy_cookie_path directives.

Route Delegation

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Tip: This feature requires CRDs. See Installing CRDs for Helm and manifest installation instructions.
Note: The community controller has no equivalent for cross-namespace route delegation. VirtualServerRoute allows different teams to manage their own routing rules while a central VirtualServer controls the domain.
Kubernetes Ingress NGINX (Ingress-NGINX)
# No equivalent — each team must have
# their own Ingress resource per host,
# or share a single Ingress manifest
F5 NGINX Ingress Controller
# VirtualServer (platform team)
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: main-app
  namespace: platform
spec:
  host: app.example.com
  routes:
    - path: /api
      route: api-team/api-routes
    - path: /web
      route: web-team/web-routes
---
# VirtualServerRoute (API team)
apiVersion: k8s.nginx.org/v1
kind: VirtualServerRoute
metadata:
  name: api-routes
  namespace: api-team
spec:
  host: app.example.com
  upstreams:
    - name: api-v1
      service: api-v1-service
      port: 80
  subroutes:
    - path: /api/v1
      action:
        pass: api-v1

SSL/TLS

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
F5 NGINX Ingress Controller
annotations:
  nginx.org/ssl-services: "my-service"
Note: For gRPC backends, use nginx.org/grpc-services: "my-service" instead.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 443
      tls:
        enable: true  # backend-protocol
Note: For gRPC backends, use type: grpc on the upstream instead. For gRPC over TLS, combine both: type: grpc and tls.enable: true.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
F5 NGINX Ingress Controller
annotations:
  nginx.org/redirect-to-https: "true"
Note: force-ssl-redirect in the Community controller forces HTTPS redirect even without a TLS certificate. Use nginx.org/redirect-to-https for similar behavior.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/ssl-ciphers: "HIGH:!aNULL:!MD5"
F5 NGINX Ingress Controller
annotations:
  nginx.org/ssl-ciphers: "HIGH:!aNULL:!MD5"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers: "true"
F5 NGINX Ingress Controller
annotations:
  nginx.org/ssl-prefer-server-ciphers: "true"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/ssl-redirect: "true"
F5 NGINX Ingress Controller
annotations:
  # Option 1: For TLS terminated at load balancer
  nginx.org/redirect-to-https: "true"
  # OR Option 2: For unconditional redirect
  nginx.org/ssl-redirect: "true"
Important distinction:
  • nginx.org/redirect-to-https checks the X-Forwarded-Proto header before redirecting. Use this when TLS terminates at a load balancer in front of NGINX.
  • nginx.org/ssl-redirect performs an unconditional 301 redirect for all incoming HTTP traffic. Use this when NGINX handles TLS termination directly. Default: true.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/ssl-passthrough: "true"
F5 NGINX Ingress Controller
# ssl-passthrough → TransportServer with built-in tls-passthrough listener
apiVersion: k8s.nginx.org/v1
kind: TransportServer
metadata:
  name: ssl-passthrough-app
spec:
  listener:
    name: tls-passthrough  # ssl-passthrough
    protocol: TLS_PASSTHROUGH
  host: secure.example.com
  upstreams:
    - name: backend
      service: secure-svc
      port: 443
  action:
    pass: backend
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/preserve-trailing-slash: "true"
  nginx.ingress.kubernetes.io/ssl-redirect: "true"
F5 NGINX Ingress Controller
annotations:
  nginx.org/redirect-to-https: "true"
  # Trailing slash is preserved by default in redirects
Note: This annotation affects SSL redirect behavior in the Community controller. The F5 NGINX Ingress Controller preserves trailing slashes by default in its redirect logic.

TCP/UDP Load Balancing

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
# ConfigMap for TCP services
apiVersion: v1
kind: ConfigMap
metadata:
  name: tcp-services
data:
  3306: "default/mysql:3306"
F5 NGINX Ingress Controller
# tcp-services ConfigMap → GlobalConfiguration + TransportServer
apiVersion: k8s.nginx.org/v1
kind: GlobalConfiguration
spec:
  listeners:
    - name: mysql-tcp
      port: 3306  # "3306" key from tcp-services ConfigMap
      protocol: TCP
---
apiVersion: k8s.nginx.org/v1
kind: TransportServer
spec:
  listener:
    name: mysql-tcp
    protocol: TCP
  upstreams:
    - name: mysql
      service: mysql  # "default/mysql:3306" value from ConfigMap
      port: 3306
  action:
    pass: mysql

Timeouts

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
F5 NGINX Ingress Controller
annotations:
  nginx.org/proxy-connect-timeout: "10s"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      connect-timeout: "10s"  # proxy-connect-timeout
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
F5 NGINX Ingress Controller
annotations:
  nginx.org/proxy-read-timeout: "60s"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      read-timeout: "60s"  # proxy-read-timeout
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
F5 NGINX Ingress Controller
annotations:
  nginx.org/proxy-send-timeout: "60s"
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      send-timeout: "60s"  # proxy-send-timeout

Upstream Configuration

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
# No per-Ingress upstream tuning
# Community controller uses ConfigMap defaults
F5 NGINX Ingress Controller
annotations:
  nginx.org/fail-timeout: "10s"
  nginx.org/keepalive: "32"
  nginx.org/max-conns: "100"
  nginx.org/max-fails: "3"
  nginx.org/upstream-zone-size: "256k"
Kubernetes Ingress NGINX (Ingress-NGINX)
# No per-Ingress upstream tuning
# Community controller uses ConfigMap defaults
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      fail-timeout: "10s"
      keepalive: 32
      max-conns: 100
      max-fails: 3
Note: These are F5 NGINX Ingress Controller-only features for fine-tuning upstream behavior. The community controller does not provide per-Ingress control over these settings.

NGINX Plus Mappings

These features require an NGINX Plus subscription and provide enterprise-grade capabilities. Most Plus features use CRD-based configuration—click the expandable rows to see full examples. All items are annotations unless otherwise indicated with a badge. Community annotations use the nginx.ingress.kubernetes.io/ prefix; F5 NGINX Ingress Controller Plus annotations use the nginx.com/ prefix (or appprotect.f5.com/ for F5 WAF for NGINX).

How to Read the Mappings
  • Annotation-to-annotation rows : Direct mappings where you change the annotation name/prefix. Click to expand and see before/after Ingress YAML examples.
  • CRD-based rows : Annotations that map to a CRD (VirtualServer, Policy, or TransportServer). Click to expand for full CRD examples with installation instructions.
  • Grouped annotations: Related annotations that work together are grouped in a single expandable row, showing how they map to a unified configuration.
  • F5 NGINX Ingress Controller-only rows: Rows marked No direct equivalent on the left are features unique to the F5 NGINX Ingress Controller with no community equivalent.

API Key Authentication

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
# No direct equivalent
# Requires custom Lua or external service
F5 NGINX Ingress Controller (Plus)
apiVersion: v1
kind: Secret
metadata:
  name: api-keys
type: nginx.org/apikey
data:
  client1: YXBpa2V5MTIzNDU=
---
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: api-key-auth
spec:
  apiKey:
    clientSecret: api-keys
    suppliedIn:
      header: [X-API-Key]
      query: [api_key]

Active Health Checks

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
annotations:
  nginx.com/health-checks: "true"
  nginx.com/health-checks-mandatory: "true"
  nginx.com/health-checks-mandatory-queue: "5"
  nginx.com/slow-start: "30s"
Note: Active health checks proactively probe upstream endpoints at configurable intervals, rather than waiting for client requests to fail. These require NGINX Plus. For more granular control (custom paths, intervals, status matching), use the VirtualServer CRD approach. For passive health checks (OSS), see Passive Health Checks in OSS Mappings.
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      healthCheck:  # health-checks
        enable: true
        fails: 3
        interval: 5s
        jitter: 2s
        mandatory: true  # health-checks-mandatory
        passes: 2
        path: /health
        persistent: true  # health-checks-mandatory-queue
        port: 8080
        statusMatch: "200-399"
      slowStart: 30s  # slow-start
Note: The VirtualServer CRD provides the most granular control over active health checks. Active health checks and slowStart require NGINX Plus. For passive health checks (OSS), see Passive Health Checks in OSS Mappings.

JWT Authentication

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
# No direct equivalent
# Requires external auth service
F5 NGINX Ingress Controller (Plus)
annotations:
  nginx.com/jwt-key: "my-jwk-secret"
  nginx.com/jwt-login-url: "https://login.example.com"
  nginx.com/jwt-realm: "API"
  nginx.com/jwt-token: "$http_authorization"
Note: The annotation approach requires a Kubernetes Secret containing the JWK. The Policy CRD approach is recommended for new deployments as it provides more granular control including JWKS URI support and key caching.
Kubernetes Ingress NGINX (Ingress-NGINX)
# No direct equivalent
# Requires external auth service
F5 NGINX Ingress Controller (Plus)
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: jwt-auth
spec:
  jwt:
    jwksURI: "https://idp.example.com/.well-known/jwks.json"  # jwt-key (enhanced)
    keyCache: 1h
    realm: "API"  # jwt-realm
    token: "$http_authorization"  # jwt-token
Note: The Policy CRD approach is recommended for new deployments. It supports JWKS URIs, key caching, and more granular configuration. The jwt-login-url annotation does not have a direct Policy CRD field — configure redirect logic separately via VirtualServer errorPages or server-snippets for 401 responses.

OIDC Authentication

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-cache-duration: "200 10m, 401 1m"
  nginx.ingress.kubernetes.io/auth-cache-key: "$remote_addr"
  nginx.ingress.kubernetes.io/auth-method: "GET"
  nginx.ingress.kubernetes.io/auth-proxy-set-headers: "default/auth-headers"
  nginx.ingress.kubernetes.io/auth-request-redirect: "https://app.example.com/callback"
  nginx.ingress.kubernetes.io/auth-response-headers: "X-Auth-User"
  nginx.ingress.kubernetes.io/auth-signin: "https://auth.example.com/oauth2/start"
  nginx.ingress.kubernetes.io/auth-signin-redirect-param: "rd"
  nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/oauth2/auth"
F5 NGINX Ingress Controller (Plus)
apiVersion: v1
kind: Secret
metadata:
  name: oidc-secret
type: nginx.org/oidc
data:
  client-secret: c2VjcmV0...
---
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: oidc-auth
spec:
  oidc:
    authEndpoint: "https://idp.example.com/authorize"  # auth-signin
    clientID: "my-client-id"
    clientSecret: oidc-secret
    jwksURI: "https://idp.example.com/.well-known/jwks.json"
    scope: "openid+profile+email"
    tokenEndpoint: "https://idp.example.com/token"  # auth-url
Note: Native OIDC support eliminates the need for oauth2-proxy sidecar. The community auth-url/auth-signin pattern uses the auth_request module (typically with oauth2-proxy) and supports any external auth service, not just OIDC. The community annotations auth-cache-duration, auth-cache-key, auth-method, auth-proxy-set-headers, auth-request-redirect, auth-response-headers, and auth-signin-redirect-param have no direct OIDC Policy equivalents — the Policy manages these concerns internally. If you need generic external auth (non-OIDC), use nginx.org/location-snippets with the auth_request directive.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-keepalive: "10"
  nginx.ingress.kubernetes.io/auth-keepalive-requests: "100"
  nginx.ingress.kubernetes.io/auth-keepalive-share-vars: "true"
  nginx.ingress.kubernetes.io/auth-keepalive-timeout: "60"
  nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/verify"
F5 NGINX Ingress Controller
# No direct auth-keepalive annotations; use snippets
# for auth_request subrequest customization.
annotations:
  nginx.org/location-snippets: |
    auth_request /auth;
    auth_request_set $auth_user $upstream_http_x_auth_user;
    proxy_set_header X-Auth-User $auth_user;
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-keepalive: "10"
  nginx.ingress.kubernetes.io/auth-keepalive-requests: "100"
  nginx.ingress.kubernetes.io/auth-keepalive-share-vars: "true"
  nginx.ingress.kubernetes.io/auth-keepalive-timeout: "60"
  nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/verify"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |
        auth_request /auth;
        auth_request_set $auth_user $upstream_http_x_auth_user;
        proxy_set_header X-Auth-User $auth_user;
      action:
        pass: backend
Note: The community controller's auth-keepalive, auth-keepalive-requests, auth-keepalive-share-vars, and auth-keepalive-timeout annotations configure keepalive connections to the external auth service. The F5 NGINX Ingress Controller doesn't have dedicated auth_request annotations. If using the F5 NGINX Ingress Controller OIDC Policy CRD, keepalive is managed internally. For generic external auth, use nginx.org/location-snippets with the auth_request directive.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-snippet: |
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Forwarded-Method $request_method;
  nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/verify"
F5 NGINX Ingress Controller
annotations:
  nginx.org/location-snippets: |
    auth_request /auth;
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Forwarded-Method $request_method;
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-snippet: |
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Forwarded-Method $request_method;
  nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/verify"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |
        auth_request /auth;
        proxy_set_header X-Original-URI $request_uri;
        proxy_set_header X-Forwarded-Method $request_method;
      action:
        pass: backend
Note: The community auth-snippet injects custom NGINX directives into the auth subrequest block. Since the F5 NGINX Ingress Controller uses the OIDC Policy CRD for authentication (or location-snippets for generic auth), any custom directives should be included directly in the nginx.org/location-snippets annotation alongside the auth_request directive.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-always-set-cookie: "true"
  nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/verify"
F5 NGINX Ingress Controller
# Use snippets to propagate cookies from auth responses
annotations:
  nginx.org/location-snippets: |
    auth_request /auth;
    auth_request_set $auth_cookie $upstream_http_set_cookie;
    add_header Set-Cookie $auth_cookie;
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/auth-always-set-cookie: "true"
  nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/verify"
F5 NGINX Ingress Controller
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: my-app
spec:
  host: app.example.com
  upstreams:
    - name: backend
      service: backend-svc
      port: 80
  routes:
    - path: /
      location-snippets: |
        auth_request /auth;
        auth_request_set $auth_cookie $upstream_http_set_cookie;
        add_header Set-Cookie $auth_cookie;
      action:
        pass: backend
Note: The community auth-always-set-cookie ensures Set-Cookie headers from the auth service are forwarded to the client regardless of the response code. The F5 NGINX Ingress Controller's OIDC Policy handles cookies internally. For generic external auth, use nginx.org/location-snippets with auth_request_set to capture and forward cookies.
Kubernetes Ingress NGINX (Ingress-NGINX)
# Opt out of global external auth for this Ingress
annotations:
  nginx.ingress.kubernetes.io/enable-global-auth: "false"
F5 NGINX Ingress Controller
# The F5 NGINX Ingress Controller does not have a global external
# auth feature. Authentication is configured per-resource
# via Policy CRDs or annotations.
# No migration action needed for this annotation.
Note: The community controller supports global external authentication via ConfigMap keys (global-auth-url, etc.). The enable-global-auth: "false" annotation opts an Ingress out of this global setting. The F5 NGINX Ingress Controller does not have global external auth — authentication is always configured per-resource (via OIDC/JWT Policy CRDs or location-snippets). This annotation can be safely removed during migration.

Session Affinity / Sticky Sessions

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/affinity: "cookie"
  nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
  nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
  nginx.ingress.kubernetes.io/session-cookie-name: "SERVERID"
  nginx.ingress.kubernetes.io/session-cookie-path: "/"
F5 NGINX Ingress Controller (Plus)
annotations:
  nginx.com/sticky-cookie-services: "serviceName=my-service srv_id expires=48h path=/"
Note: The nginx.com/sticky-cookie-services annotation provides a simpler way to configure session persistence via Ingress annotations. For more granular control (domain, secure, samesite, httpOnly), use the VirtualServer CRD approach.
Kubernetes Ingress NGINX (Ingress-NGINX)
annotations:
  nginx.ingress.kubernetes.io/affinity: "cookie"
  nginx.ingress.kubernetes.io/affinity-mode: "balanced"
  nginx.ingress.kubernetes.io/session-cookie-change-on-failure: "true"
  nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none: "true"
  nginx.ingress.kubernetes.io/session-cookie-domain: ".example.com"
  nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
  nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
  nginx.ingress.kubernetes.io/session-cookie-name: "SERVERID"
  nginx.ingress.kubernetes.io/session-cookie-path: "/"
  nginx.ingress.kubernetes.io/session-cookie-samesite: "Strict"
  nginx.ingress.kubernetes.io/session-cookie-secure: "true"
F5 NGINX Ingress Controller (Plus)
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: backend
      service: my-service
      port: 80
      sessionCookie:
        domain: ".example.com"  # session-cookie-domain
        enable: true  # affinity: "cookie"
        expires: 48h  # session-cookie-expires / session-cookie-max-age
        httpOnly: true
        name: SERVERID  # session-cookie-name
        path: /  # session-cookie-path
        samesite: strict  # session-cookie-samesite
        secure: true  # session-cookie-secure
Note: The VirtualServer CRD provides the most granular control over session persistence. Community-specific features affinity-mode, session-cookie-change-on-failure, and session-cookie-conditional-samesite-none have no direct equivalents in the F5 NGINX Ingress Controller.
Kubernetes Ingress NGINX (Ingress-NGINX)
# Canary with session affinity behavior
annotations:
  nginx.ingress.kubernetes.io/affinity: "cookie"
  nginx.ingress.kubernetes.io/affinity-canary-behavior: "sticky"
  nginx.ingress.kubernetes.io/canary: "true"
  nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
F5 NGINX Ingress Controller (Plus)
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
spec:
  upstreams:
    - name: main
      service: main-svc
      port: 80
      sessionCookie:  # affinity: "cookie" + affinity-canary-behavior: "sticky"
        enable: true
        httpOnly: true
        name: SERVERID
    - name: canary
      service: canary-svc
      port: 80
      sessionCookie:  # affinity: "cookie" + affinity-canary-behavior: "sticky"
        enable: true
        httpOnly: true
        name: SERVERID
  routes:
    - path: /
      matches:
        - conditions:
            - header: X-Canary  # canary-by-header
              value: "true"
          action:
            pass: canary
      action:
        pass: main
Note: The community controller's affinity-canary-behavior: "sticky" ensures users routed to canary by session affinity continue to be served by the canary. The F5 NGINX Ingress Controller separates these concerns: canary routing uses VirtualServer matches[], splits[], while session persistence uses the VirtualServer upstreams[].sessionCookie field. The sessionCookie feature requires NGINX Plus. See Session Affinity for more details.

WAF (Web Application Firewall)

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
annotations:
  appprotect.f5.com/app-protect-enable: "True"
  appprotect.f5.com/app-protect-policy: "default/waf-policy"
  appprotect.f5.com/app-protect-security-log: "default/log-config"
  appprotect.f5.com/app-protect-security-log-destination: "syslog:server=syslog:514"
  appprotect.f5.com/app-protect-security-log-enable: "True"
Note: The appprotect.f5.com/ annotations enable F5 WAF for NGINX via Ingress annotations. The Policy CRD approach is recommended for new deployments as it provides more flexibility and reusability.
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: waf-policy
spec:
  waf:
    apPolicy: "default/waf-policy"
    enable: true
    securityLogs:
      - enable: true
        apLogConf: "default/log-config"
        logDest: "syslog:server=syslog:514"
Note: F5 WAF for NGINX provides enterprise WAF capabilities beyond ModSecurity. The Policy CRD approach allows WAF policies to be shared across multiple VirtualServer routes. For community ModSecurity annotations, see the ModSecurity / WAF section in OSS Mappings.

ConfigMap Mappings

Both the community and F5 NGINX Ingress Controllers use a Kubernetes ConfigMap for global NGINX settings. Many keys share the same name, but some are renamed or have different semantics. The keys listed below are community ConfigMap keys that have an equivalent F5 NGINX Ingress Controller ConfigMap key.

Tip: A significant number of ConfigMap keys are identical between the two controllers — you can migrate those without any changes. The tables below highlight both the identical keys and the ones that need renaming or adjustment.

Identical Keys

These keys have the same name and semantics in both controllers. No changes are needed when migrating.

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  proxy-connect-timeout: "30"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  proxy-connect-timeout: "30"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  proxy-read-timeout: "120"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  proxy-read-timeout: "120"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  proxy-send-timeout: "60"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  proxy-send-timeout: "60"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  client-body-buffer-size: "16k"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  client-body-buffer-size: "16k"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  proxy-buffering: "on"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  proxy-buffering: "on"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  proxy-buffer-size: "8k"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  proxy-buffer-size: "8k"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  proxy-busy-buffers-size: "16k"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  proxy-busy-buffers-size: "16k"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  ssl-ciphers: "ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  ssl-ciphers: "ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  ssl-protocols: "TLSv1.2 TLSv1.3"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  ssl-protocols: "TLSv1.2 TLSv1.3"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  ssl-redirect: "true"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  ssl-redirect: "true"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  hsts: "true"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  hsts: "true"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  hsts-max-age: "31536000"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  hsts-max-age: "31536000"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  hsts-include-subdomains: "true"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  hsts-include-subdomains: "true"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  error-log-level: "warn"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  error-log-level: "warn"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  worker-processes: "auto"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  worker-processes: "auto"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  worker-cpu-affinity: "auto"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  worker-cpu-affinity: "auto"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  worker-shutdown-timeout: "30s"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  worker-shutdown-timeout: "30s"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  server-tokens: "false"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  server-tokens: "false"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  map-hash-bucket-size: "128"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  map-hash-bucket-size: "128"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  variables-hash-bucket-size: "256"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  variables-hash-bucket-size: "256"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  variables-hash-max-size: "2048"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  variables-hash-max-size: "2048"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  otel-service-name: "nginx-ingress"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  otel-service-name: "nginx-ingress"

Proxy & Buffering

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  proxy-body-size: "8m"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  client-max-body-size: "8m"
Note: Both keys map to the NGINX client_max_body_size directive. The F5 NGINX Ingress Controller uses the NGINX-native naming convention.
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  proxy-buffers-number: "4"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  proxy-buffers: "4 8k"
Note: The community key takes just a number (buffer count). The F5 NGINX Ingress Controller key takes the full NGINX proxy_buffers format: "number size" (e.g., "4 8k").

SSL/TLS

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  ssl-dh-param: "default/dh-param"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  ssl-dhparam-file: "/etc/nginx/secrets/dhparam.pem"
Note: The community key references a Kubernetes secret name, while the F5 NGINX Ingress Controller key expects a file path to the DH parameters file mounted into the pod.
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  force-ssl-redirect: "true"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  redirect-to-https: "true"
Note: The community controller uses an X-Forwarded-Proto header check to determine whether to redirect. The F5 NGINX Ingress Controller performs a direct HTTP-to-HTTPS redirect.
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  hsts: "true"
  hsts-preload: "true"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  hsts: "true"
Note: The F5 NGINX Ingress Controller's hsts: "true" includes the preload directive by default. There is no separate hsts-preload key — just enable hsts.

Logging

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  disable-access-log: "true"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  access-log-off: "true"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  log-format-upstream: '$remote_addr - $request_id'
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  log-format: '$remote_addr - $request_id'
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  log-format-stream: '$remote_addr [$time_local]'
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  stream-log-format: '$remote_addr [$time_local]'
Note: The word order is swapped: community uses log-format-stream, while the F5 NGINX Ingress Controller uses stream-log-format.
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  log-format-escape-json: "true"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  log-format-escaping: "json"
Note: The community controller uses separate boolean keys (log-format-escape-json and log-format-escape-none). The F5 NGINX Ingress Controller uses a single key with a string value: "json", "default", or "none".

Worker & Performance

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  max-worker-connections: "16384"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  worker-connections: "16384"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  max-worker-open-files: "65536"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  worker-rlimit-nofile: "65536"

Upstream & Load Balancing

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  upstream-keepalive-connections: "320"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  keepalive: "320"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  load-balance: "ewma"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  lb-method: "random two least_conn"
Note: The community controller defaults to round_robin and supports methods like ewma and least_conn. The F5 NGINX Ingress Controller defaults to random two least_conn and uses standard NGINX load balancing method names.

Keepalive & Timeouts

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  keep-alive: "75"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  keepalive-timeout: "75s"
Note: Both map to the NGINX keepalive_timeout directive. The F5 NGINX Ingress Controller key uses the NGINX-native name and expects a time unit suffix (e.g., "75s").
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  keep-alive-requests: "1000"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  keepalive-requests: "1000"

Server & Hash Configuration

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  server-name-hash-max-size: "1024"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  server-names-hash-max-size: "1024"
Note: Subtle difference: the community uses server-name-hash-max-size and server-name-hash-bucket-size (singular "name"), while the F5 NGINX Ingress Controller uses server-names-hash-max-size and server-names-hash-bucket-size (plural "names") matching the NGINX directives.
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  server-name-hash-bucket-size: "128"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  server-names-hash-bucket-size: "128"

Headers

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  hide-headers: "X-Powered-By,Server"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  proxy-hide-headers: "X-Powered-By,Server"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  proxy-real-ip-cidr: "10.0.0.0/8,172.16.0.0/12"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  set-real-ip-from: "10.0.0.0/8,172.16.0.0/12"
Note: Both map to the NGINX set_real_ip_from directive. The F5 NGINX Ingress Controller key uses the NGINX-native naming convention.

Protocol

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  use-http2: "true"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  http2: "true"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  use-proxy-protocol: "true"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  proxy-protocol: "true"

Snippets

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  main-snippet: |
    worker_rlimit_nofile 65535;
  http-snippet: |
    map $http_upgrade $connection_upgrade {
      default upgrade;
      '' close;
    }
  server-snippet: |
    add_header X-Custom "value";
  location-snippet: |
    proxy_cache_valid 200 10m;
  stream-snippet: |
    log_format stream '$remote_addr [$time_local]';
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  main-snippets: |
    worker_rlimit_nofile 65535;
  http-snippets: |
    map $http_upgrade $connection_upgrade {
      default upgrade;
      '' close;
    }
  server-snippets: |
    add_header X-Custom "value";
  location-snippets: |
    proxy_cache_valid 200 10m;
  stream-snippets: |
    log_format stream '$remote_addr [$time_local]';
Note: All snippet keys change from singular to plural: main-snippetmain-snippets, http-snippethttp-snippets, server-snippetserver-snippets, location-snippetlocation-snippets, stream-snippetstream-snippets.

OpenTelemetry

Kubernetes Ingress NGINX (Ingress-NGINX)F5 NGINX Ingress Controller
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  enable-opentelemetry: "true"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  otel-trace-in-http: "true"
Kubernetes Ingress NGINX (Ingress-NGINX) ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
data:
  otlp-collector-host: "otel-collector.observability"
  otlp-collector-port: "4317"
F5 NGINX Ingress Controller ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  otel-exporter-endpoint: "otel-collector.observability:4317"
Note: The community controller uses separate keys for host and port. The F5 NGINX Ingress Controller combines them into a single endpoint URL.