Skip to content
Published on

The Complete Traefik Guide — IngressRoute, Middleware, and Dynamic Configuration

Authors

Introduction

When you run a Kubernetes cluster, you inevitably need an ingress point that routes external traffic to internal services. The standard Ingress resource alone struggles to express requirements like path rewriting, authentication, and rate limiting, and because each controller uses its own annotation syntax, portability suffers.

Traefik is a cloud-native edge router that solves these limitations through the concepts of dynamic configuration and middleware. Written in Go as a single binary, it watches multiple providers at once (Kubernetes CRDs, the standard Ingress, Docker, Consul, and more) and reconfigures routing rules in real time. There is no need to reload a config file or restart the process.

In this article, we will work through Traefik's core concepts, then explore IngressRoute CRDs, middleware chaining, automatic TLS with Let's Encrypt, the dashboard, and Traefik's relationship to the Gateway API, which has become the standard in 2026. We close with Helm deployment and the pitfalls you are likely to encounter.

This article is for the following readers.

  • Those evaluating Traefik as an alternative to ingress-nginx on Kubernetes
  • Those who want to escape the annotation hell of standard Ingress
  • Those who want to manage automatic TLS and middleware chaining declaratively
  • Those weighing a migration path from Ingress to the Gateway API

Traefik Architecture at a Glance

Traefik's routing model consists of four core objects. Once you understand the flow from an incoming request to a backend pod, the rest of the configuration becomes far easier.

                   Internet / external clients
                            |
                            v
   +-------------------------------------------------+
   |                  EntryPoint                     |
   |       (listens on :80 web / :443 websecure)     |
   +-------------------------------------------------+
                            |
                            v
   +-------------------------------------------------+
   |                    Router                       |
   |  rule match: Host(`app.example.com`) && Path    |
   |  on match, hand off to the Middleware chain     |
   +-------------------------------------------------+
                            |
                            v
   +-------------------------------------------------+
   |            Middleware chain (in order)          |
   |   auth -> redirect -> ratelimit -> stripprefix  |
   +-------------------------------------------------+
                            |
                            v
   +-------------------------------------------------+
   |                   Service                       |
   |    load-balancing target (k8s Service / pods)   |
   +-------------------------------------------------+
                            |
                            v
                       backend Pod

EntryPoint

An EntryPoint is the network entry point where Traefik receives traffic. It is usually defined in the static configuration, specifying a port and protocol. By convention, port 80 is called web and port 443 is called websecure.

# traefik.yml (static configuration)
entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true
  websecure:
    address: ":443"
    http:
      tls: {}
  metrics:
    address: ":9100"

In the example above, every HTTP request arriving on web is permanently redirected to websecure (HTTPS). Applying a global redirect at the EntryPoint level is a very common pattern.

Router

A Router defines the rules used to match incoming requests. You can combine various matchers such as Host, Path, Header, and Method using logical operators.

# dynamic configuration example
http:
  routers:
    my-app:
      rule: "Host(`app.example.com`) && PathPrefix(`/api`)"
      entryPoints:
        - websecure
      middlewares:
        - strip-api-prefix
      service: my-app-service
      tls:
        certResolver: letsencrypt

The rule is expressed as a backtick-wrapped value. Priority is automatically computed based on rule length by default, but you can also set it explicitly with the priority field.

Middleware

Middleware is the stage that transforms requests or responses between the Router and the Service. Dozens of built-in middlewares are provided for authentication, header injection, path rewriting, rate limiting, compression, and more, and you can connect several of them into a chain. The order of the chain directly affects behavior, so designing the order carefully matters.

Service

A Service points to the actual backend and defines options such as the load-balancing strategy (weighted round robin), health checks, and sticky sessions. On Kubernetes it usually maps to a cluster Service resource.

Provider

A Provider is the source from which Traefik reads its configuration. The key point is that Traefik watches multiple providers simultaneously. In a Kubernetes environment you mainly use two.

ProviderDescriptionResources used
kubernetesCRDBased on Traefik-specific CRDsIngressRoute, Middleware, etc.
kubernetesIngressHandles the standard Ingress resourceIngress, IngressClass
kubernetesGatewayHandles the Gateway API standardGateway, HTTPRoute
# enable providers in the static configuration
providers:
  kubernetesCRD:
    allowCrossNamespace: false
  kubernetesIngress:
    ingressClass: traefik
  kubernetesGateway: {}

