Skip to content
application · reverse-proxy · network

Routes that find themselves,Traefik.

A cloud-native reverse proxy that discovers services dynamically from Docker labels, Kubernetes CRDs, or file config — and provisions Let's Encrypt TLS automatically. This guide moves from local to production: Docker Compose, k3s patching, BasicAuth middleware, and Cloudflare DNS-01 challenges for ACME certificates.

No reloads, no config drift

Traefik watches a provider — the Docker socket, Kubernetes CRDs, or a config file — and rebuilds its routers as containers start and stop. Certificates renew themselves through ACME, so routing stays hands-off.

  • Discovery — routers built from labels and CRDs.
  • Middleware — BasicAuth, rate limits, rewrites.
  • TLS — automatic ACME via Cloudflare DNS-01.
Let's EncryptTLS issuer
DNS-01ACME challenge
Docker · k8sProviders
:8080Dashboard port

Start here

Overview

Traefik is a cloud-native reverse proxy and load balancer that makes deploying, configuring, and integrating infrastructure components easy and automatic. Unlike a static proxy, Traefik discovers services dynamically from providers — Docker labels, Kubernetes CRDs, or file config — and reconfigures its routes as containers start and stop. It also provisions Let’s Encrypt TLS certificates automatically.

This guide moves from local to production: run Traefik with Docker Compose, patch and install it on a k3s Kubernetes cluster, attach BasicAuth middleware, and automate Cloudflare DNS challenges for ACME certificates.

  1. Docker — Stand up Traefik locally with Compose and an acme.json for certificates.

  2. Kubernetes — Install the CRDs and RBAC, then patch the service and add Helm.

  3. Middleware — Isolate concerns like auth into Middleware resources referenced by Ingress.

  4. Cloudflare — Wire scoped API tokens so Traefik solves the ACME DNS-01 challenge automatically.

Local first

Docker

Docker Compose - There should be an acme.json file that you create and pass through the docker with the permission of chmod 600. - Furthermore, there are two more files that you will have to configure and pass through before launching the traefik container. We provided them in the #config section below.

A complete docker-compose.yml that runs Traefik, mounts the Docker socket for auto-discovery, and routes to a whoami test service:

services:
traefik:
image: traefik:v3.1
command:
- "--providers.docker=true"
- "--providers.docker.exposedByDefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--api.dashboard=true"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./acme.json:/acme.json"
restart: unless-stopped
whoami:
image: traefik/whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.entrypoints=web"

Traefik reads the traefik.* labels on each container and builds routers automatically — no separate config file needed for basic routing. Mounting the Docker socket read-only (:ro) lets Traefik watch for containers starting and stopping.

Cluster ingress

Kubernetes

  • Patching Traefik on k3s cluster

    • We want to find the instance of where traefik is running. Running sudo kubectl get all -o wide --all-namespaces should display all your containers, look for traefik.

    • Patch

      • Terminal window
        sudo kubectl patch svc traefik -n kube-system -p '{"spec":{"externalTrafficPolicy":"Cluster"}}'
    • std output should be service/traefik patched

  • Helm Charts

    • Terminal window
      helm repo add traefik https://helm.traefik.io/traefik
      • Sucess: std output should be

        • Terminal window
          "traefik" has been added to your repositories
    • Terminal window
      helm repo update
  • Traefik Middleware for Kubernetes

    • Middleware kind should be isolated for performance and security reasons.

      • Auth - Kind: Middleware

        • Example:

          • apiVersion: traefik.containo.us/v1alpha1
            kind: Middleware
            metadata:
            name: longhorn-auth
            namespace: longhorn-system
            spec:
            basicAuth:
            secret: authsecret
            • The middleware should be saved as a yaml / yml file and applied using kubectl.
      • Auth - Kind: Ingress

        • Calling the longhorn-auth in the Ingress via annotations:

          • Example:

            • apiVersion: networking.k8s.io/v1
              kind: Ingress
              metadata:
              name: longhorn-ing-traefik
              namespace: longhorn-system
              annotations:
              externalTrafficPolicy: Local
              kubernetes.io/ingress.class: traefik
              traefik.ingress.kubernetes.io/router.middlewares: longhorn-system-longhorn-auth@kubernetescrd
              ingress.kubernetes.io/whitelist-x-forwarded-for: 'true'
              spec:
              rules:
              - host: 'x.kbve.com'
              http:
              paths:
              - path: /
              pathType: Prefix
              backend:
              service:
              name: longhorn-service-provider
              port:
              number: 8000
            • In our PoC above, we see that the middleware is referenced as:

              traefik.ingress.kubernetes.io/router.middlewares: longhorn-system-longhorn-auth@kubernetescrd

              Its important to note the namespace of the middleware, longhorn-system , before calling the middleware’s name. This is to let the crd know where the middleware is located.

Static & dynamic

Config

Traefik reads two kinds of configuration: static (traefik.yml, entrypoints and providers, loaded once at startup) and dynamic (routers, services, middleware, hot-reloaded at runtime). For Docker deployments, create the ACME store first so Traefik can persist issued certificates:

