Modern web applications require robust routing and load balancing to handle traffic efficiently. Traefik, a modern reverse proxy and load balancer, has become increasingly popular for its dynamic configuration and cloud-native design. In this comprehensive guide, we’ll build a complete microservices demo using Traefik as an ingress controller on Kubernetes, demonstrating how to route traffic to different services based on URL paths.

By the end of this tutorial, you’ll have a fully functional demo application with multiple microservices, all orchestrated through Traefik routing. This hands-on approach will give you practical experience with Kubernetes ingress controllers and microservices architecture.

What We’ll Build

Our demo application will include:

  • Frontend Service: A web interface displaying the demo application’s homepage
  • API Service: RESTful endpoints for book data management
  • User Service: Authentication and user management endpoints
  • Traefik: Acting as the ingress controller to route traffic

The architecture will demonstrate path-based routing where:

  • / routes to the frontend service
  • /api/* routes to the API service
  • /users/* routes to the user service
  • /dashboard/ provides access to Traefik’s management interface

Prerequisites

Before we begin, ensure you have the following installed:

  • Docker: For containerization
  • kind (Kubernetes in Docker): For local Kubernetes cluster
  • kubectl: Kubernetes command-line tool
  • Basic understanding of Kubernetes concepts: Pods, Services, Deployments

If you haven’t installed these tools, you can find installation guides on their respective official websites.

Project Structure

Our project follows a clean, organized structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Traefik-demo/
├── README.md
├── kind-config.yaml
├── base/
│ ├── traefik/
│ │ ├── 01-namespaces.yaml
│ │ ├── 02-rbac.yaml
│ │ ├── 03-deployment-service.yaml
│ │ └── 04-dashboard-route.yaml
│ └── bookstore/
│ ├── 01-namespace.yaml
│ ├── 02-services.yaml
│ ├── 03-middlewares.yaml
│ └── 04-ingress-routes.yaml

This structure separates Traefik configuration from application services, making it easy to manage and scale.

Creating the KinD Cluster

First, let’s create a local Kubernetes cluster using KinD (Kubernetes in Docker). We’ll use a custom configuration that exposes the necessary ports for our services.

Create a kind-config.yaml file with the following configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Kind cluster configuration for Traefik
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
# Map port 80 for web traffic
- containerPort: 80
hostPort: 80
protocol: TCP
listenAddress: "0.0.0.0"
# Map port 8080 for Traefik dashboard
- containerPort: 8080
hostPort: 8080
protocol: TCP
listenAddress: "0.0.0.0"
# Map port 443 for HTTPS traffic
- containerPort: 443
hostPort: 443
protocol: TCP
listenAddress: "0.0.0.0"
- role: worker
- role: worker

This configuration creates a multi-node cluster with:

  • One control-plane node with ingress-ready label
  • Two worker nodes for workload distribution
  • Port mappings for HTTP (80), HTTPS (443), and Traefik dashboard (8080)

Create the cluster:

1
kind create cluster --name traefik-demo --config kind-config.yaml

Installing Traefik CRDs

Traefik uses Custom Resource Definitions (CRDs) to extend Kubernetes with Traefik-specific resources like IngressRoute and Middleware. Install the required CRDs:

1
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.0/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml

These CRDs enable us to use advanced Traefik features like path stripping and custom routing rules.

Setting Up Traefik

Namespace and RBAC Configuration

Create the Traefik namespace:

1
2
3
4
5
6
apiVersion: v1
kind: Namespace
metadata:
name: traefik-system
labels:
name: traefik-system

Set up the service account and permissions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
apiVersion: v1
kind: ServiceAccount
metadata:
name: traefik
namespace: traefik-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: traefik
rules:
- apiGroups: [""]
resources: ["services", "endpoints", "secrets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses", "ingressclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses/status"]
verbs: ["update"]
- apiGroups: ["traefik.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: traefik
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik
subjects:
- kind: ServiceAccount
name: traefik
namespace: traefik-system

Traefik Deployment

Deploy Traefik with the following configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
apiVersion: apps/v1
kind: Deployment
metadata:
name: traefik
namespace: traefik-system
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik
containers:
- name: traefik
image: traefik:v3.0
args:
- --api.dashboard=true
- --api.insecure=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --providers.kubernetescrd=true
- --providers.kubernetescrd.allowcrossnamespace=true
- --providers.kubernetesingress=true
- --log.level=INFO
- --accesslog=true
- --metrics.prometheus=true
- --ping=true
ports:
- name: web
containerPort: 80
hostPort: 80
- name: websecure
containerPort: 443
hostPort: 443
- name: dashboard
containerPort: 8080
hostPort: 8080
livenessProbe:
httpGet:
path: /ping
port: 8080
readinessProbe:
httpGet:
path: /ping
port: 8080
nodeSelector:
ingress-ready: "true"
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Equal"
effect: "NoSchedule"

Key configuration points:

  • API Dashboard: Enabled with insecure mode for demo purposes
  • Entry Points: Web (80) and WebSecure (443)
  • Providers: Both CRD and standard Ingress support
  • Health Checks: Liveness and readiness probes
  • Node Placement: Scheduled on the control-plane node

Creating the Demo Bookstore Application

Frontend Service

The frontend serves as the main landing page for our bookstore:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: bookstore
spec:
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: frontend-html

The frontend includes an HTML page that provides:

  • Welcome message and service information
  • Links to test API endpoints
  • Architecture overview
  • Navigation to the Traefik dashboard

API Service

The API service handles book data and provides RESTful endpoints:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: bookstore
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: api-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
- name: api-data
mountPath: /usr/share/nginx/html
volumes:
- name: api-config
configMap:
name: api-nginx-config
- name: api-data
configMap:
name: api-data

The API service provides:

  • /books - Returns a JSON list of available books
  • /health - Health check endpoint
  • Root endpoint with API information

User Service

The user service manages authentication and user profiles:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
namespace: bookstore
spec:
replicas: 2
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: user-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
volumes:
- name: user-config
configMap:
name: user-service-config

Endpoints include:

  • /profile - User profile information
  • /login - Authentication endpoint
  • /health - Health check

Configuring Traefik Routing

Middlewares

Traefik middlewares modify requests before forwarding them to services. We use stripPrefix middleware to remove URL prefixes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: strip-api-prefix
namespace: bookstore
spec:
stripPrefix:
prefixes:
- /api
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: strip-users-prefix
namespace: bookstore
spec:
stripPrefix:
prefixes:
- /users

These middlewares ensure that:

  • Requests to /api/books become /books when forwarded to the API service
  • Requests to /users/profile become /profile when forwarded to the user service

IngressRoute Configuration

The IngressRoute defines how Traefik routes traffic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: bookstore-routes
namespace: bookstore
spec:
entryPoints:
- web
routes:
# API service routes
- match: PathPrefix(`/api`)
kind: Rule
services:
- name: api
port: 80
middlewares:
- name: strip-api-prefix
namespace: bookstore

# User service routes
- match: PathPrefix(`/users`)
kind: Rule
services:
- name: user-service
port: 80
middlewares:
- name: strip-users-prefix
namespace: bookstore

# Frontend catch-all (must be last)
- match: PathPrefix(`/`)
kind: Rule
services:
- name: frontend
port: 80

Important: The order of routes matters! More specific routes (/api, /users) must come before the catch-all route (/).

Deployment and Testing

Deploy All Resources

Apply all the Traefik resources:

1
kubectl apply -f base/traefik/

Deploy the bookstore application:

1
kubectl apply -f base/bookstore/

Verify the Deployment

Check that all pods are running:

1
kubectl get pods -A

You should see:

  • Traefik pod in traefik-system namespace
  • Frontend, API, and user service pods in bookstore namespace

Testing the Application

  1. Frontend Service: Visit http://localhost

    • Should display the bookstore homepage
    • Contains links to test other services
  2. API Endpoints:

    • http://localhost/api/books - Returns book data in JSON
    • http://localhost/api/health - API health status
  3. User Endpoints:

    • http://localhost/users/profile - User profile data
    • http://localhost/users/login - Login information
  4. Traefik Dashboard: http://localhost:9000/dashboard/

    • Shows routing configuration
    • Displays service health status
    • Provides traffic metrics

Understanding the Architecture

Traffic Flow

  1. Client Request: Browser makes request to http://localhost/api/books
  2. Traefik Reception: Traefik receives the request on port 80
  3. Route Matching: Traefik matches the request against IngressRoute rules
  4. Middleware Processing: strip-api-prefix removes /api from the path
  5. Service Forwarding: Modified request (/books) is sent to the API service
  6. Response: API service responds with book data
  7. Client Response: Traefik forwards the response to the client

Service Discovery

Traefik automatically discovers services through:

  • Kubernetes API: Watches for IngressRoute and Service changes
  • Dynamic Configuration: Updates routing rules without restart
  • Health Checking: Monitors service health through readiness probes

Load Balancing

Each service runs with 2 replicas, and Traefik automatically:

  • Distributes traffic across healthy instances
  • Removes unhealthy instances from rotation
  • Provides session affinity if configured

Troubleshooting Common Issues

Pod Not Starting

Check pod logs:

1
2
kubectl logs -n traefik-system deployment/traefik
kubectl logs -n bookstore deployment/frontend

Routing Not Working

  1. Verify IngressRoute configuration:

    1
    kubectl get ingressroute -n bookstore -o yaml
  2. Check Traefik dashboard for route status

  3. Ensure middleware names match between definitions and usage

Port Conflicts

If ports 80 or 8080 are already in use:

  1. Stop conflicting services
  2. Or modify the kind-config.yaml to use different ports

Service Discovery Issues

Verify that:

  • Services have correct selectors
  • Pods have matching labels
  • Services are in the correct namespace

Advanced Configuration Options

HTTPS/TLS Configuration

To enable HTTPS:

1
2
3
4
5
6
7
8
9
10
11
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: bookstore-https
spec:
entryPoints:
- websecure
tls:
secretName: bookstore-tls
routes:
# ... same routes as HTTP version

Rate Limiting

Add rate limiting middleware:

1
2
3
4
5
6
7
8
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rate-limit
spec:
rateLimit:
burst: 100
period: 1m

Authentication

Configure basic auth:

1
2
3
4
5
6
7
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: auth
spec:
basicAuth:
secret: auth-secret

Production Considerations

Security Hardening

  1. Disable Insecure API: Remove --api.insecure=true in production
  2. Enable TLS: Configure proper certificates
  3. Network Policies: Restrict pod-to-pod communication
  4. RBAC: Use minimal required permissions

High Availability

  1. Multiple Replicas: Run multiple Traefik instances
  2. Anti-Affinity: Ensure replicas run on different nodes
  3. Health Checks: Configure appropriate probe settings
  4. Resource Limits: Set CPU and memory limits

Monitoring and Observability

  1. Metrics: Enable Prometheus metrics collection
  2. Logging: Configure structured logging
  3. Tracing: Integrate with Jaeger or Zipkin
  4. Alerting: Set up alerts for service health

Cleanup

To remove the demo environment:

1
2
# Delete the Kubernetes cluster
kind delete cluster --name traefik-demo

This removes all resources and frees up system resources.

Conclusion

In this comprehensive guide, we’ve built a complete microservices demo using Traefik on Kubernetes. We’ve covered:

  • Setting up a local Kubernetes cluster with KinD
  • Installing and configuring Traefik as an ingress controller
  • Creating multiple microservices with different responsibilities
  • Implementing path-based routing with middlewares
  • Testing and troubleshooting the complete system

This demo provides a solid foundation for understanding how Traefik works in a microservices architecture. The concepts and configurations shown here can be adapted for production use with appropriate security and scalability considerations.

Traefik’s dynamic configuration capabilities make it an excellent choice for modern cloud-native applications where services are frequently updated and deployed. Its integration with Kubernetes through CRDs provides a powerful and flexible routing solution that scales with your application needs.

The hands-on experience gained from this tutorial will help you implement similar solutions in your own projects, whether you’re building new microservices architectures or migrating existing monolithic applications. You can find required files and configurations in the GitHub repository.

Happy Coding