Ingress vs. IngressRoute CRD

This is the point that confuses Traefik newcomers the most. Both approaches route traffic, but they differ greatly in expressiveness and portability.

Limitations of Standard Ingress

The standard Ingress is an official Kubernetes resource, so it is highly portable, but the spec itself only standardizes host- and path-based routing. Every other advanced feature (rewriting, authentication, rate limiting, and so on) depends on controller-specific annotations.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    traefik.ingress.kubernetes.io/router.middlewares: default-auth@kubernetescrd
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
  ingressClassName: traefik
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-service
                port:
                  number: 80

As you can see, attaching middleware relies on annotation strings, and this syntax cannot be carried over to another controller as is. As of 2026 the standard Ingress API is frozen. In other words, no new features will be added, and the community's direction has shifted to the Gateway API.

IngressRoute CRD

IngressRoute is a CRD defined by Traefik that expresses routing rules, middleware, and TLS settings as explicit YAML fields instead of annotations.

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: my-app
  namespace: default
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`app.example.com`) && PathPrefix(`/api`)
      kind: Rule
      priority: 10
      middlewares:
        - name: api-auth
        - name: strip-api-prefix
      services:
        - name: my-app-service
          port: 80
  tls:
    certResolver: letsencrypt

The table below compares the two approaches.

AspectStandard IngressIngressRoute CRD
PortabilityHigh (k8s standard)Low (Traefik-only)
Middleware expressionAnnotation stringsNative fields
Matching rulesMostly host / pathRich: Host, Header, Method
TCP / UDP routingNot supportedIngressRouteTCP / UDP
Priority controlLimitedExplicit priority field
Future standardFrozenTied to Traefik

In short, if portability is the top priority, choose the standard Ingress or the Gateway API; if you want to use every Traefik feature, choose IngressRoute. That said, for a new project it is reasonable in 2026 to consider the Gateway API first, which we describe later.

Middleware Chaining in Practice

Middleware is Traefik's most powerful feature. You define each middleware as an independent CRD and reference them in order from an IngressRoute to build a chain. Let's look at four commonly used ones.

1. Basic Authentication (BasicAuth)

First create a Secret holding the credentials, then reference it from the middleware.

apiVersion: v1
kind: Secret
metadata:
  name: dashboard-auth-secret
  namespace: default
type: Opaque
stringData:
  users: |
    admin:$apr1$abcd1234$encryptedpasswordhash
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: api-auth
  namespace: default
spec:
  basicAuth:
    secret: dashboard-auth-secret
    removeHeader: true

The users value must be in the hash format generated by htpasswd. Setting removeHeader to true prevents the Authorization header from being forwarded to the backend after authentication.

2. Redirect (RedirectScheme / RedirectRegex)

You can force HTTP to HTTPS or send a specific path pattern elsewhere.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: https-redirect
  namespace: default
spec:
  redirectScheme:
    scheme: https
    permanent: true
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: legacy-redirect
  namespace: default
spec:
  redirectRegex:
    regex: "^https://old.example.com/(.*)"
    replacement: "https://new.example.com/${1}"
    permanent: true

3. Rate Limit

Limit the number of requests per second or minute to protect the backend. average is the allowed average rate and burst is the instantaneous allowance.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: api-ratelimit
  namespace: default
spec:
  rateLimit:
    average: 100
    burst: 50
    period: 1s
    sourceCriterion:
      ipStrategy:
        depth: 1

sourceCriterion sets the basis for identifying the client. If you sit behind a proxy, configure ipStrategy.depth so the real client IP is extracted from the X-Forwarded-For header.

4. StripPrefix

When the backend expects the root path (/) but external clients access it under a specific prefix, strip the leading part of the path.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: strip-api-prefix
  namespace: default
spec:
  stripPrefix:
    prefixes:
      - /api
    forceSlash: false

When this middleware is applied, an external /api/users request is delivered to the backend as /users.

Building the Chain and Ordering

