portfolio Anshul Bisen
ask my work

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.

The kind of infrastructure that teaches you by breaking.

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 infrastructure mess that made the lesson stick.

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.

Terminal window
# Install ArgoCD with minimal footprint
kubectl create namespace argocd
kubectl 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 RAM

The 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/v1alpha1
kind: ApplicationSet
metadata:
name: homelab-services
namespace: argocd
spec:
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: true

Adding 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.

Homelab, but treated like a real environment.

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.