- Cluster host
- Intel NUC running Proxmox VE
- Out-of-band
- JetKVM on the NUC, including power control
- Cluster
- Talos Linux: one control-plane VM and four workers
- Workers
- Three Raspberry Pi 4, plus one VM on the NUC for database workloads
- Storage
- NFS from a dedicated container; node-local volumes on the database VM
- Second site
- Raspberry Pi 3 running Docker Compose
- Cloud edge
- Oracle Cloud ARM instance running NixOS
- Gateway and switching
- Ubiquiti UniFi, with VLANs and zone-based firewall rules declared in Terraform
- Cluster networking
- Cilium, replacing kube-proxy; Gateway API for ingress
- DNS
- Blocky for filtering, Unbound for DNSSEC validation and DoT upstream
- Tunnels
- WireGuard between the edge and home; Tailscale for CI runners and remote access
- Public DNS and certificates
- Cloudflare, Let's Encrypt DNS-01
- Infrastructure
- Terraform for networking and machines; Ansible and Nix for VM and LXC configuration
- Delivery
- GitHub Actions, ArgoCD, Renovate
- Secrets
- HashiCorp Vault. Action runners authenticate with a GitHub OIDC token to get a short-lived token scoped to that workflow, the cluster authenticates with a ServiceAccount token, and SSH host and client certificates are signed by Vault CAs.
- Policy
- Kyverno at admission in enforce mode, with the same policy set and exceptions run against every rendered chart in CI. Tetragon for eBPF runtime tracing. Trivy scans code in CI.
- Observability
- Prometheus and Alertmanager, Loki for logs, Alloy for collection, Grafana, Gatus
Storage
One goal when making architectural decisions was minimising SD card wear. That is why the default storage class is NFS, from an HDD-backed LXC on the NUC. The databases, which need block storage, run on a tainted worker that is also a VM on the Intel NUC, backed by an SSD.
These are temporary solutions. Once I get a new NAS, both move to Democratic CSI on TrueNAS.
DNS
Clients have a DoH profile pointing them at a domain that resolves to the public edge. HAProxy does not terminate the TLS; instead it relays the encrypted traffic across the WireGuard tunnel, and the handshake completes against the homelab.
Blocky terminates the session and filters using blocklists, then hands off to Unbound for DNSSEC validation. Unbound's upstream query leaves over DoT to 1.1.1.1, inside a ProtonVPN tunnel.
Cloudflare answers the query but sees a ProtonVPN exit address. ProtonVPN carries the traffic but the payload is DoT, so it sees only that a DNS connection happened. Neither side holds both halves, which is the same separation Oblivious DNS was built to provide, but self-hosted.
A canary name serves as the check. Public DNS points it at a dead IP, and a custom zone in Blocky points it somewhere real, so it only resolves correctly when the query actually went through this path.
- applications
- 17 reconciled by ArgoCD
- platform
- 5 charts installed by Terraform
- charts
- 17 wrapper charts, 17 upstream versions pinned
Platform
- argo-cd argocd argo-cd 10.11.1
Installed by Terraform. From then on it deploys everything else from this repository.
- argocd-config argocd
ApplicationSet: Reconciles the cluster against this repository.
- cert-manager cert-manager cert-manager v1.21.3
Issues the wildcard certificate from Let's Encrypt over a DNS-01 challenge.
- cilium kube-system cilium 1.20.4
Replaces kube-proxy as CNI, advertises LoadBalancer addresses, Gateway API for HTTPRoutes.
- cluster-namespaces
Namespaces and their Pod Security Admission labels.
- cnpg cnpg-system cloudnative-pg 0.29.1
Postgres operator for shared DB.
- crossplane crossplane-system crossplane 2.4.3
Provisions Postgres databases and PgBouncer poolers and writes the credentials to a secret.
- csi-driver-nfs kube-system csi-driver-nfs 4.13.6
Backs the default storage class with the NFS export.
- external-secrets external-secrets external-secrets 2.14.0
Pulls secrets from Vault for Services. The cluster authenticates with a short-lived ServiceAccount token.
Networking
- dns dns
Blocky for filtering and caching, with an Unbound sidecar for DNSSEC validation, prefetching and DoT forwarding upstream.
- gateway-external-routes gateway
Gateway API routes for services hosted outside the cluster.
- tailscale tailscale tailscale-operator 1.104.2
Tailscale operator. GitHub Actions runners join the tailnet to reach the cluster.
Identity
- authentik authentik authentik 2026.8.5
Single sign-on for the services.
Policy
- kyverno kyverno kyverno-policies 3.9.2
The policy set for baseline pod hardening, enforced at admission.
- kyverno-operator kyverno kyverno 3.9.2
The Kyverno admission controller. CI runs the same policy set against rendered charts before merge.
- tetragon tetragon tetragon 1.7.1
eBPF runtime observability. The tracing policies are observe-only.
Observability
- alloy monitoring alloy 1.13.1
Telemetry collection. Scrapes nodes, Proxmox syslog and UniFi syslog.
- gatus gatus gatus 1.5.0
Black-box health checks and alerting.
- kube-prometheus-stack monitoring kube-prometheus-stack 92.3.1
Prometheus, Alertmanager and Grafana.
- loki monitoring loki 7.3.0
Log storage, queried from Grafana.
Storage
- local-path-provisioner local-path-storage local-path-provisioner 0.0.37
Node-local volumes on the database node. Everything else uses the NFS storage class.
Applications
- it-tools it-tools
A self-hosted collection of small conversion and encoding utilities.