Containers

How To Enable Traefik Dashboard on k3s Kubernetes

K3s ships Traefik with the dashboard already switched on inside the pod, but nothing routes to it. Port-forward to port 8080 on a fresh cluster and /dashboard/ answers 404, because the packaged chart leaves its dashboard IngressRoute disabled. To enable the Traefik dashboard on K3s, you turn that route on with a HelmChartConfig and put basic auth in front of it.

Original content from computingforgeeks.com - post 62800

The older recipe still being copied around (--api.insecure=true plus ports.traefik.expose: true) no longer works. On the current chart it fails the Helm upgrade outright, and a mistyped key fails the other way: Helm says the upgrade went fine and changes nothing. We cover both failures below with the exact errors. Every command here was run on K3s v1.36.5+k3s1 on Ubuntu 24.04 in November 2026, and all the output comes from that node.

Install K3s on the server

If you already have a K3s server with Traefik running, skip to the next section. Otherwise, install a single server node. The --write-kubeconfig-mode 644 flag lets your normal user run kubectl without sudo, which the rest of this guide assumes. It also makes the cluster-admin kubeconfig readable by every local account. On a shared host, leave the flag off, copy /etc/rancher/k3s/k3s.yaml to ~/.kube/config with sudo, chown it to your user and export KUBECONFIG=~/.kube/config, because the bundled kubectl otherwise keeps reading the file under /etc. The version pin matches the lab. Drop INSTALL_K3S_VERSION to get whatever the stable channel currently serves.

# https://github.com/k3s-io/k3s/releases
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.36.5+k3s1 sh -s - --write-kubeconfig-mode 644

Once the installer has fetched the binary and linked kubectl to it, it starts the k3s unit, and within about a minute you should see the node report Ready with the version you pinned:

K3s v1.36.5+k3s1 install output and Ready node on Ubuntu 24.04

The distro-specific details (firewall ports, agents, upgrades) are covered in our guides to install K3s on Ubuntu 26.04, K3s on Rocky Linux 10 and K3s on Debian 13. The dashboard steps below are the same on all of them.

Check what Traefik exposes by default

Before changing anything, confirm which Traefik build and chart K3s deployed, because the values you can set depend on the chart. The chart tarball lives in a root-only directory, so that listing needs sudo:

kubectl -n kube-system get pods
kubectl -n kube-system get svc traefik
kubectl -n kube-system get deploy traefik -o jsonpath='{.spec.template.spec.containers[0].image}'; echo
sudo ls /var/lib/rancher/k3s/server/static/charts/

Now test the dashboard itself. Traefik’s internal entrypoint listens on 8080 in the pod, so a port-forward is the quickest probe:

kubectl -n kube-system port-forward deploy/traefik 8080:8080 &
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/dashboard/
kubectl get ingressroute -A

Our node ran the Traefik image and chart shown below, and the dashboard answered 404 with no IngressRoute anywhere in the cluster:

Default K3s Traefik pods, chart 40.1.5 and dashboard returning 404 with no IngressRoute

A 404 here is the default, not a fault. The deployment args include --api.dashboard=true but not --api.insecure, so the API is only reachable through a router that points at api@internal. Stop the port-forward with kill %1. Before moving on, note three values from your own output, because they decide which keys the chart accepts:

  • the Traefik image tag in the deployment
  • the traefik-*.tgz chart version in the charts directory
  • the 404 from /dashboard/ on port 8080

If your chart is older than the one in the screenshot, open its values.yaml before copying the manifests below. On a current K3s release, you can have the chart create the dashboard router for you, with authentication in front of it.

Create the basic auth secret and middleware

The dashboard shows every router, service and middleware in the cluster, so do not publish it without a login. Traefik’s basicAuth middleware reads bcrypt hashes from a Secret, and htpasswd from apache2-utils generates them. Pick your own password in place of the example:

sudo apt-get install -y apache2-utils
htpasswd -nbB admin 'S3cure-Dash-2026' > users
kubectl -n kube-system create secret generic traefik-dashboard-auth --from-file=users

Keep a single key in the Secret. Naming the file users follows the convention in the Traefik docs, and the middleware reads the htpasswd lines from it. Delete the local copy with rm users once the Secret exists. On Rocky Linux or Fedora the same tool comes from the httpd-tools package. Next, create the middleware manifest:

vim dashboard-auth-middleware.yaml

Add the following. It lives in kube-system next to Traefik, so the dashboard route can reference it by name alone:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: dashboard-auth
  namespace: kube-system
spec:
  basicAuth:
    secret: traefik-dashboard-auth

Apply it with kubectl apply -f dashboard-auth-middleware.yaml. It should print middleware.traefik.io/dashboard-auth created. Nothing uses it yet, so this step changes no traffic.

Enable the dashboard IngressRoute

K3s installs Traefik through its Helm controller, so you never edit the Traefik deployment directly. A HelmChartConfig named traefik in kube-system merges your values over the packaged ones, and the controller reruns the Helm upgrade whenever that object changes. Create the file:

vim traefik-config.yaml

Paste the values below and change traefik.example.com to the hostname you’ll use for the dashboard:

apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |-
    ingressRoute:
      dashboard:
        enabled: true
        matchRule: Host(`traefik.example.com`)
        entryPoints:
          - web
        middlewares:
          - name: dashboard-auth

Copy it into the K3s auto-deploy directory rather than running kubectl apply. K3s reapplies files in that directory on every start and whenever they change, so a stray manual edit to the object gets reverted to what the file says. Then check the Helm job’s log:

sudo cp traefik-config.yaml /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
kubectl -n kube-system get pods | grep helm-install
kubectl -n kube-system logs job/helm-install-traefik --tail=6
kubectl -n kube-system get ingressroute

Look for a new helm-install-traefik pod in Completed, DESCRIPTION: Upgrade complete in its log, and a traefik-dashboard IngressRoute. On our node that took about 30 seconds:

Creating the basic auth secret, middleware and HelmChartConfig for the Traefik dashboard on K3s

Do not stop at “Upgrade complete”. Helm prints that line when it rendered the chart without an error, and it does not mean your keys did anything, as the troubleshooting section shows. The IngressRoute appearing is the real proof.

Verify the route and the login

Describe the route to confirm the chart wired in your hostname, the web entrypoint and the middleware. Then call it with and without credentials. The Host header lets you test from the node before DNS exists:

kubectl -n kube-system describe ingressroute traefik-dashboard
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: traefik.example.com' http://127.0.0.1/dashboard/
curl -s -o /dev/null -w '%{http_code}\n' -u admin:S3cure-Dash-2026 -H 'Host: traefik.example.com' http://127.0.0.1/dashboard/
curl -s -u admin:S3cure-Dash-2026 -H 'Host: traefik.example.com' http://127.0.0.1/api/version

In your output, the route should send Host(`traefik.example.com`) through dashboard-auth to api@internal. The anonymous request gets 401, the authenticated one gets 200, and the API reports the running version:

Traefik dashboard IngressRoute details with 401 without credentials and 200 with basic auth

To open it in a browser, the hostname has to resolve to a node IP. That’s the EXTERNAL-IP the traefik service showed earlier, because the K3s service load balancer binds ports 80 and 443 on each node. Create a DNS A record, or for a quick test add a line to /etc/hosts on your workstation:

echo "10.0.1.50 traefik.example.com" | sudo tee -a /etc/hosts

Browse to http://traefik.example.com/dashboard/ (the trailing slash matters) and log in with the user you created. The overview lists the four entrypoints from the chart (metrics 9100, traefik 8080, web 8000, websecure 8443) and green counters for routers, services and middlewares:

Traefik 3.7 dashboard overview on K3s showing entrypoints, routers, services and middlewares

You’ll come back to the HTTP Routers page whenever one of your Ingress objects misbehaves. On a fresh cluster it holds the dashboard route from the Kubernetes CRD provider plus Traefik’s own ping and metrics routers:

Traefik dashboard HTTP Routers page listing the K3s dashboard route to api@internal

Every Ingress you deploy later shows up here with its rule, entrypoint and backing service. A red status icon on a router is usually the fastest way to spot a typo in a service name or port.

Troubleshoot HelmChartConfig changes

Almost every Traefik dashboard problem on K3s comes from the values passed in the HelmChartConfig. The Helm job is where the evidence lives, so kubectl -n kube-system logs job/helm-install-traefik is the first command to run. The two cases below are the ones we reproduced on the lab node.

