Install Argo CD on k3s
Install Argo CD on a k3s single-node cluster using Helm, then create and auto-sync your first GitOps Application from a Git repository.
Before you start
- ▸A machine with at least 2 vCPUs and 4 GB RAM running a supported Linux distro
- ▸A user account with sudo privileges and outbound internet access
- ▸A domain name or static IP for the Argo CD UI (port-forward works for local-only access)
- ▸Basic familiarity with kubectl and Kubernetes concepts (namespaces, pods, services)
Argo CD turns a Kubernetes cluster into a self-healing GitOps engine. Running it on k3s gives you a full GitOps workflow on a single VM or bare-metal node — useful for homelabs, edge sites, and staging environments that mirror production. This guide walks from a fresh machine to a working Argo CD installation with your first synced Application, using Helm for the install so upgrades stay manageable.
Prerequisites and Assumptions
You need a machine with at least 2 vCPUs and 4 GB RAM running a supported distro, a domain name or static IP (for the UI), and kubectl available after k3s installs. All commands run as a non-root user with sudo access unless noted.
Step 1: Install k3s
k3s ships a single binary that includes containerd, flannel, CoreDNS, and a built-in load balancer (ServiceLB). The installer script is the fastest path to a working cluster.
curl -sfL https://get.k3s.io | sh -s - \
--disable traefik \
--write-kubeconfig-mode 644
--disable traefik removes k3s's bundled ingress controller so Argo CD's own ingress or port-forward isn't blocked. --write-kubeconfig-mode 644 lets your regular user read the kubeconfig without sudo.
Wait about 30 seconds, then confirm the node is ready:
kubectl get nodes
Expected output (version will vary):
# NAME STATUS ROLES AGE VERSION
# myhost Ready control-plane,master 45s v1.29.4+k3s1
Export the kubeconfig path for tools that don't read the k3s default location:
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
echo 'export KUBECONFIG=/etc/rancher/k3s/k3s.yaml' >> ~/.bashrc
Step 2: Install Helm
Install Helm 3 using the official script. It does not touch your cluster — it is a local client only.
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
Verify:
helm version --short
Step 3: Install Argo CD via Helm
Add the Argo Helm repository
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
Create a values override file
A minimal argocd-values.yaml disables TLS on the server pod (termination happens at the ingress or you use port-forward) and sets resource limits appropriate for a single-node cluster:
cat > argocd-values.yaml << 'EOF'
global:
domain: argocd.example.com # change to your domain or remove if using port-forward
server:
extraArgs:
- --insecure # disable built-in TLS; terminate at ingress layer
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
repoServer:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
applicationSet:
enabled: true
notifications:
enabled: false # enable later when you need alerting
EOF
Install into its own namespace
kubectl create namespace argocd
helm install argocd argo/argo-cd \
--namespace argocd \
--version 7.3.4 \
--values argocd-values.yaml \
--wait
Pin --version to a specific chart release so future helm repo update runs don't silently upgrade. Check github.com/argoproj/argo-helm/releases for the latest stable tag.
Confirm all pods reach Running:
kubectl get pods -n argocd
Step 4: Access the Argo CD UI
Retrieve the initial admin password
Helm sets a random initial password stored in a Secret:
kubectl get secret argocd-initial-admin-secret \
-n argocd \
-o jsonpath="{.data.password}" | base64 -d; echo
Port-forward for quick access
If you're not setting up an ingress yet, forward the server port to your workstation:
kubectl port-forward svc/argocd-server -n argocd 8080:80
Open http://localhost:8080 in your browser. Log in with username admin and the password you retrieved above.
Optional: expose via k3s ServiceLB (LoadBalancer)
If you want a stable IP on your LAN without an ingress controller, patch the service type:
kubectl patch svc argocd-server \
-n argocd \
-p '{"spec":{"type":"LoadBalancer"}}'
k3s's built-in ServiceLB will assign the node's IP. Retrieve it with:
kubectl get svc argocd-server -n argocd
Step 5: Install the Argo CD CLI
The CLI is optional but makes scripting and inspecting Applications much faster than the UI alone.
VERSION=$(curl -s https://api.github.com/repos/argoproj/argo-cd/releases/latest \
| grep tag_name | cut -d '"' -f4)
curl -sSL -o argocd \
"https://github.com/argoproj/argo-cd/releases/download/${VERSION}/argocd-linux-amd64"
chmod +x argocd
sudo mv argocd /usr/local/bin/
Log in against the port-forwarded address (run port-forward in a separate terminal first):
argocd login localhost:8080 \
--username admin \
--password "$(kubectl get secret argocd-initial-admin-secret \
-n argocd -o jsonpath='{.data.password}' | base64 -d)" \
--insecure
Change the admin password immediately:
argocd account update-password
Step 6: Create Your First Application
An Argo CD Application maps a Git repository path to a Kubernetes namespace and keeps them in sync. The cleanest way to define one is with a YAML manifest committed to Git — but for a first smoke-test, the CLI is faster.
Deploy the official guestbook example
argocd app create guestbook \
--repo https://github.com/argoproj/argocd-example-apps.git \
--path guestbook \
--dest-server https://kubernetes.default.svc \
--dest-namespace default \
--sync-policy automated \
--auto-prune \
--self-heal
- --sync-policy automated — Argo CD syncs whenever it detects drift, no manual trigger needed.
- --auto-prune — resources removed from Git are deleted from the cluster.
- --self-heal — manual cluster changes that deviate from Git are reverted automatically.
Watch the sync
argocd app get guestbook
argocd app wait guestbook --health
Once health is Healthy and sync status is Synced, check the deployed resources:
kubectl get all -n default -l app=guestbook
Step 7: Define an Application in Git (the GitOps Way)
Long-term, manage Applications as YAML committed to a repository. Create apps/guestbook-app.yaml in your own repo:
cat > guestbook-app.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
EOF
kubectl apply -f guestbook-app.yaml
Apply this manifest once; Argo CD owns the lifecycle from that point forward. Point this pattern at your own app repo to go fully GitOps.
Verification
# All Argo CD pods healthy
kubectl get pods -n argocd
# Application sync status
argocd app list
# Cluster-wide resource count as a sanity check
kubectl get all -A | grep -v kube-system | grep -v argocd
Troubleshooting
k3s node stays NotReady
Check the service: sudo systemctl status k3s. Containerd socket issues after a reboot are usually fixed with sudo systemctl restart k3s.
Argo CD pods in CrashLoopBackOff
Inspect logs: kubectl logs -n argocd deploy/argocd-server. The most common cause on constrained nodes is OOM — raise the memory limit in argocd-values.yaml and run helm upgrade argocd argo/argo-cd -n argocd --values argocd-values.yaml.
Application stuck OutOfSync
Run argocd app diff guestbook to see exactly what the cluster has versus what Git expects. If a resource has a last-applied-configuration annotation conflict, force a hard refresh: argocd app get guestbook --hard-refresh.
Can't reach the UI after port-forward exits
The port-forward process dies with the terminal session. For persistent access on a homelab, use a systemd user service to keep it running, or set up an nginx/Caddy reverse proxy in front of the LoadBalancer IP.
Frequently asked questions
- Can I run Argo CD on a k3s cluster with less than 4 GB RAM?
- Technically yes — lower the resource limits in your values file and disable applicationSet and notifications. Expect slower sync cycles and possible OOM kills under load on nodes with 2 GB or less.
- Why disable Traefik during k3s install?
- k3s ships Traefik as a default ingress controller on port 80/443. Leaving it enabled doesn't break Argo CD, but it adds a layer you may not want and can interfere if you later install ingress-nginx or Caddy to front the Argo CD UI.
- How do I upgrade Argo CD after the initial install?
- Run helm repo update, then helm upgrade argocd argo/argo-cd -n argocd --version <new-version> --values argocd-values.yaml. Always check the Argo CD upgrade notes for that version before running the command.
- How does Argo CD authenticate to a private Git repository?
- Use argocd repo add <url> --ssh-private-key-path ~/.ssh/id_ed25519 for SSH repos, or --username / --password for HTTPS. Credentials are stored as Secrets in the argocd namespace.
- What is the difference between Synced and Healthy in Argo CD?
- Synced means the live cluster state matches what is defined in Git. Healthy means the deployed resources are actually running and passing their readiness checks. An app can be Synced but Degraded if pods are crash-looping after deployment.
Related guides
Configure Prometheus Alertmanager
Configure Prometheus Alertmanager with routing trees, receivers, inhibition rules, grouping, Go templates, and PagerDuty/Slack on-call integrations.
Build an Intranet Server on Linux
Set up a complete small-office intranet on one Linux box: Nginx web server, dnsmasq local DNS, Samba file sharing, and a Wiki.js team wiki.
Build an nftables Firewall Script
Build a complete nftables firewall from scratch: tables, chains, sets, default-deny input policy, service allowlisting, and persistent systemd configuration.
Caddy as a Reverse Proxy
Set up Caddy as a reverse proxy with automatic HTTPS, load balancing, WebSocket passthrough, reusable snippets, and header control — no certbot required.