Wife approved HomeOps driven by Kubernetes and GitOps using ArgoCD
... managed with ArgoCD, Renovate and GitHub Actions 🤖
This is a mono repository for my home infrastructure and Kubernetes nodes. I try to adhere to Infrastructure as Code (IaC) and GitOps practices using tools like Kubernetes, ArgoCD, Renovate and GitHub Actions.
I have a HA setup running 3 Dell OptiPlex 7060 Micros (6-core, 16GB) with Talos Linux, all acting as control planes that also accept workloads.
My Kubernetes environment is deployed with Talos Linux, with MetalLB providing LoadBalancer support.
ArgoCD watches the kubernetes directory (see structure below) and changes the cluster to match the state of this Git repository. A single root application, kubernetes/argo-root.yaml, syncs every ApplicationSet in kubernetes/appsets/. Each ApplicationSet generates the Application that deploys one app, following the app of apps pattern.
Clusters use short, Dorset-themed names rather than encoding distro or environment info into the directory name. This keeps paths concise and avoids churn if the underlying distro changes.
| Cluster | Environment | Description |
|---|---|---|
| portland | Production | Primary workload cluster |
This Git repository contains the following directories:
📁 kubernetes
├── argo-root.yaml # root app: syncs everything in appsets/
├── 📁 appsets # one ApplicationSet per app
│ ├── 📁 argocd-helm # Argo CD manages itself
│ ├── 📁 atuin # manifest app -> clusters/portland/apps/atuin
│ ├── 📁 CLUSTER # deploys clusters/portland/CLUSTER
│ ├── 📁 vault-helm # Helm app, values inline
│ └── ...
└── 📁 clusters
└── 📁 portland # production cluster
├── 📁 apps # plain manifests for non-Helm apps
│ └── 📁 app
│ ├── deployment.yaml
│ ├── service.yaml
│ └── ...
└── 📁 CLUSTER # cluster-wide manifests
├── 📁 cluster-role-bindings
├── 📁 gateway-api # GatewayClass, shared Gateway, wildcard cert
├── 📁 namespaces
└── 📁 secrets # VaultStaticSecrets, synced from Vault
📁 terraform
└── 📁 vault # Vault config + secrets as code (see its README)Each ApplicationSet deploys one app in one of two ways:
- Helm apps (
*-helm) name the chart directly. Their values live inline in the ApplicationSet underhelm.valuesObject, so there are no separatevalues.yamlfiles. - Manifest apps point at a directory of plain manifests:
source:
repoURL: "https://github.com/mrpbennett/home-ops.git"
path: "kubernetes/clusters/{{.cluster}}/apps/{{.app_name}}"The cluster generator value (portland) picks both the folder under clusters/ and the Argo CD destination cluster.
Argo CD manages itself through appset-helm-argocd.yaml, so on a fresh cluster it's installed once with Helm, using the same chart version, release name and values as that appset. Argo CD then takes over the existing resources when the appset syncs.
APPSET=kubernetes/appsets/argocd-helm/appset-helm-argocd.yaml
# 1. Install Argo CD with the appset's own values and chart version
yq '.spec.template.spec.sources[0].helm.valuesObject' $APPSET > /tmp/argocd-values.yaml
helm install argocd argo-cd \
--repo https://argoproj.github.io/argo-helm \
--version "$(yq '.spec.template.spec.sources[0].targetRevision' $APPSET)" \
-n argocd --create-namespace \
-f /tmp/argocd-values.yaml \
--set server.httproute.enabled=false # Gateway API CRDs don't exist yet
# 2. Log in. There's no LoadBalancer IP until MetalLB is running, so port-forward
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d
kubectl -n argocd port-forward svc/argocd-server 8080:80 # http://localhost:8080, user admin
# 3. Hand everything over to GitOps
kubectl apply -f kubernetes/argo-root.yaml
# 4. Once the "argocd" Application shows Synced, delete Helm's record of the release
kubectl -n argocd delete secret -l owner=helm,name=argocd- The release name must be
argocdto match the generated Application, so Argo CD takes over the existing resources instead of duplicating them. httprouteis disabled only for the first install, because the HTTPRoute resource type doesn't exist until Envoy Gateway installs it. Argo CD adds the HTTPRoute afterwards.- After step 4, never run
helm upgrade. Upgrade Argo CD by changing the appset in git. configs.clusterCredentialsin the appset registers this cluster asportland, the destination every appset targets.- Expect sync errors at first. Apps retry until the resource types and namespaces they depend on exist, so the cluster converges in about 5–15 minutes.
- Vault needs manual steps. It starts sealed, so initialise and unseal it, then apply the Terraform; until then, the apps that need its secrets wait.
The Vault steps, and how sync ordering works, are in terraform/vault/README.md.
| Name | Description |
|---|---|
| ArgoCD | GitOps tool built to deploy applications to Kubernetes |
| Argo Workflows | Workflow management to help with CronWorkflows |
| Cert Manager | Certificate management |
| Docker Registry | Private container registry |
| Envoy Gateway | API Gateway |
| Grafana | Observability platform |
| Helm | The package manager for Kubernetes |
| Talos Linux | Kubernetes OS |
| Keycloak | OIDC provider |
| Kubernetes | Container-orchestration system, the backbone of this project |
| Loki | Log aggregation system |
| ExternalDNS | External DNS server configuration |
| NGINX | Kubernetes Ingress Controller |
| MetalLB | Kubernetes load balancer |
| Prometheus | Systems monitoring and alerting toolkit |
| SeaweedFS | Data Warehouse Object Storage |
| Trino | Fast distributed SQL query engine |
| Tailscale | Secure connectivity |
In my cluster there is one instance of ExternalDNS running. This syncs to a LXCPi5 running Adguard Home for syncing local DNS records. This setup allows me to create dns records with valid certification via cert-manager and cloudflares API.
| Device | Count | CPU | OS Disk Size | Data Disk Size | Ram | Operating System | Purpose |
|---|---|---|---|---|---|---|---|
| Dell 7060 micro | 3 | 6-core | 1TB NVMe | - | 16GB | Talos Linux | Control planes that run workloads |
| Dell 7060 micro | 1 | - | 256GB SSD | 1TB NVMe | 16GB | Proxmox | Hypervisor |
Thanks to all the people who donate their time to the Home Operations Discord community. Be sure to check out kubesearch.dev for ideas on how to deploy applications or get ideas on what you may deploy.