Terminal window
touch acme.json
chmod 600 acme.json

A minimal static traefik.yml enabling the Docker provider and a Let’s Encrypt resolver:

entryPoints:
websecure:
address: ":443"
providers:
docker:
exposedByDefault: false
certificatesResolvers:
letsencrypt:
acme:
storage: acme.json
dnsChallenge:
provider: cloudflare

Mount acme.json and traefik.yml into the container and pass the Cloudflare token (see Cloudflare below) so the dnsChallenge resolver can complete.

CRDs & IngressRoute

Notes

According to the notes on Traefik & Kubernetes we first need to install the Resource Definitions and RBAC into kubectl by running the following commands:

Terminal window
# Install Traefik Resource Definitions:
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v2.8/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
# Install RBAC for Traefik:
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v2.8/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml

After this installation, we’ll have a set of Custom Resource Definitions which should have the following benefits:

  • The usage of name and namespace to refer to another Kubernetes resource.
  • The usage of secret for sensitive data (TLS certificates and credentials).
  • The structure of the configuration.
  • The requirement to declare all the definitions.

See the list of CRDs in the dedicated routing section.

The biggest thing we need from this is the ability to add the BasicAuth plugin. This plugin (which is what we tried to reference before with the auth@file line) uses an htpasswd password to block incoming traffic to the pod.

This will require setting up an IngressRoute (which is a specific Kubernetes resource added by the Traefik Resource Definitions) with settings to specify what the middlewares are. Find more info on the Traefik Middlewares Here

IngressRoute is Traefik’s native alternative to the standard Kubernetes Ingress. It exposes Traefik-specific features — middleware chains, TLS resolvers, and match rules — directly, without annotation soup. This route attaches the longhorn-auth middleware from the previous example:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: longhorn-route
namespace: longhorn-system
spec:
entryPoints:
- websecure
routes:
- match: Host(`x.kbve.com`)
kind: Rule
middlewares:
- name: longhorn-auth
services:
- name: longhorn-service-provider
port: 8000
tls:
certResolver: letsencrypt

The match rule uses Traefik’s expression syntax — combine with &&, ||, and PathPrefix(...) for fine-grained routing. certResolver: letsencrypt triggers automatic ACME certificate issuance for the host.

See every route

Dashboard

Traefik ships a web dashboard that visualizes every router, service, and middleware it has discovered — invaluable for debugging why a route isn’t matching. Enable it with --api.dashboard=true (see the Compose example above), then secure it behind BasicAuth:

labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.kbve.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$..."

Automated TLS

Cloudflare

These are notes on integrating Cloudflare with Traefik, including automating some of the actions so that you may not have to repeat them.

Official Docs

Access the API Tokens directly from Cloudflare Profile

Common environmental variable names and their purpose:

  • CF_API_EMAIL - The Cloudflare account holder’s email.
  • CF_API_KEY - The Cloudflare API key.
  • CF_DNS_API_TOKEN - The API token with DNS:Edit permission.
  • CF_ZONE_API_TOKEN - The API token with Zone:Read permission.

Questions

Frequently asked

What is Traefik?

Traefik is a cloud-native reverse proxy and load balancer that automatically discovers services from providers like Docker and Kubernetes, and provisions Let's Encrypt TLS certificates without manual configuration.

How is Traefik different from Nginx?

Traefik discovers routes dynamically from labels and CRDs, so it reconfigures itself as containers start and stop. Nginx uses static config files that require a reload. Traefik also ships automatic ACME TLS, whereas Nginx needs an external tool like certbot.

What is a Traefik middleware?

Middleware modifies requests or responses as they pass through a router — for example BasicAuth, rate limiting, or header rewrites. On Kubernetes it is a Middleware CRD referenced from an Ingress or IngressRoute by namespace-qualified name.

How does Traefik get TLS certificates from Cloudflare?

Traefik uses the ACME DNS-01 challenge with Cloudflare's API. You supply a scoped API token (DNS:Edit and Zone:Read) via environment variables, and Traefik creates the required DNS TXT record automatically to prove domain ownership.

What is the difference between Ingress and IngressRoute in Traefik?

Ingress is the standard Kubernetes resource that Traefik supports via annotations. IngressRoute is Traefik's own CRD that exposes middleware chains, TLS resolvers, and expressive match rules directly, without relying on annotations.

How do I secure the Traefik dashboard?

Enable it with --api.dashboard=true, then attach a BasicAuth middleware referencing api@internal and never expose it publicly without authentication. In Docker Compose, escape dollar signs in the htpasswd hash as double dollar signs.

Why does my Traefik htpasswd password break in Docker Compose?

Compose treats a single dollar sign as variable substitution. Escape every dollar sign in the BasicAuth hash as a double dollar sign so the literal htpasswd value is passed through to Traefik.

How does Traefik discover services automatically?

Traefik watches a provider — the Docker socket, Kubernetes CRDs, or a config file — and builds routers from labels or resources as containers start and stop. This is why it needs no manual reload when your services change.