Skip to content
mrpbennettPublic

Repository files navigation

Wife approved HomeOps driven by Kubernetes and GitOps using ArgoCD

Image used with permission from k8s-at-home

talos GitHub Last Commit Home Operations Discord

My Home Operations Repository :octocat:

... managed with ArgoCD, Renovate and GitHub Actions 🤖


💡 Overview

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.

The purpose here is to learn Kubernetes, while practising GitOps

🌱 Kubernetes

Installation

My Kubernetes environment is deployed with Talos Linux, with MetalLB providing LoadBalancer support.

GitOps

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.

Cluster Naming

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

Directories

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 under helm.valuesObject, so there are no separate values.yaml files.
  • 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.

Bootstrapping a new 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 argocd to match the generated Application, so Argo CD takes over the existing resources instead of duplicating them.
  • httproute is 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.clusterCredentials in the appset registers this cluster as portland, 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.

Tech stack

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

🌎 DNS

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.


🔧 Hardware

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

⭐ Stargazers

Star History Chart


🤝 Gratitude and Thanks

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.