ArgoCD on a single-node k3s cluster: overkill or exactly right
Everyone told me ArgoCD on a single-node homelab was absurd. GitOps on a single node means I can rebuild from a Git repo in under 10 minutes.
When I mentioned in a Discord server that I was running ArgoCD on my single-node k3s homelab, the response was unanimous: overkill. “Just use kubectl apply.” “ArgoCD is for teams with 50 microservices.” “You are adding complexity for the sake of adding complexity.” I understand the instinct. ArgoCD is enterprise-grade continuous delivery software. My homelab runs twelve services on a mini PC in my closet. The scale mismatch is obvious. But scale is the wrong lens. The right lens is recoverability.
A lot of my month-one leadership came through infrastructure choices that looked small from the outside. It also builds on what I learned earlier in “Docker multi-stage builds that actually make your images small.” I was building the muscle memory that later fed the infrastructure and ctrlpane projects at home: reproducible defaults, cheap feedback loops, and enough observability that I did not need to guess under pressure.
The Disaster Recovery Argument
Two months before installing ArgoCD, my k3s node’s SSD died. No warning, no SMART alerts, just a dead drive on a Tuesday morning. I spent the entire day rebuilding the cluster from memory and shell history. Reinstall k3s, recreate namespaces, reapply manifests I found scattered across three directories, recreate secrets from a password manager, and hope I did not forget anything. It took nine hours and I missed two services that stayed offline for another day because I forgot they existed.
That experience was the direct motivation for GitOps. If every piece of cluster state lives in a Git repository, and a tool continuously reconciles the cluster to match the repository, then disaster recovery is: install k3s, install ArgoCD, point it at the repo, wait ten minutes. That is not overkill. That is the minimum viable backup strategy for a cluster you care about.
The Minimal ArgoCD Installation
ArgoCD’s default installation is heavy. It deploys a Redis instance, a full web UI, a metrics server, and several controllers that consume about 600 MB of RAM. On a 16 GB node running twelve application pods, that is a significant overhead. I used the minimal installation that strips it down to the essentials.
# Install ArgoCD with minimal footprintkubectl create namespace argocdkubectl apply -n argocd \ -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/core-install.yaml
# Resource usage after minimal install:# argocd-application-controller: ~120 MB RAM# argocd-repo-server: ~80 MB RAM# Total: ~200 MB RAMThe core installation skips the web UI, the API server for external access, and the Redis cache. Total RAM usage is about 200 MB instead of 600 MB. I manage ArgoCD entirely through the CLI and YAML manifests. The web UI is nice for exploration but unnecessary for a single-operator homelab.
The ApplicationSet Pattern
Instead of creating individual ArgoCD Application resources for each of my twelve services, I use an ApplicationSet with a Git generator. The generator discovers directories in my homelab repository and creates an ArgoCD Application for each one automatically.
apiVersion: argoproj.io/v1alpha1kind: ApplicationSetmetadata: name: homelab-services namespace: argocdspec: generators: - git: repoURL: https://github.com/me/homelab.git revision: main directories: - path: services/* template: metadata: name: '{{path.basename}}' spec: project: default source: repoURL: https://github.com/me/homelab.git targetRevision: main path: '{{path}}' destination: server: https://kubernetes.default.svc namespace: '{{path.basename}}' syncPolicy: automated: prune: true selfHeal: trueAdding a new service to the cluster is now: create a directory in the Git repo with Kubernetes manifests, push, and ArgoCD creates the namespace and deploys everything automatically. Removing a service is: delete the directory and push. The prune policy cleans up the Kubernetes resources. No kubectl commands, no SSH access, no manual steps.
The Sync Workflow
Every change to the cluster flows through a Git commit. I never run kubectl apply directly. The workflow is deliberately boring.
- Edit a manifest or Helm values file in the Git repository.
- Commit and push to main.
- ArgoCD detects the change within three minutes (configurable poll interval).
- ArgoCD applies the diff to the cluster.
- If the sync fails, ArgoCD retries with exponential backoff and sends a webhook to my monitoring.
- The self-heal policy reverts any manual changes made outside of Git within the next sync cycle.
The self-heal policy is the hidden gem. If I SSH into the node and run kubectl delete pod out of frustration at 2am, ArgoCD recreates it within three minutes because the desired state in Git says the pod should exist. The Git repository is always the truth. The cluster is a reflection of that truth.
The Rebuild Test
After setting up ArgoCD, I ran the test that motivated the entire project: I wiped my k3s cluster and rebuilt it from scratch.
- Reinstall k3s: 30 seconds.
- Install ArgoCD core: 45 seconds.
- Apply the ApplicationSet: 10 seconds.
- Wait for all 12 services to sync: 7 minutes.
- Restore secrets from sealed-secrets backup: 2 minutes.
- Total time from bare metal to fully operational cluster: under 10 minutes.
The nine-hour rebuild is now a ten-minute operation. Every service, every configuration, every ingress rule is declaratively defined in Git. The only manual step is restoring encrypted secrets. That alone justified ArgoCD on a homelab, regardless of scale.
The builder phase was less glamorous than people imagine. It was mostly a series of stubborn, unfashionable choices that kept future-me out of 2 a.m. incident calls. I still make the same kind of choices inside infrastructure and ctrlpane.
ArgoCD on a single node is not overkill. It is a backup strategy that happens to also automate deployments. If you cannot rebuild your homelab from a Git repo in under 15 minutes, you do not have infrastructure as code. You have infrastructure as memory.