3.3 KiB
CLAUDE.md
This file provides guidance to Claude Code when working anywhere in this repository. Each stack directory (01-proxmox-infra, 02-k8s-bootstrap, etc.) has its own CLAUDE.md with stack-specific commands and architecture — Claude Code loads both this file and the stack's file automatically when working inside a stack directory.
Project overview
Pulumi (TypeScript) IaC for a Proxmox-based k3s homelab. Independent Pulumi projects ("stacks"), each with its own Pulumi.yaml/package.json, deployed in order and linked via pulumi.StackReference:
| # | Directory | Pulumi stack | Purpose |
|---|---|---|---|
| 1 | 01-proxmox-infra |
proxmox-infra |
Provisions k3s VMs + DHCP on Proxmox/pfSense |
| 2 | 02-k8s-bootstrap |
k8s-bootstrap |
Installs k3s on the VMs over SSH |
| 3 | 03-k8s-infra |
k8s-infra |
Cluster infra: storage, cert-manager, kube-vip |
| 4 | 04-k8s-apps |
k8s-apps |
Application workloads (e.g. heimdall) |
| 5 | 05-monitoring |
monitoring |
kube-prometheus-stack (Prometheus + Grafana) |
All stacks use the Pulumi stack name dev.
Deployment order & StackReferences
proxmox-infra → k8s-bootstrap → k8s-infra
↘ k8s-apps → monitoring
k8s-bootstrapreads VM IDs, Proxmox credentials, and the SSH private key fromproxmox-infravia StackReference.k8s-infra,k8s-apps, andmonitoringall readkubeconfigfromk8s-bootstrapvia StackReference (requireOutput("kubeconfig")) — never setkubeconfigas Pulumi config in these stacks.monitoringadditionally readsdomainfromk8s-appsvia StackReference.- StackReference format:
${pulumi.getOrganization()}/<stack-name>/dev.
A stack's outputs must exist (i.e. it must have been deployed) before any downstream stack referencing it will succeed.
Shared conventions
- TypeScript:
strict: true,target: es2024,module/moduleResolution: nodenext,noImplicitReturns,noFallthroughCasesInSwitch— functions need explicit return types where TS can't infer them. - Secrets: read via
config.requireSecret(), never via environment variables or hardcoded values. Pulumi.dev.yaml: gitignored (contains the encryption salt) — never delete it or secrets become unrecoverable. Encrypted withPULUMI_CONFIG_PASSPHRASE.- Backend: self-hosted Postgres state backend, set via
PULUMI_BACKEND_URL. - Don't add a co-author when committing to git.
Common commands (run from a stack directory)
npm install
pulumi preview
pulumi up --yes
pulumi destroy
pulumi stack output
pulumi config
Requires PULUMI_BACKEND_URL and PULUMI_CONFIG_PASSPHRASE in the environment (or pulumi login).
CI/CD (Gitea Actions)
One workflow per stack: .gitea/workflows/0N-deploy-<stack>.yaml, scoped to changes under that stack's directory.
- Pull request →
main:pulumi previewonly. 01/02(VM provisioning / cluster bootstrap — riskier to automate): deploy only via manualworkflow_dispatch.03–05: deploy on push tomainorworkflow_dispatch—pulumi refreshthenpulumi up.- Each workflow restores
Pulumi.dev.yamlfrom a base64-encoded Gitea secret (<STACK>_PULUMI_DEV_YAML). After changing local config, re-encode and update the secret:base64 -w 0 0N-<stack>/Pulumi.dev.yaml