Skip to content

Moving from Ingress NGINX to Traefik

Caution

This page is being continuously updated as more edge cases of the NGINX Ingress Controller to Traefik migration are being discovered.

Due to the March 2026 retirement of Ingress NGINX, Welkin is adopting Traefik instead as the standard Ingress Controller. We are making the transition as smooth as possible for both Application Developers and Platform Administrators.

Welkin Clusters will run both Ingress NGINX and Traefik during a transitional period to make sure environments have the opportunity to safely migrate. As an application developer, you can start testing on Traefik today by stepwise adapting your services with just a few changes at a time.

Prerequisites

Confirm both controllers are operational.

kubectl get pods -n ingress-nginx
kubectl get pods -n traefik

You should be able to resolve the *.traefik.$DOMAIN to the Traefik LoadBalancer IP using for example dig.

export TRAEFIK_EXTERNAL_LB=$(dig "*.traefik.$DOMAIN" +short)

The Migration Strategy

To guarantee service continuity, we utilize a side-by-side validation strategy, where Traefik is configured to shadow your existing NGINX Ingress resources. By running both controllers simultaneously, you are able to verify your services in isolation before redirecting any production traffic.

Clone and Patch

For all Ingress resources, follow these steps to migrate from NGINX to Traefik:

  1. Create a copy of the existing Ingress resource.
  2. Update the ingressClassName to traefik.
  3. Replace NGINX-specific annotations or configuration snippets with the corresponding Traefik Middlewares.

Note

Traefik natively supports all standard Kubernetes Ingress fields.

If your Ingress uses only standard fields like paths and hosts without custom annotations, simply changing the ingressClassName is often sufficient.

Please read "Converting Annotations to Traefik Middlewares" for information about annotation support and instructions on how to create appropriate Traefik Middlewares.

Validation

You can verify that your services are running correctly on Traefik by sending a request directly to the Traefik LoadBalancer IP. This allows you to test the migration in isolation while your production DNS continues to point safely to NGINX.

Option 1: Client-Side Resolution

You can test routing without changing DNS by running:

curl --resolve example.base-domain.com:443:$TRAEFIK_EXTERNAL_LB https://example.base-domain.com

Alternatively, by updating your local /etc/hosts file to point your domain to the $TRAEFIK_EXTERNAL_LB IP.

This allows you to test the application in a browser on standard ports.

Option 2: Separate DNS

You can create separate Ingress objects and DNS pointing to the $TRAEFIK_EXTERNAL_LB IP.

The *.traefik.$DOMAIN DNS record pointing to the Traefik LoadBalancer can also be used directly.

The Permanent Switch

When validation is successful update your public DNS records to point to the Traefik LoadBalancer IP. Once propagation is complete, delete the old NGINX Ingress.

Converting Annotations to Traefik Middlewares

Traefik provides support for translating many common Ingress-NGINX annotations when using the nginx Ingress class name, though some may behave slightly differently.

However, not all annotations are supported. Some require additional steps and need to be converted to Traefik Middlewares. For more details on which annotations are supported or unsupported, see Traefik & Ingresses with NGINX annotations.

Caution

Due to a limitation in how Traefik's translation between Ingress NGINX and its own configuration format works: if you continue to use Ingress-NGINX annotations, you cannot use Traefik Middlewares on the same resource. We therefore recommend a full conversion to Traefik Middlewares to avoid configuration issues.

In the following sections below are step-by-step guides to help you convert various types of annotations into their Traefik Middleware equivalents.

For rate-limiting and allowlisting -- which need a conversion from NGINX annotation to Traefik Middleware -- please find an example in our documentation on Network Model.

Here the allowlist annotation nginx.ingress.kubernetes.io/whitelist-source-range: 98.128.193.2/32 is currently not a supported annotation. To keep the allow-list feature a Middleware resource is created:

# Blocklisted IP's will get HTTP 403.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: demo-allowlist
  namespace: <namespace>
spec:
  ipAllowList:
    sourceRange:
      - 98.128.193.2/32

