Skip to main content
Updated Feb 23, 2026

Traefik Ingress Controller

The previous lesson explained why Ingress has limitations—annotation chaos, vendor lock-in, no role separation. Gateway API solves these problems, but it introduces new abstractions: GatewayClass, Gateway, HTTPRoute, BackendTrafficPolicy. That's a lot to absorb before routing your first request.

What if you just want external traffic reaching your Task API in 5 minutes?

Traefik offers exactly that. It's a simpler ingress controller with its own CRDs (IngressRoute, Middleware) that are more intuitive than Ingress annotations. You'll understand traffic routing fundamentals faster with Traefik, which prepares you for Gateway API's more powerful—but more complex—model.

This lesson gets your Task API accessible from outside the cluster with rate limiting protection. Then you'll understand exactly what Gateway API improves upon.


Installing Traefik with Helm

Traefik publishes a Helm chart that handles all the complexity—Deployment, Service, ServiceAccount, RBAC, CRDs. You just run two commands.

Add the Traefik Helm repository:

helm repo add traefik https://traefik.github.io/charts

Output:

"traefik" has been added to your repositories

Update repository information:

helm repo update

Output:

Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "traefik" chart repository
Update Complete. ⎈Happy Helming!⎈

Install Traefik in its own namespace:

helm install traefik traefik/traefik -n traefik --create-namespace

Output:

NAME: traefik
LAST DEPLOYED: Mon Dec 30 10:15:00 2024
NAMESPACE: traefik
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Traefik Proxy v3.2.0 has been deployed successfully on traefik namespace!

Verify the installation:

kubectl get pods -n traefik

Output:

NAME                       READY   STATUS    RESTARTS   AGE
traefik-7d9c8f6c5b-xk2m9 1/1 Running 0 30s

Check the Service created by Traefik:

kubectl get svc -n traefik

Output:

NAME      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
traefik LoadBalancer 10.96.45.123 localhost 80:31234/TCP,443:32456/TCP 45s

On Docker Desktop, localhost appears as the external IP. On a cloud provider, you'd see a real public IP. Either way, Traefik now accepts traffic on ports 80 (HTTP) and 443 (HTTPS).


Understanding Traefik Architecture

Before creating routes, understand how Traefik processes requests:

┌─────────────────────────────────────────────────────────────────────┐
│ Traefik Architecture │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Internet Request │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ EntryPoints │ ← Ports Traefik listens on (80, 443) │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Routers │ ← Match rules (Host, Path, Headers) │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Middlewares │ ← Transformations (rate limit, auth, headers) │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Services │ ← Backend targets (Kubernetes Services) │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

EntryPoints: Ports where Traefik accepts connections. The Helm chart creates web (port 80) and websecure (port 443) by default.

Routers: Rules that match incoming requests. When a request matches a Router's rule (e.g., Host('api.example.com') && PathPrefix('/tasks')), Traefik sends it to the associated Service.

Middlewares: Transformations applied before reaching the backend. Rate limiting, authentication, header manipulation, path stripping—all are Middlewares.

Services: Backend targets. In Kubernetes, these point to Kubernetes Services that forward to Pods.

This is similar to Gateway API's model (Gateway, HTTPRoute, BackendTrafficPolicy) but with different terminology and simpler CRDs.


Creating Your First IngressRoute

Traefik's IngressRoute CRD replaces Kubernetes' Ingress resource with a cleaner syntax. No annotations required.

First, ensure your Task API is running. If you've been following along, you have it deployed:

kubectl get svc task-api -n task-api

Output:

NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
task-api ClusterIP 10.96.78.234 <none> 8000/TCP 2h

Now create task-api-ingressroute.yaml that routes /api/v1/tasks to the Task API:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: task-api-route
namespace: task-api
spec:
entryPoints:
- web # Listen on port 80
routes:
- match: PathPrefix(`/api/v1/tasks`)
kind: Rule
services:
- name: task-api
port: 8000

Output:

(This is the manifest—we'll apply it next)

Apply the IngressRoute:

kubectl apply -f task-api-ingressroute.yaml

Output:

ingressroute.traefik.io/task-api-route created

Test the route:

curl http://localhost/api/v1/tasks

Output:

{"tasks": []}

Your Task API is now accessible from outside the cluster through Traefik. The request flow:

  1. Request arrives at localhost:80 (Traefik's web EntryPoint)
  2. Traefik matches PathPrefix('/api/v1/tasks')task-api-route
  3. Traefik forwards to task-api:8000 in the task-api namespace
  4. Task API responds, Traefik returns the response

Compare this to the Ingress resource from Lesson 1. No annotations needed. The routing logic is in structured YAML fields, not string annotations.


Understanding the Match Rule Syntax

Traefik's match rules use a simple expression language:

ExpressionMeaning
Host('api.example.com')Match requests with this Host header
PathPrefix('/api')Match paths starting with /api
Path('/health')Match exactly /health
Headers('X-Version', '2')Match requests with this header value
Method('POST')Match only POST requests
&&AND - both conditions must match
`

Examples:

Route only POST requests to /tasks:

routes:
- match: PathPrefix(`/api/v1/tasks`) && Method(`POST`)
kind: Rule
services:
- name: task-api
port: 8000

Route based on API version header:

routes:
- match: PathPrefix(`/api`) && Headers(`X-API-Version`, `v2`)
kind: Rule
services:
- name: task-api-v2
port: 8000

This expressive matching is something standard Ingress cannot do without controller-specific annotations.


Adding Rate Limiting with Middleware

Your AI agent's Task API could be expensive to run. Without rate limiting, a single user could exhaust your compute budget. Traefik Middlewares handle this.

Create ratelimit-middleware.yaml:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rate-limit
namespace: task-api
spec:
rateLimit:
average: 10 # 10 requests per second average
burst: 20 # Allow bursts up to 20 requests
period: 1s # Time window

Output:

(This is the manifest—we'll apply it next)

Apply the Middleware:

kubectl apply -f ratelimit-middleware.yaml

Output:

middleware.traefik.io/rate-limit created

Now attach the Middleware to your IngressRoute. Create task-api-ingressroute-ratelimited.yaml:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: task-api-route
namespace: task-api
spec:
entryPoints:
- web
routes:
- match: PathPrefix(`/api/v1/tasks`)
kind: Rule
middlewares:
- name: rate-limit # Attach the Middleware
services:
- name: task-api
port: 8000

Apply the updated IngressRoute:

kubectl apply -f task-api-ingressroute-ratelimited.yaml

Output:

ingressroute.traefik.io/task-api-route configured

Test the rate limit by sending many requests quickly:

for i in {1..25}; do
curl -s -o /dev/null -w "%{http_code}\n" http://localhost/api/v1/tasks
done

Output:

200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
200
429
429
429
429
429

After approximately 20 requests (the burst limit), Traefik returns 429 Too Many Requests. The rate limiter is protecting your backend.


Path Manipulation with Middlewares

Sometimes your backend expects a different path than what external clients use. Middlewares can transform paths.

StripPrefix Middleware

Remove a path prefix before forwarding. Create strip-prefix-middleware.yaml:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: strip-api-prefix
namespace: task-api
spec:
stripPrefix:
prefixes:
- /api/v1

Output:

(Manifest only)

If a client requests /api/v1/tasks, Traefik forwards /tasks to the backend. Useful when your internal services don't expect version prefixes.

AddPrefix Middleware

Add a path prefix before forwarding. Create add-prefix-middleware.yaml:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: add-internal-prefix
namespace: task-api
spec:
addPrefix:
prefix: /internal

Output:

(Manifest only)

A request to /tasks becomes /internal/tasks when reaching the backend.

Headers Middleware

Add or modify HTTP headers. Create headers-middleware.yaml:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: custom-headers
namespace: task-api
spec:
headers:
customRequestHeaders:
X-Forwarded-By: "traefik"
X-Real-IP: "" # Empty string removes the header
customResponseHeaders:
X-Served-By: "task-api-cluster"

Output:

(Manifest only)

Chaining Multiple Middlewares

Apply multiple Middlewares in sequence:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: task-api-route
namespace: task-api
spec:
entryPoints:
- web
routes:
- match: PathPrefix(`/api/v1/tasks`)
kind: Rule
middlewares:
- name: rate-limit # Applied first
- name: strip-api-prefix # Applied second
- name: custom-headers # Applied third
services:
- name: task-api
port: 8000

Middlewares execute in order. Rate limiting happens before path stripping, which happens before header modification.


Enabling the Traefik Dashboard

Traefik includes a web dashboard for monitoring routes, services, and middlewares. Enable it by upgrading the Helm release:

helm upgrade traefik traefik/traefik -n traefik \
--set dashboard.enabled=true \
--set ingressRoute.dashboard.enabled=true

Output:

Release "traefik" has been upgraded. Happy Helming!
NAME: traefik
LAST DEPLOYED: Mon Dec 30 10:45:00 2024
NAMESPACE: traefik
STATUS: deployed
REVISION: 2

Access the dashboard via port-forward:

kubectl port-forward -n traefik svc/traefik 9000:9000

Output:

Forwarding from 127.0.0.1:9000 -> 9000
Forwarding from [::1]:9000 -> 9000

Open http://localhost:9000/dashboard/ in your browser. You'll see:

  • HTTP Routers: Your IngressRoutes and their status
  • HTTP Services: Backend services Traefik knows about
  • HTTP Middlewares: All Middleware resources
  • EntryPoints: Ports Traefik is listening on

The dashboard updates in real-time. Create a new IngressRoute, and it appears within seconds. This visibility is valuable for debugging routing issues.


When to Use Traefik vs Gateway API

Both Traefik and Gateway API solve the same problem—routing external traffic to internal services. The choice depends on your situation.

Use Traefik When:

ScenarioWhy Traefik
Quick development/staging setupInstall → route in 5 minutes
Team already knows TraefikExisting expertise reduces learning curve
Don't need cross-vendor portabilityTraefik CRDs are Traefik-only
Simpler configuration preferredIngressRoute is more intuitive than Gateway API
Need dashboard out of the boxBuilt-in visualization

Use Gateway API When:

ScenarioWhy Gateway API
Production with potential vendor changesGatewayClass swap without route rewrites
Need role separation (platform vs app team)Gateway owned by platform, HTTPRoute by apps
Want CNCF-standard portabilityIndustry standard, multiple implementations
Need advanced traffic splitting, mirroringNative support vs Traefik-specific CRDs
Enterprise compliance requirementsStandard API easier to audit

Decision Table

QuestionTraefikGateway API
Time to first route?Minutes30+ minutes (install controller + CRDs)
Vendor lock-in?Yes (Traefik CRDs)No (standard API)
Role separation?LimitedBuilt-in (GatewayClass/Gateway/HTTPRoute)
Learning curve?LowerHigher
Advanced features?Via MiddlewaresVia BackendTrafficPolicy
Multi-cluster?PossibleBetter supported

For this book's learning path: We teach Traefik first because it's faster to understand. Once you've internalized traffic routing concepts with Traefik, Gateway API's abstractions will make more sense. Lessons 3-11 will then teach Gateway API as your production solution.


Exercises

Exercise 1: Install Traefik and Access Dashboard

If you haven't already:

  1. Install Traefik with Helm
  2. Enable the dashboard
  3. Access the dashboard at localhost:9000/dashboard/

Verify you can see EntryPoints, Routers, Services, and Middlewares sections.

Exercise 2: Create IngressRoute for Task API

Create an IngressRoute that:

  • Listens on EntryPoint web
  • Matches path /api/v1/tasks
  • Routes to the task-api Service on port 8000

Test with curl http://localhost/api/v1/tasks.

Exercise 3: Add Rate Limiting Middleware

  1. Create a Middleware with:
    • average: 5 requests per second
    • burst: 10 requests
  2. Attach it to your IngressRoute
  3. Test by sending 15 requests quickly

Verify you see 429 responses after the burst limit.

Exercise 4: Add StripPrefix Middleware

  1. Create a StripPrefix Middleware that removes /api/v1
  2. Chain it with your rate limit Middleware
  3. Verify the order matters: rate limiting should happen first

Check the Traefik dashboard to see both Middlewares attached to your route.


Reflect on Your Skill

You built a traffic-engineer skill in Lesson 0. Test and improve it based on what you learned.

Update Your Skill's Decision Logic

Your skill should now include Traefik as an option:

When should your skill recommend Traefik?

  • Development/staging environments needing quick setup
  • Teams already using Traefik
  • Simpler configuration requirements
  • Need built-in dashboard for monitoring

When should your skill recommend Gateway API?

  • Production environments requiring vendor portability
  • Multi-team setups needing role separation
  • Advanced traffic patterns (splitting, mirroring)
  • CNCF compliance requirements

What Traefik Patterns Are Worth Encoding?

Consider adding these to your skill:

  1. Standard IngressRoute template with EntryPoints and match rules
  2. Rate limiting Middleware with configurable average/burst
  3. Middleware chaining pattern (rate limit → strip prefix → headers)
  4. Dashboard enablement for development clusters

Your skill should generate the appropriate CRDs based on whether Traefik or Gateway API is chosen.