Well, this article is the answer. We’ll configure ArgoCD to be accessible through a public domain (e.g., argocd.example.com) with HTTPS using certificates automatically generated by Let’s Encrypt — all handled by Traefik as the Ingress Controller, without needing to install cert-manager separately.
Intended for kubeadm bare-metal/on-prem clusters with a public domain whose DNS is already pointed to the node’s IP.
Here are some recommendations if you plan to bring this setup to production:
1. Use Longhorn for Persistent Volumes
Instead of using hostPath which stores data directly on the node, use Longhorn — a distributed block storage solution for Kubernetes. Longhorn provides:
Data replication across nodes (data survives node failures)
Automatic snapshots & backups
Volume portability — pods can move between nodes without losing data
Built-in monitoring dashboard
2. Use MetalLB for Network Management
Instead of using hostNetwork: true (which bypasses network isolation), use MetalLB as a bare-metal LoadBalancer:
Provides proper external IPs for type: LoadBalancer Services
Eliminates the need for hostNetwork — Traefik can run with normal network isolation
Supports L2 (ARP) and BGP modes
Enables multiple Traefik replicas with the same IP (high availability)
3. Harden the Security Context
For production, improve Traefik’s security context:
Use runAsNonRoot: true with NET_BIND_SERVICE capability
Set readOnlyRootFilesystem: true
Set allowPrivilegeEscalation: false
Drop all capabilities except the ones actually needed
A production-grade setup with Longhorn + MetalLB + hardened Traefik will be covered in a separate article. For now, let’s focus on getting a functional setup first.
Edit the manifest to disable ArgoCD’s built-in TLS, since HTTPS will be handled entirely
by Traefik in front of it.
Terminal window
1
nanoinstall.yaml
Find the Deployment named argocd-server and change the args section to:
1
spec:
2
containers:
3
- args:
4
- /usr/local/bin/argocd-server
5
- --staticassets
6
- /shared/app
7
- --insecure
Why is --insecure safe here?
This flag only makes ArgoCD serve HTTP from the pod side. Traefik sits in front and
handles TLS termination — so external communication remains HTTPS-encrypted.
Internal communication from Traefik to the ArgoCD pod goes over HTTP within the cluster,
which is already isolated.
Create the namespace and deploy.
Terminal window
1
kubectlcreatenamespaceargocd
2
kubectlapply--server-side-finstall.yaml-nargocd
Why use --server-side?
Recent ArgoCD versions have very large CRDs, especially
applicationsets.argoproj.io. By default, kubectl apply stores the entire manifest
content as a kubectl.kubernetes.io/last-applied-configuration annotation on the CRD
object itself. Kubernetes limits annotation size to a maximum of 256KB (262144 bytes) — and
ArgoCD’s CRDs exceed this limit, causing regular kubectl apply to fail with:
1
The CustomResourceDefinition "applicationsets.argoproj.io" is invalid:
2
metadata.annotations: Too long: may not be more than 262144 bytes
With --server-side, the apply process is handled on the API server side (not the client),
so the last-applied-configuration annotation is not stored and the 256KB limit doesn’t apply.
If a CRD was previously applied the regular way and a conflict occurs, add
the --force-conflicts flag:
Replace email@example.com with your active email address for Let’s Encrypt notifications.
Breaking changes in Traefik v3 (Helm chart >= 30.x):
certResolvers at the values level is no longer recognized — ACME configuration is now passed
directly via additionalArguments to the Traefik binary
tls inside ports.websecure is no longer allowed in the v3 schema
tlsStore.default.defaultCertResolver has been removed from the CRD — do not use it
Cert resolver is specified per-Ingress via annotation:
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
Explanation of other configuration choices:
Deployment + hostNetwork: true — Traefik binds directly to host ports,
the simplest solution for bare-metal without a LoadBalancer. Using Deployment
(not DaemonSet) ensures only one replica manages ACME certificates
tlsChallenge=true — Traefik verifies the domain with Let’s Encrypt via the TLS-ALPN-01 challenge
directly on port 443, no need for port 80 for the challenge (but port 80 is still open
for HTTP → HTTPS redirect)
persistence.enabled: true — certificates are stored on a PersistentVolume so they don’t need
to be re-requested from Let’s Encrypt every time the pod restarts (Let’s Encrypt has strict rate limits)
Create an ingress.yaml file. Traefik will automatically request a certificate from Let’s Encrypt
based on the traefik.ingress.kubernetes.io/router.tls.certresolver annotation.
Terminal window
1
nanoingress.yaml
1
apiVersion: networking.k8s.io/v1
2
kind: Ingress
3
metadata:
4
name: argocd-server-ingress
5
namespace: argocd
6
annotations:
7
# Use the certresolver configured in Traefik values
The browser shows the HTTPS padlock because the certificate from Let’s Encrypt is valid.
Accessing via http:// will automatically redirect to https://.
This article is a continuation of the bare-metal Kubernetes installation series. Adjust domains, IPs, and version numbers to match your own environment.
Setting Up ArgoCD with Traefik and HTTPS (Let's Encrypt) on a Public Domain