When you list multiple middlewares as an array in an IngressRoute, they are applied in that order. Getting the order wrong can produce unexpected behavior, so be careful.

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: secure-api
  namespace: default
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`api.example.com`)
      kind: Rule
      middlewares:
        - name: https-redirect
        - name: api-auth
        - name: api-ratelimit
        - name: strip-api-prefix
      services:
        - name: api-service
          port: 8080
  tls:
    certResolver: letsencrypt

The chain above operates in the following order.

request
  -> https-redirect   (redirect to HTTPS if HTTP)
  -> api-auth         (verify authentication)
  -> api-ratelimit    (throttle request rate)
  -> strip-api-prefix (remove path prefix)
  -> api-service      (forward to backend)

As a principle, place cheap middlewares that are likely to reject (redirect, authentication, rate limit) toward the front so unnecessary processing is cut off early. Put transformations such as path rewriting toward the back.

Automatic TLS — Let's Encrypt ACME

One of Traefik's appealing features is automatic certificate issuance and renewal via Let's Encrypt. It embeds the ACME protocol, so it works without a separate cert-manager.

ACME Challenge Types

ACME supports three challenges that prove domain ownership.

ChallengeHow it worksWildcard certs
HTTP-01Responds with a validation token on port 80Not supported
TLS-ALPN-01TLS handshake on port 443Not supported
DNS-01Validation via a DNS TXT recordSupported
# static configuration — HTTP-01 challenge
certificatesResolvers:
  letsencrypt:
    acme:
      email: ops@example.com
      storage: /data/acme.json
      httpChallenge:
        entryPoint: web

If you need a wildcard certificate, you must use the DNS-01 challenge. Inject the API token of your DNS provider (for example, Cloudflare or Route53) as an environment variable.

certificatesResolvers:
  letsencrypt-dns:
    acme:
      email: ops@example.com
      storage: /data/acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"

Because the acme.json file stores issued certificates and private keys, restrict its permissions to 600 and store it on a persistent volume (PVC). When the pod scales to several replicas, concurrent writes to the same file can collide, so in multi-replica environments it is safer to use cert-manager separately or to limit certificate issuance to a single instance.

Dashboard

Traefik currently provides a web dashboard that visually shows the state of routers, services, and middlewares. It is handy for quickly checking the live routing configuration.

# enable the dashboard and API in the static configuration
api:
  dashboard: true
  insecure: false

Setting insecure to true exposes it without authentication, so you must never use that in production. Instead, expose the dashboard via an IngressRoute and protect it with a BasicAuth middleware.

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-dashboard
  namespace: traefik
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`traefik.example.com`)
      kind: Rule
      middlewares:
        - name: dashboard-auth
      services:
        - name: api@internal
          kind: TraefikService
  tls:
    certResolver: letsencrypt

Note that the service points to an internal TraefikService called api@internal. This is a special reference to Traefik's internal API handler.

Gateway API Support

As of 2026 the most important trend is the Gateway API. As mentioned, the standard Ingress is frozen, and the next-generation standard for Kubernetes networking has been settled as the Gateway API. The Gateway API provides role separation (infrastructure operators own the Gateway, app developers own the HTTPRoute) and rich routing expressiveness through standard CRDs.

Traefik is a conformant Gateway API implementation and processes the standard resources through the kubernetesGateway provider. In other words, you can obtain IngressRoute-level expressiveness as a standard without vendor lock-in.

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: traefik
spec:
  controllerName: traefik.io/gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: traefik-gateway
  namespace: traefik
spec:
  gatewayClassName: traefik
  listeners:
    - name: web
      protocol: HTTP
      port: 80
    - name: websecure
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        certificateRefs:
          - name: example-tls
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app-route
  namespace: default
spec:
  parentRefs:
    - name: traefik-gateway
      namespace: traefik
  hostnames:
    - "app.example.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: my-app-service
          port: 80

The table below summarizes where the three approaches stand.

ApproachStandard?ExpressivenessRecommended for
Standard Ingressk8s standard (frozen)LowSimple routing, legacy
IngressRouteTraefik-onlyHighWhen you need Traefik features
Gateway APIk8s standard (evolving)HighRecommended for new projects

For now the Gateway API's middleware expressiveness may not be as rich as IngressRoute, so a transitional setup that mixes both approaches is common. In the long run, converging on the Gateway API is recommended.

Deployment in Practice — Helm values

In production it is common to deploy Traefik with the official Helm chart. The following is a values example frequently used in the field.

