Broken by Design

Migrating from ingress-nginx to traefik

As anybody using self hosted k8s knows the ingress-nginx Ingress got retired March 2026, and with the recent RCE found in nginx , it means by using ingress-nginx you are using vulnerable version that will receive no security updates.

What are the options

Replacing the ingress-nginx especially if you rely on it's specific annotation ( which made it so useful in the first place) can be quite a lot of work. Fortunately Traefik steeped in and started offering a compatible implementation that makes the migration (kind of) easy. They have a good page on how to check whether you can switch w/o issues , but unfortunately there is no good single source of how to configure it, besides snippets of configs, which when you are not familiar with traefik don't mean much, even with LLM.

Current setup

The cluster I am migrating this on , is a small few nodes cluster, with ingress-nginx running as a DaemonSet with hostNetwork, ingress-nginx installed with following helm values:

controller:
  hostNetwork: true
  allowSnippetAnnotations: true
  config:
    annotations-risk-level: Critical  
  ingressClassResource:
    default: true
 

Traefik helm config

# values.yaml
 
ports:
  # on my setup traefik port 8080 conflicts with k8s services so it had to be changed
  traefik:
    port: 9090
    hostPort: 9090
    # the api/dashboard should not be accessible directly to the world
    hostIP: 127.0.0.1
    expose:
      default: false
 
  # Defines the HTTP entry point named 'web'
  web:
    port: 80
    hostPort: 80
    # Instructs this entry point to redirect all traffic to the 'websecure' entry point
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true
    allowACMEByPass: true  # ←this is needed for cert manager
 
  # Defines the HTTPS entry point named 'websecure'
  websecure:
    port: 443
    hostPort: 443
 
  metrics:
    port: 9100
    hostPort: 9100
    exposedPort: 9100
    # the metrics endpoint should not be accessible directly to the world
    hostIP: 127.0.0.1
 
 
# Enables the dashboard in Secure Mode
api:
  dashboard: true
  insecure: false
 
ingressRoute:
  # Expose dashboard under a subdomain, thi
  dashboard:
    enabled: true
    matchRule: Host(`dashboard.my-domain.com`)
    entryPoints:
      - websecure
    middlewares:
      - name: dashboard-auth
        namespace: ingress-nginx
    tls:
      secretName: traefik-dashboard-tls 
 
# Creates a BasicAuth Middleware and Secret for the Dashboard Security
extraObjects:
  - apiVersion: v1
    kind: Secret
    metadata:
      name: dashboard-auth-secret
    type: kubernetes.io/basic-auth
    stringData:
      username: root
      password: "very-secret-password"      # Replace with an Actual Password
  - apiVersion: traefik.io/v1alpha1
    kind: Middleware
    metadata:
      name: dashboard-auth
    spec:
      basicAuth:
        secret: dashboard-auth-secret
  - apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: traefik-dashboard-tls
    spec:
      secretName: traefik-dashboard-tls
      dnsNames:
        - dashboard.my-domain.com
      issuerRef:
        name: letsencrypt-prod 
        kind: ClusterIssuer
 
providers:
  kubernetesIngress:
    enabled: false # Traefik doc explicitly says it can cause problems
  kubernetesIngressNGINX:
    # -- Enable Kubernetes Ingress NGINX provider
    enabled: true
    allowSnippetAnnotations: true
    ingressClassByName: true
    ingressClass: "nginx"
 
deployment:
  kind: DaemonSet
  initContainers:
    - name: set-unprivileged-port
      image: busybox
      command:
        - sh
        - -c
        - sysctl -w net.ipv4.ip_unprivileged_port_start=80
      securityContext:
        privileged: true
        allowPrivilegeEscalation: true
        runAsUser: 0
        runAsNonRoot: false
 
hostNetwork: true
 
# this allows access to the dashboard via kube port-forward
service:
  enabled: true
  spec:
    type: ClusterIP  # or disable entirely
 
 
# Needed for DaemonSet
updateStrategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 1
    maxSurge: 0

Migrate

  1. Uninstall ingress-nginx
  2. Install traefik with the values provided above values
helm repo add traefik https://traefik.github.io/charts
helm repo update
helm install traefik traefik/traefik -f values.yaml 
  1. You need to also manually create a nginx ingress class
# ingress-class.yaml 
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx
spec:
  controller: k8s.io/ingress-nginx
 

kubectl apply -f ingress-class.yaml

  1. At this point, everything should work as it was with the ingress-nginx, including cert-manager.