Enforcing Mutual TLS in Traefik Mesh: A Practical Guide
Learn how to enable global mTLS in Traefik Mesh, configure the built‑in CA, and verify security with a sample micro‑service pair. Understand trade‑offs, resource impact, and how to disable mTLS for legacy services.
28 Jan 2026, 16:48 UTC

Problem: Unsecured Inter‑Service Traffic
In a Kubernetes cluster, micro‑services often talk to one another over plain HTTP. A breach in one pod can expose the entire mesh, and network policies alone rarely enforce encryption. Mutual TLS (mTLS) authenticates both client and server, protecting data in transit and preventing rogue pods from joining the conversation.
Thesis: Global mTLS is Simple, but Requires Planning
Traefik Mesh ships with an internal Certificate Authority (CA) that signs short‑lived certificates (90‑day rotation). Enabling mTLS globally is a single flag, but you must ensure that all services receive the sidecar injector, that the CA bundle is trusted outside the cluster, and that the cluster has enough resources to run the extra sidecars.
1. How Traefik Mesh Handles mTLS
When mTLS is enabled, every pod that is part of the mesh gets a sidecar proxy. The proxy terminates TLS, validates the peer certificate against the internal CA, and forwards the request to the application container. The application code remains untouched; all TLS logic lives in the proxy.
The internal CA automatically rotates certificates every 90 days, so you don’t need to manage keys manually. The CA’s root certificate is exposed via the mesh-ca secret, which can be mounted into any pod that needs to verify outbound traffic.
2. Enabling Global mTLS
2.1. Mesh Configuration
# /etc/traefikmesh/mesh.yaml
mTLS:
enabled: true
Apply the file to the Mesh CRD:
kubectl apply -f mesh.yaml
2.2. Deploy the CA Bundle
Traefik Mesh creates a mesh-ca secret automatically when you install the mesh. Verify its presence:
kubectl get secret mesh-ca -n traefik-mesh -o yaml
Expose the CA to external consumers by creating a ConfigMap or mounting the secret into pods that need to verify certificates.
2.3. Ensure Sidecar Injection
Sidecar injection is enabled by default for all namespaces labeled traefik.io/mesh=true. Add the label to the namespace where your services live:
kubectl label namespace my‑ns traefik.io/mesh=true
All deployments in that namespace will receive the traefik-proxy sidecar automatically.
3. Worked Example: Two Micro‑Services
We’ll deploy two simple services, frontend and backend, and verify that traffic between them is encrypted.
3.1. Backend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
labels:
app: backend
spec:
replicas: 1
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: http
image: hashicorp/http-echo
args: ["-text=Hello from backend"]
ports:
- containerPort: 80
3.2. Frontend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
app: frontend
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: curl
image: curlimages/curl
command: ["sleep", "infinity"]
Expose the backend with a Service:
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 80
targetPort: 80
3.3. Verify mTLS
Open a shell into the frontend pod:
kubectl exec -it $(kubectl get pod -l app=frontend -o jsonpath='{.items[0].metadata.name}') -- sh
Attempt a plain HTTP request (should fail because the sidecar expects TLS):
curl http://backend:80
Result: connection refused or TLS handshake error.
Now perform a TLS request using the CA bundle (the sidecar automatically mounts the CA into /etc/traefik/ca.crt inside the pod). Use curl -k to skip client certificate verification for the demo:
curl -k https://backend:80
Response: Hello from backend. The sidecar handled the TLS handshake. To confirm that the handshake actually occurred, check the sidecar logs:
kubectl logs -n my‑ns $(kubectl get pod -l app=frontend -o jsonpath='{.items[0].metadata.name}') -c traefik-proxy | grep "TLS handshake"
Expected log snippet: TLS handshake succeeded.
4. Trade‑offs & Limitations
- Resource Impact: Each sidecar adds ~30 MiB memory and ~0.1 CPU. In large clusters, this can push node limits. Monitor
traefik-proxymetrics and adjust node sizing. - Latency: TLS termination adds a few milliseconds per hop. For latency‑sensitive workloads, benchmark before production rollout.
- CA Trust Outside the Cluster: External clients (e.g., CI/CD pipelines) must trust the internal CA. Export the root cert from
mesh-caand add it to their trust store. - Legacy Services: If a pod cannot run the sidecar (e.g., uses a privileged image), you can disable mTLS per‑service with annotations:
traefik.io/mtls: "false". This lowers overall security. - Certificate Rotation: Certificates rotate every 90 days. Ensure that any custom client code that caches certs refreshes them or validates against the CA bundle each request.
5. Actionable Checklist
- Enable
mTLS.enabled: truein the Mesh CRD. - Label target namespaces with
traefik.io/mesh=true. - Verify the
mesh-casecret exists. - Deploy services and confirm sidecars are present (check pod containers).
- Test inter‑service traffic with
curl -kand check sidecar logs for handshake success. - Monitor
tls.handshake.countmetric in the Traefik Mesh dashboard; it should increment on each request. - For legacy services, annotate
traefik.io/mtls: "false"and document the security trade‑off. - Export the internal CA root to external trust stores if needed.
- Schedule a periodic audit of sidecar resource usage and latency.
By following these steps you’ll harden your mesh with minimal code changes while keeping an eye on performance and legacy compatibility.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.