# add the repository and install
helm repo add traefik https://traefik.github.io/charts
helm repo update
helm install traefik traefik/traefik \
  --namespace traefik --create-namespace \
  --values values.yaml
# values.yaml
deployment:
  replicas: 2

ingressClass:
  enabled: true
  isDefaultClass: true

providers:
  kubernetesCRD:
    enabled: true
    allowCrossNamespace: false
  kubernetesIngress:
    enabled: true
  kubernetesGateway:
    enabled: true

ports:
  web:
    redirectTo:
      port: websecure
  websecure:
    tls:
      enabled: true

certificatesResolvers:
  letsencrypt:
    acme:
      email: ops@example.com
      storage: /data/acme.json
      httpChallenge:
        entryPoint: web

persistence:
  enabled: true
  size: 128Mi
  path: /data

dashboard:
  enabled: true

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi

logs:
  general:
    level: INFO
  access:
    enabled: true

After installation, check the state with the following commands.

kubectl get pods -n traefik
kubectl get svc -n traefik
kubectl logs -n traefik deploy/traefik --tail=100

Operations and Tuning

Access Logs and Metrics

In production, write access logs in JSON format and enable Prometheus metrics to secure observability.

metrics:
  prometheus:
    entryPoint: metrics
    addEntryPointsLabels: true
    addServicesLabels: true

accessLog:
  format: json
  filters:
    statusCodes:
      - "400-499"
      - "500-599"

Using the statusCodes filter logs only 4xx and 5xx responses, reducing log volume.

Health Checks and Probes

Traefik's own readiness and liveness probes are configured through the ping EntryPoint.

ping:
  entryPoint: web

Timeout Tuning

If you handle long-lived connections or large uploads, adjust the transport timeouts of the EntryPoint.

entryPoints:
  websecure:
    address: ":443"
    transport:
      respondingTimeouts:
        readTimeout: 60s
        writeTimeout: 60s
        idleTimeout: 180s

Pitfalls and Troubleshooting

Here are the problems you will frequently run into in operations.

1. 404 or the Router Does Not Match

The most common cause is a missing EntryPoint specification. If you do not specify entryPoints on an IngressRoute, it may be exposed on every EntryPoint, or traffic may not arrive on the intended port. First check in the dashboard whether the router is registered and whether the rule is correct.

# query router state via the API
kubectl port-forward -n traefik deploy/traefik 8080:8080
curl http://localhost:8080/api/http/routers

2. Middleware Not Found

To reference a middleware in another namespace, you must specify the namespace. In the annotation-based approach for an IngressRoute, you use the form name-namespace@kubernetescrd. Also, if allowCrossNamespace is false, cross-namespace references are blocked.

3. Certificate Is Not Issued

The HTTP-01 challenge requires port 80 to be reachable from the outside. Check your firewall or load balancer settings. If the acme.json file permissions are not 600, Traefik will reject it. Also, sharing the same acme.json across multiple replicas can tangle issuance.

4. Backend Breaks After StripPrefix

If the backend application references static resources by absolute path, links can break after the prefix is stripped. In that case configure a base path in the application, or handle the path more precisely with the replacePathRegex middleware.

5. The Real Client IP Is Not Visible

When you sit behind a cloud load balancer, only the LB's IP may appear in rate limits or logs. Configure forwardedHeaders.trustedIPs and adjust the rate limit middleware's ipStrategy.depth to match your environment.

entryPoints:
  websecure:
    address: ":443"
    forwardedHeaders:
      trustedIPs:
        - "10.0.0.0/8"
        - "172.16.0.0/12"

Closing

Traefik is a powerful tool that bundles dynamic configuration, middleware, and automatic TLS so you can operate Kubernetes ingress declaratively. The IngressRoute CRD frees you from the annotation hell of standard Ingress, but it comes at the cost of vendor lock-in.

As of 2026 the standard Ingress is frozen and the Gateway API has settled in as the next-generation standard. Because Traefik officially supports the Gateway API, for a new project it is realistic to consider the Gateway API first while still using IngressRoute where you need Traefik's middleware ecosystem. If you understand the core concepts accurately (EntryPoint, Router, Middleware, Service, Provider), you can operate confidently no matter which approach you choose.

References