Technology Stack
This is the complete inventory of what runs the lab, from bare hardware up to the applications on top. Versions are pinned in code (mostly the Dockerfile and the Helm chart references) and kept current by Renovate, so they are not repeated here.
Infrastructure layers
Section titled “Infrastructure layers”The lab is built in five layers, each covered by a tutorial.
| Layer | Technology | Role |
|---|---|---|
| 0. Hardware | Low-power x86_64 mini PCs | Three or more nodes. Quiet, cheap to run. |
| 1. Operating system | Debian | Installed unattended from a preseed.cfg. |
| 2. Virtualisation | Proxmox VE | Type-1 hypervisor, configured with Ansible. |
| 3. Storage | TrueNAS | Runs as a Proxmox VM, serves storage over NFS/SMB network shares. |
| 4. Kubernetes | Talos Linux | Immutable, API-driven nodes provisioned as VMs. |
Proxmox earns its place by letting one box run both Kubernetes nodes and a storage VM, and by making clusters cheap to rebuild while iterating. Talos is the opposite of a general-purpose OS: no SSH, no shell, no package manager, just an API. That trade buys a small attack surface and fully declarative nodes.
Runner toolchain
Section titled “Runner toolchain”There is no toolchain to install locally. A single container image (built by the Dockerfile) carries everything the layers need:
| Tool | Used for |
|---|---|
| OpenTofu | Terraform-compatible IaC engine. |
| Terragrunt | DRY wrapper around OpenTofu, wires up remote state. |
| Ansible | Proxmox cluster setup and Kubernetes bootstrap. |
| Task | Task runner that drives the workflows. |
| talosctl | Talos node and cluster management. |
| kubectl | Kubernetes CLI. |
| Helm | Renders charts (through Kustomize). |
| Kustomize | Manifest assembly, with --enable-helm. |
| jq | JSON wrangling in scripts. |
A second image variant adds task-ui for a browser-based view of the tasks. There is deliberately no argocd or bws CLI: Argo CD is driven through kubectl, and Bitwarden access happens in-cluster through External Secrets.
Infrastructure as code
Section titled “Infrastructure as code”OpenTofu provisions the VMs and Terragrunt keeps the configuration DRY, with remote state in a Cloudflare R2 bucket. The OpenTofu providers in use are:
bpg/proxmoxfor Proxmox VMssiderolabs/talosfor Talos machine config and bootstrapmaxlaverse/bitwardenfor reading secrets during bootstraphashicorp/kubernetes,hashicorp/helm,hashicorp/local,hashicorp/time,hashicorp/null
Layer 2 uses the lae.proxmox Ansible role for the core cluster build, alongside custom roles for the rest: tailscale-bootstrap, btrfs-subvolume, proxmox-permissions, pve-resource-mappings (PCI passthrough), cert, and a handful of package and host hygiene roles. The same playbook also creates the Proxmox API user and token that Terragrunt later uses.
Kubernetes platform
Section titled “Kubernetes platform”Everything inside the cluster is GitOps-managed. Argo CD watches this repository and applies it, using ApplicationSets to generate applications from the directory tree. Components live under kubernetes/cluster/active, with retired ones kept under inactive for reference.
Networking and ingress
Section titled “Networking and ingress”Ingress has been through three generations. It started on ingress-nginx, then moved to the Gateway API fronted by Istio (written up on the Tower of Kubes blog). It now runs Envoy Gateway, adopted for its built-in OIDC support. Istio still lives under kubernetes/cluster/inactive for reference to the previous setup.
| Component | Role |
|---|---|
| Cilium | eBPF-based CNI: pod networking, network policy, and Hubble observability. |
| Envoy Gateway | Current ingress, a Gateway API implementation. Runs a Coraza WAF policy. |
| CoreDNS | In-cluster DNS. |
| external-dns | Publishes DNS records to Cloudflare, including a dynamic-DNS target. |
| Tailscale operator | Exposes services and node access over the Tailnet. |
Certificates
Section titled “Certificates”cert-manager issues TLS certificates from Let’s Encrypt using the ACME DNS-01 challenge through Cloudflare. The letsencrypt-prod and letsencrypt-staging ClusterIssuers and the certificates themselves live in cert-resources.
Secrets
Section titled “Secrets”The External Secrets Operator syncs secrets from Bitwarden Secrets Manager into the cluster. The single bootstrap token (BWS_ACCESS_TOKEN) is all that is needed to unlock the rest, so no application secrets are ever committed to Git.
Database operators
Section titled “Database operators”| Operator | Provides |
|---|---|
| CloudNativePG | PostgreSQL clusters. |
| mariadb-operator | MariaDB databases. |
Other operators
Section titled “Other operators”| Operator | Provides |
|---|---|
| kaniop | Kanidm, the cluster’s identity provider. |
Storage
Section titled “Storage”Longhorn provides the default storage class, with replicated block volumes for application state. The csi-driver-smb and csi-driver-nfs drivers mount shares from the TrueNAS VM for bulk data and media.
Platform add-ons
Section titled “Platform add-ons”| Add-on | Role |
|---|---|
| metrics-server | Resource metrics for autoscaling and kubectl top. |
| node-feature-discovery | Labels nodes by hardware capability. |
| intel-gpu-resource-driver | Exposes Intel iGPUs for hardware transcoding. |
| talos-cloud-controller-manager | Node lifecycle integration for Talos. |
| spegel | Peer-to-peer OCI registry mirror to speed up image pulls. |
| reloader | Restarts workloads when their config or secrets change. |
| reflector | Mirrors secrets and config maps across namespaces. |
| tuppr | Automates Talos and Kubernetes upgrades. |
| monitoring | VictoriaMetrics K8s Stack. |
Applications
Section titled “Applications”The self-hosted apps live under kubernetes/cluster/active/apps; that directory is the source of truth as the set changes. As of writing it includes:
- Media: Immich (photos), a Jellyfin-based media server, the *arr stack (
servarr), Seerr (requests), qBittorrent, MeTube, and Pinepods (podcasts). - Knowledge and productivity: Outline (wiki), Mealie (recipes), Miniflux (RSS), Karakeep (bookmarks), and a books server.
- Developer tooling: Forgejo (Git forge), OpenGist (snippets), Actions Runner Controller (CI runners), and a Renovate operator.
- Utilities: Homepage (dashboard), IT-Tools, CyberChef, OmniTools, BentoPDF, and Transmute.
Design principles
Section titled “Design principles”A few rules shaped most of these choices, and the design decisions page goes deeper on the decisions:
- Open source wherever possible
- Everything declarative, with Git as the single source of truth.