By default, K3s ships with Traefik as the Ingress controller. You can configure it to automatically generate TLS certificates using Let’s Encrypt via the ACME protocol.
This guide assumes:
- You're running K3s with Traefik enabled
- You have a public domain name (e.g.,
me.example.com
) - Ports 80 and 443 are open and forwarded to your K3s nodes
- DNS for your domain points to your K3s cluster external IP
✅ Step 1: Check That Traefik Is Installed
Run the following to verify Traefik is deployed:
kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik
If you see a Traefik pod running, you're good.
If not, reinstall K3s with the --disable traefik
flag removed.
✍️ Step 2: Create a Traefik Configuration Override
📍 You’ll only do this on one of the control-plane nodes (any server running
k3s server
).
Create a file at:
/var/lib/rancher/k3s/server/manifests/traefik-config.yaml
Paste the following config (customize your email address):
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
additionalArguments:
- "[email protected]"
- "--certificatesresolvers.default.acme.storage=/data/acme.json"
- "--certificatesresolvers.default.acme.httpchallenge.entrypoint=web"
ports:
web:
exposedPort: 80
websecure:
exposedPort: 443
Replace
[email protected]
with your real email. Let’s Encrypt will use this to notify you about certificate expiry issues.
Save and close the file.
K3s will detect the new manifest and automatically apply it (no restart needed).
🧪 Step 3: Verify Traefik Restarted With New Settings
Check that the Traefik pods restarted and picked up the changes:
kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik
Watch logs to see ACME activity:
kubectl logs -n kube-system -l app.kubernetes.io/name=traefik --tail=100 -f
Look for lines like:
Starting provider aggregator.ProviderAggregator
... Using HTTP challenge
... Registering with ACME server https://acme-v02.api.letsencrypt.org
🌐 Step 4: Create an Ingress Resource with TLS
Here’s a sample Ingress you can use for something like me.example.com:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: me-ingress
annotations:
kubernetes.io/ingress.class: "traefik"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls.certresolver: default
spec:
rules:
- host: me.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: me-loadbalancer
port:
number: 9000
tls:
- hosts:
- me.example.com
Replace
me.example.com
with your real domain, andme-loadbalancer
with the name of your Service.
🌍 Step 5: Point DNS to Your Cluster
Make sure me.example.com
points to your external IP (e.g., from a cloud provider or your router). You can use services like:
- Cloudflare DNS
- DuckDNS (free dynamic DNS)
- Any custom domain registrar
🚀 Step 6: Apply the Ingress
kubectl apply -f me-ingress.yaml
Wait 30–60 seconds. Then check if the certificate was issued:
kubectl describe ingress me-ingress -n my-namesapce
You should see a section under TLS
with a certificate from Let’s Encrypt.
🔐 Step 7: Access Your App via HTTPS
Open your browser and go to:
https://me.example.com
You should see a valid SSL certificate (green lock icon) and no warnings.
🧰 Troubleshooting Tips
Issue | Solution |
---|---|
ERR Unable to obtain ACME certificate for domains error="unable to generate a certificate for the domains [me.example.com]: error: one or more domains had a problem:\n[me.example.com] acme: error: 403 :: urn:ietf:params:acme:error:unauthorized :: xxx.xxx.xxx.xxx: Invalid response from http://me.example.com/.well-known/acme-challenge/yyyy: 503\n" ACME CA=https://acme-v02.api.letsencrypt.org/directory acmeCA=https://acme-v02.api.letsencrypt.org/directory domains=["me.example.com"] providerName=default.acme routerName=minio-me-ingress-me-example-com@kubernetes rule="Host(me.example.com ) && PathPrefix(/ ) |
This url http://me.example.com/.well-known/acme-challenge/yyyy shour return 404. If you are running a loadbalancer make shure that it allows 404 statuses to pass health check |
When using Load Balancer | Make sure that it is not proxied and have ports 80 and 443 open. |
Certificate not generated | Check if port 80 is open and reachable |
DNS not working | Use nslookup me.example.com from your local machine |
Traefik logs show ACME errors | Check email and domain configuration |
Still using HTTP | Ensure you're hitting https:// and not http://
|
✅ Summary
Task | Done |
---|---|
Verified Traefik is installed | ✅ |
Configured Let's Encrypt via manifest | ✅ |
Created Ingress with TLS annotations | ✅ |
DNS points to your K3s cluster | ✅ |
Accessed service over HTTPS | ✅ |