Enable and Verify the Built-in Traefik Ingress Controller in k3s
Confirm Traefik is running in kube-system, its Service is exposed via NodePort, and a test Ingress routes traffic. Covers prerequisites, verification commands, and recovery if Traefik was disabled.
28 Oct 2025, 20:13 UTC

Problem: Ingress resources exist but external traffic never reaches a service
In a fresh k3s install the Ingress controller may be disabled, not yet scheduled, or the service not reachable from outside the node. The useful takeaway is to confirm Traefik is actually running in kube-system and its service is exposed before creating production Ingress objects.
Desired outcome
Traefik Ingress controller deployed in namespace kube-system, pods Running on cluster nodes, a Service exposing ports 80 and 443 via ClusterIP and NodePort, and Ingress resources routing HTTP traffic to backend Services.
Prerequisites
- A running k3s cluster, single-node or multi-node. Version assumption: k3s v1.30+ where Traefik is enabled by default. Older releases may differ.
- kubectl configured with cluster-admin privileges on the control plane node.
- Network access to the node IP or LoadBalancer address and no firewall blocking the Traefik NodePorts.
Check whether Traefik is enabled
If k3s was started with --disable traefik, the controller will not be installed.
On the control plane node, inspect the running server process:
ps aux | grep k3s
Look for --disable traefik in the arguments. If present, re-enable by editing the systemd unit or config file and restarting k3s:
sudo systemctl restart k3s
Required permission: root on the node. Risk: restarting k3s briefly interrupts API server availability.
Alternative start flag for a new server:
k3s server --enable traefik
Verify deployment and service
Run from any machine with kubectl access:
kubectl get deploy -n kube-system traefik
Confirm the deployment object exists. Then check pods:
kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik -o wide
Expected check: pod status is Running and the node field shows a cluster node. If pods are Pending, review node resources and CNI.
Check the service:
kubectl get svc -n kube-system traefik
kubectl describe svc -n kube-system traefik
Expected check: Service type is ClusterIP with NodePort allocations for 80 and 443. Note the NodePort numbers for external access.
Validate routing with a test Ingress
Create a simple backend in a test namespace:
kubectl create namespace test-ingress
kubectl apply -n test-ingress -f -
Deploy a test pod and service, then an Ingress that points to it. The Ingress must reference the host you will request and the service name.
Access the service via node IP and NodePort, for example node-ip:nodeport. Confirm HTTP response from the backend.
Inspect Traefik logs for startup errors:
kubectl logs -n kube-system <traefik-pod>
Replace <traefik-pod> with the pod name from the earlier get pods output.
Expected checks
- Deployment traefik exists in kube-system.
- Pods labeled app.kubernetes.io/name=traefik are Running.
- Service traefik exposes NodePorts for 80 and 443.
- A test Ingress routes to the backend when accessed via node IP:NodePort.
- Traefik logs show no repeated crash loops.
Recovery options and limitations
If Traefik is missing: ensure k3s was not started with --disable traefik and restart the server. Do not edit the deployment manually; it is managed by k3s.
If pods are CrashLoopBackOff: check node resources, disk pressure, and CNI connectivity. Review logs before restarting.
Default configuration is HTTP only. TLS requires a Kubernetes Secret of type tls and an Ingress with tls section. Verification of TLS needs a valid certificate and curl with host header.
In HA clusters Traefik may run on multiple nodes. Ensure firewalls allow the NodePorts on all nodes that host pods.
Limitation: this guide assumes the built-in Traefik distribution shipped with k3s. Custom Helm installs or external ingress controllers are out of scope.
Practical way to check result: after the test Ingress is applied, request the host via the node IP and NodePort and confirm the backend response. If response is received, the controller is correctly enabled and reachable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.