Error: UPGRADE FAILED: can’t index item of type bool

This is what the old dashboard recipe produces. Its HelmChartConfig sets ports.traefik.expose: true with --api.insecure=true in additionalArguments. Newer Traefik charts made expose a map keyed by service name (the default is default: false), so a plain boolean breaks the service template. We applied that exact manifest to the lab cluster:

helm-install-traefik UPGRADE FAILED error caused by ports.traefik.expose set to true

Meanwhile the job pod sits in Error and keeps restarting, while Traefik keeps running on its last good release, so your traffic is not affected. If the old values live in a file under /var/lib/rancher/k3s/server/manifests/, overwrite it with the working traefik-config.yaml from this guide, because K3s reapplies that directory and deleting a file there does not delete the object it created. If you applied the old manifest with kubectl, remove it with kubectl -n kube-system delete helmchartconfig traefik, and the next job completes cleanly. Do not try to rescue the old approach by switching to expose: {default: true}. That publishes the unauthenticated internal entrypoint on the load balancer, which is exactly what the IngressRoute plus middleware avoids. If you also created the old guide’s traefik-dashboard Ingress pointing at port 9000, delete it with kubectl -n kube-system delete ingress traefik-dashboard so it can’t shadow the new route on the same hostname.

Upgrade complete, but the dashboard returns 404

Helm values are case-sensitive, and the chart quietly ignores a key it doesn’t recognise. To show it, we changed ingressRoute: to ingressroute: in the same file and copied it over the working one:

Lowercase ingressroute key in HelmChartConfig reports Upgrade complete but removes the dashboard route

Helm logged revision 4 as Upgrade complete with no warning, yet the IngressRoute was gone and the authenticated request now got 404. The same trap catches provider keys. Our K3s on Ubuntu 24.04 lab found that kubernetesIngressNginx is ignored while kubernetesIngressNGINX works. That rename applies to the Traefik chart v40 line that K3s bundles from v1.36.2 onward, including the v1.36.5 release used here, and our K3s on Ubuntu 26.04 guide walks through the one-line fix. When a change seems to do nothing, check the object it should have created (kubectl get ingressroute -n kube-system, or the deployment args) instead of trusting the Helm status. Then compare your keys against the chart’s own values.yaml. Restoring the correct file brought the route back and the login returned 200 again.

Harden it before anyone else uses it

Basic auth on the web entrypoint sends the password in clear text, which is acceptable on a lab LAN and nowhere else. For a shared or internet-facing cluster, tighten the setup in a few places:

  • websecure instead of web in entryPoints
  • a real certificate for the dashboard hostname instead of Traefik’s default self-signed one
  • an ipAllowList middleware listing only your admin network, with service.spec.externalTrafficPolicy: Local so Traefik sees real client IPs (and no node-external-ip on NAT’d nodes)
  • one htpasswd line per admin, so you can revoke one person without rotating everyone

Each extra middleware is another entry under middlewares in the same HelmChartConfig, applied in the order listed. The K3s documentation on customizing packaged components with HelmChartConfig explains how the merge works if you plan to stack more values on top. If you’re still choosing a distribution, our k0s vs K3s vs MicroK8s benchmark shows what each one bundles. For the dashboard itself, the chart’s IngressRoute plus a basicAuth middleware is the setup we’d run. It’s two small files, survives K3s restarts, and never exposes the insecure API port. After a K3s minor upgrade, confirm the IngressRoute still exists, because a chart bump can change which keys are valid.

Keep reading

Best UI Applications for Managing Docker Containers Containers Best UI Applications for Managing Docker Containers Install Docker and Run Containers on Ubuntu 24.04|22.04 Containers Install Docker and Run Containers on Ubuntu 24.04|22.04 Install UniFi OS Server on Ubuntu 24.04 LTS Containers Install UniFi OS Server on Ubuntu 24.04 LTS CapRover Docker Install on Debian 13: Deploy Your First App Containers CapRover Docker Install on Debian 13: Deploy Your First App Best OpenShift Books for Admins and EX280 Candidates Books Best OpenShift Books for Admins and EX280 Candidates Enable CloudWatch Logging in EKS Kubernetes Cluster Kubernetes Enable CloudWatch Logging in EKS Kubernetes Cluster

Leave a Comment

Press ESC to close