And the Traefik-specific Middleware annotation is added to the Ingress resource:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo-app
  annotations:
    # This Traefik-specific annotation "plugs the gap"
    traefik.ingress.kubernetes.io/router.middlewares: <namespace>-demo-allowlist@kubernetescrd
spec:
  # Remember to also update the Ingress class name to Traefik
  ingressClassName: traefik
...

Metrics

Ingress-NGINX Metric Traefik equivalent Notes
nginx_ingress_controller_requests traefik_service_requests_total Filter by code instead of status.
nginx_ingress_controller_request_duration_seconds traefik_service_request_duration_seconds Warning: Histogram buckets differ by default. (See longer explanation below)
nginx_ingress_controller_response_duration_seconds traefik_service_request_duration_seconds Partial: Traefik bundles "wait + processing" into a single metric. It does not separate "upstream time" from "total time" in metrics. (See longer explanation below)
nginx_ingress_controller_header_duration_seconds MISSING
nginx_ingress_controller_connect_duration_seconds MISSING
nginx_ingress_controller_response_size MISSING (Histogram) Traefik provides a Counter (traefik_service_responses_bytes_total) but no Histogram. You can track bandwidth, but not "size distribution."
nginx_ingress_controller_request_size MISSING (Histogram) Traefik provides a Counter (traefik_service_requests_bytes_total) but no Histogram.
nginx_ingress_controller_bytes_sent traefik_service_responses_bytes_total
nginx_ingress_controller_nginx_process_connections traefik_open_connections
nginx_ingress_controller_nginx_process_connections_total traefik_entrypoint_requests_total
nginx_ingress_controller_nginx_process_cpu_seconds_total process_cpu_seconds_total
nginx_ingress_controller_nginx_process_num_procs go_goroutines NGINX is multi-process (workers), Traefik is single-process (Goroutines).
nginx_ingress_controller_nginx_process_oldest_start_time_seconds process_start_time_seconds
nginx_ingress_controller_nginx_process_read_bytes_total traefik_entrypoint_requests_bytes_total
nginx_ingress_controller_nginx_process_requests_total traefik_entrypoint_requests_total
nginx_ingress_controller_nginx_process_resident_memory_bytes process_resident_memory_bytes
nginx_ingress_controller_nginx_process_virtual_memory_bytes process_virtual_memory_bytes
nginx_ingress_controller_nginx_process_write_bytes_total traefik_entrypoint_responses_bytes_total
nginx_ingress_controller_build_info MISSING Traefik does not output a specific metric for this. Could maybe use kube_pod_container_info instead.
nginx_ingress_controller_check_success MISSING Traefik lacks the this metric because it updates routing rules dynamically in memory, whereas NGINX must validate a static configuration file before every reload.
nginx_ingress_controller_config_hash MISSING Traefik does not expose a config hash. Use traefik_config_last_reload_success to verify sync timestamp.
nginx_ingress_controller_config_last_reload_successful MISSING
nginx_ingress_controller_config_last_reload_successful_timestamp_seconds traefik_config_last_reload_success Timestamp of last successful config update.
nginx_ingress_controller_ssl_certificate_info traefik_tls_certs_not_after Like NGINX, Traefik puts the cert details (CN, Issuer) in the labels. However, the value Differs, Traefik is the expiry timestamp and NGINX is 1.
nginx_ingress_controller_success MISSING traefik_config_reloads_total could possibly be used to prove the controller is active but they are not counting the same thing.
nginx_ingress_controller_orphan_ingress MISSING
nginx_ingress_controller_admission_config_size MISSING
nginx_ingress_controller_admission_render_duration MISSING
nginx_ingress_controller_admission_render_ingresses MISSING
nginx_ingress_controller_admission_roundtrip_duration MISSING
nginx_ingress_controller_admission_tested_duration MISSING
nginx_ingress_controller_admission_tested_ingresses MISSING

Troubleshooting

See Traefik's official migration docs for more troubleshooting advice.