Provisioning¶
The cluster is provisioned using OpenTofu, an open-source Terraform-compatible IaC tool. All infrastructure state is stored in PostgreSQL.
NAS Dependencies¶
The following services must be running on the NAS before cluster provisioning can begin. These are not managed by OpenTofu — they are preconditions.
| Service | Why it's needed |
|---|---|
| Vault | OpenTofu reads secrets from Vault during provisioning (Gitea token, OIDC client ID, ESO token). Without it, tofu plan fails immediately. |
| PostgreSQL | Stores OpenTofu state. Required to initialize the backend. |
| Gitea | The repository that Flux will watch must exist and be accessible before taloser-flux runs. |
| Pi-hole | DNS must resolve internal hostnames (e.g. vault.hdhomelab.com, gitea.hdhomelab.com, pve2.hdhomelab.com) for providers and modules to connect. |
| MinIO | S3-compatible object storage used by cluster workloads. Runs on the NAS; buckets are managed by the buckets OpenTofu deployment. |
Rebuilding from scratch
Bring up these NAS services first, then proceed with cluster provisioning.
Repository Structure¶
tofu/
├── tf-deploy/ # Deployment configurations (what to create)
│ ├── noah/ # Kubernetes cluster
│ ├── psql/ # PostgreSQL databases and roles
│ ├── buckets/ # MinIO S3 buckets
│ └── authentik/ # Authentik SSO applications
└── tf-modules/ # Reusable modules (how to create it)
├── taloser/ # Talos VMs on Proxmox
├── taloser-k8s/ # Core k8s infrastructure (Cilium, ESO)
├── taloser-flux/ # Flux CD bootstrap
├── psql/ # PostgreSQL role/database management
├── minio-bucket/ # MinIO bucket management
├── authentik-oidc/ # Authentik OIDC provider
└── authentik-ldap/ # Authentik LDAP provider
Cluster Provisioning (tf-deploy/noah)¶
The cluster deployment is split into three modules that run in sequence:
graph LR
A[taloser\nVMs + Talos config] --> B[taloser-k8s\nCilium + ESO + VPA]
B --> C[taloser-flux\nFlux CD bootstrap]
Each module depends on the previous one being fully ready — this isn't just ordering, it's a hard bootstrap dependency chain:
Why this order?
- taloser must run first because there is no cluster until VMs exist and Talos is configured. Subsequent modules need the kubeconfig output from this step to connect to the API server.
- taloser-k8s must run before Flux because it installs the things Flux-managed workloads depend on at reconcile time: Cilium (without it, pods can't communicate and nothing reaches Ready), ExternalSecrets Operator (without it, any
ExternalSecretresource Flux creates would error immediately), and VPA (CRDs must exist before Flux appliesVerticalPodAutoscalerobjects in app manifests). - taloser-flux runs last because it hands control to GitOps. Once Flux is bootstrapped it begins reconciling the repository, so the platform must already be stable before that happens.
Provisions Proxmox VMs and applies Talos Linux configuration.
- Downloads the Talos image from factory.talos.dev to each Proxmox node
- Creates VMs with the specified CPU, RAM, disks, and MAC addresses
- Applies Talos machine configs (control plane / worker)
- Outputs kubeconfig and Talos client config
Node configuration is defined in locals.tf — see Cluster for the full node table.
Installs core Kubernetes infrastructure via Helm:
- Cilium — CNI, L2 LoadBalancer, Gateway API, Ingress Controller
- ExternalSecrets Operator — syncs secrets from Vault into Kubernetes
- Vertical Pod Autoscaler — installs VPA controller and CRDs; by running here, all Flux-managed
VerticalPodAutoscalerobjects reconcile on first apply without any ordering dependency
Bootstraps Flux CD onto the cluster:
- Installs Flux controllers (including image-reflector and image-automation)
- Configures Flux to watch the
fluxbranch of the Gitea repository - Uses a Gitea token from Vault for authentication
State Backend¶
State is stored in PostgreSQL on the NAS. Initialize with:
Providers¶
| Provider | Purpose |
|---|---|
bpg/proxmox |
Create and manage Proxmox VMs |
fluxcd/flux |
Bootstrap Flux CD |
go-gitea/gitea |
Configure Gitea repository for Flux |
hashicorp/vault |
Read secrets during provisioning |
hashicorp/kubernetes |
Apply k8s resources |
hashicorp/helm |
Install Helm charts |
Other Deployments¶
psql (tf-deploy/psql)¶
Manages PostgreSQL databases and roles on the shared NAS database. Uses the psql module to create per-app databases, users, and grants. Credentials are generated at provision time and written directly to Vault — no init containers or manual setup needed.
See PostgreSQL Provisioning for the full pattern, module design, and how to add a new database.
buckets (tf-deploy/buckets)¶
Manages MinIO S3 buckets using the minio-bucket module.
authentik (tf-deploy/authentik)¶
Manages Authentik SSO applications using the authentik-oidc and authentik-ldap modules — creates OIDC providers and LDAP configurations for apps that need SSO.
Running a Plan¶
cd tofu/tf-deploy/noah
tofu init -backend-config=backend.pg.tfbackend
tofu plan -out plan.out
tofu apply plan.out