When exposing Argo CD in a homelab using Traefik as the Ingress controller and Cloudflare Tunnel for HTTPS, I encountered a persistent 404 Not Found
on the root path (/
). Here’s a breakdown of what caused it, how I verified my routing setup, and what fixed the problem.
✅ The Setup
I used the following technologies:
- K3s as my Kubernetes distribution.
- Traefik as the built-in Ingress controller.
- Cloudflare Tunnel to expose
https://argo.elladali.com
to the public. - Argo CD installed in the
argocd
namespace.
My Ingress looked like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: argo.elladali.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
- path: /fallback
pathType: Prefix
backend:
service:
name: fallback-service
port:
number: 80
The /fallback
route was added to verify if traffic was even reaching my cluster — and it worked! A 404 on /fallback/
confirmed that my DNS, Cloudflare Tunnel, and Ingress routing were functioning correctly.
❌ The Problem: 404 Not Found
on /
Despite routing being correct, https://argo.elladali.com/
kept returning a 404.
I had customized the Argo CD config with this ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cmd-params-cm
namespace: argocd
data:
server.insecure: "true"
server.rootpath: "/"
server.basehref: "/"
At first glance, setting server.rootpath
and server.basehref
to /
seems harmless. But it turns out this breaks things.
🧠 The Root Cause
According to Argo CD documentation, server.rootpath
is only needed when Argo CD is hosted at a sub-path like /argocd
.
Setting it to /
tells the Argo CD UI to treat every request as if it were under a sub-path — which it’s not. As a result, all requests to /
, /login
, /api
, etc., get routed incorrectly and return 404.
This is a common mistake in reverse proxy setups. You don’t need to set
rootpath
if you’re using a clean domain likeargo.elladali.com/
.
✅ The Fix
-
Remove the path settings from the ConfigMap:
data: server.insecure: "true" # Remove or comment out: # server.rootpath: "/" # server.basehref: "/"
-
Apply the changes and restart the Argo CD server:
kubectl apply -f argocd-cmd-params-cm.yaml kubectl -n argocd rollout restart deployment argocd-server
-
Confirm it’s working:
Either port-forward internally:
kubectl -n argocd port-forward svc/argocd-server 8080:80 curl -I http://localhost:8080/ # Should return 302 → /login
Or refresh
https://argo.elladali.com/
in your browser — the UI should load!
✅ Bonus: Using /fallback
to Confirm Ingress
Creating a dummy route like /fallback
is a simple trick to check if your Traefik Ingress is working.
Here’s how to create a basic fallback service:
kubectl create deployment fallback --image=nginx -n argocd
kubectl expose deployment fallback --port=80 --target-port=80 -n argocd --name=fallback-service
Now visit https://argo.elladali.com/fallback/
. If it shows a 404 from Nginx or a default page, you know the Ingress works.
🎉 Summary
If you’re exposing Argo CD on a root domain (argo.example.com
) behind Traefik:
- ✅ Don’t set
server.rootpath
orserver.basehref
unless you’re using a sub-path. - ✅ Use a
/fallback
path to test Ingress behavior. - ✅ Confirm everything with
curl
or port-forward before debugging Cloudflare or TLS.
This setup now runs cleanly in my homelab, with Argo CD securely exposed via Cloudflare Tunnel — no TLS management, no headaches.
Got a similar setup or issue? Let me know — happy to dig deeper.