A GitOps-managed Kubernetes homelab running on a Raspberry Pi, powered by Argo CD
Single-node K3s cluster managed entirely through Argo CD using the app-of-apps pattern. Changes merged to main are automatically synced to the cluster using a GitHub webhook.
- Orchestration: K3s – lightweight Kubernetes
- GitOps: Argo CD
- Networking: Cilium (eBPF-based CNI) + Tailscale (mesh VPN, GitOps-managed ACLs)
- Storage: Longhorn with Backblaze B2 backups
- Monitoring: Grafana Cloud (Prometheus, Loki, Tempo) – alerting & OnCall managed via Terraform
- DNS: Pi-hole
- Secrets: Sealed Secrets
- Infrastructure as Code: Terraform (Grafana Cloud) + Ansible (node config)
- Dependency updates: Renovate
| Application | Type | Version |
|---|---|---|
| argo-cd | Helm | v9.7.1 |
| cilium | Helm | v1.19.1 |
| discord-bot | Helm | v0.23.0 |
| heartbeat | Git | - |
| k8s-monitoring | Helm | v4.3.2 |
| longhorn | Helm | v1.12.0 |
| pi-hole | Helm | v2.38.0 |
| sealed-secrets | Helm | v2.18.6 |
| system-upgrade | Git + Kustomize | v0.20.1 |
| tailscale | Helm | v1.98.9 |
.
├── apps/ # Argo CD application manifests
│ └── <app>/
│ ├── application.yaml # Argo CD Application resource
│ ├── secret.yaml # Sealed Secret (if needed)
│ └── resources/ # Additional resources (if needed)
├── ansible/ # Node provisioning playbooks
│ ├── playbook.yml
│ ├── inventory.yml
│ └── templates/
├── terraform/
│ └── grafana/ # Grafana Cloud config (contact points, OnCall, notification policies)
├── policy.hujson # Tailscale ACL policy (GitOps-managed)
├── scripts/ # Utility scripts
│ ├── seal-secrets.sh
│ ├── extract-secrets.sh
│ └── generate-apps-table.sh
├── secrets/ # Raw secrets (git-ignored)
└── root-application.yaml # Root Argo CD Application
- A K3s node (provisioned with the Ansible playbook in
ansible/) kubectlconfigured for the clusterkubesealCLI for secret management
-
Provision the node:
$ cd ansible && ansible-playbook playbook.yml
-
Install the root application:
$ kubectl apply -f root-application.yaml
Argo CD will discover and sync all applications in
apps/automatically.
To seal secrets for the cluster:
$ ./scripts/seal-secrets.shTo extract raw secrets from a running cluster:
$ ./scripts/extract-secrets.shNote
Raw secrets are stored in secrets/ and are git-ignored. Only sealed (encrypted) versions are committed.
This project contains code generated by Large Language Models (LLMs), under human supervision and proofreading.