Files
homelab-infrastructure-as-code/README.md
T
kasun c436075c1b
Deploy Proxmox Infra / Pulumi Preview (push) Has been skipped
Deploy Proxmox Infra / Pulumi Deploy (push) Successful in 46s
fix: restore stack config step in workflow, add README
Restore Stack Config steps were incorrectly removed in the previous
commit — Pulumi.dev.yaml is gitignored so CI cannot access it without
decoding it from the PULUMI_DEV_YAML secret at runtime.

Also adds README.md documenting the project, stack layout, setup
steps, CI/CD secrets, and roadmap.
2026-05-28 02:59:23 +02:00

148 lines
5.5 KiB
Markdown

# Homelab Infrastructure as Code
A Pulumi-based IaC template for managing a Proxmox homelab. The goal is to replace manual GUI configuration and ad-hoc YAML stacks (LXC, VM, Docker, etc.) with version-controlled, reproducible infrastructure — starting with a highly available k3s cluster across multiple Proxmox nodes.
This repo is intentionally abstract: credentials are never hardcoded, making it easy to fork and adapt as a template for your own homelab.
## Why IaC for a homelab?
- **Reproducibility** — rebuild your entire environment from scratch with a single command
- **Version history** — every change is tracked in git; roll back at any time
- **Auditability** — diff infrastructure changes before applying them (`pulumi preview`)
- **Automation** — CI/CD handles deploys; no manual SSH into nodes
- **Portability** — swap node names, datastores, or credentials without touching logic
## Repository layout
```
.
├── proxmox-infra/ # Pulumi TypeScript stack — VMs & LXC on Proxmox
│ ├── index.ts # All Pulumi resources
│ ├── Pulumi.yaml # Stack project definition
│ └── Pulumi.dev.yaml # Encrypted stack config (gitignored)
├── .gitea/
│ └── workflows/
│ └── deploy-proxmox-infra.yaml # Gitea Actions CI/CD pipeline
```
## Current stack: `proxmox-infra`
Provisions a 5-node k3s cluster spread across two Proxmox hosts (`pve` and `pve-bckp`, third bare metal host to be added later for actual parity):
| VM name | Role | Proxmox node |
| ------------ | ------ | ------------ |
| k3s-master-1 | master | pve |
| k3s-master-2 | master | pve |
| k3s-worker-1 | worker | pve |
| k3s-master-3 | master | pve-bckp |
| k3s-worker-2 | worker | pve-bckp |
Each node is a full clone of an Ubuntu Noble (24.04) cloud-image template, with cloud-init injecting hostname, user credentials, and SSH key at boot.
**Tech stack:**
- [Pulumi](https://www.pulumi.com/) with TypeScript
- [`@muhlba91/pulumi-proxmoxve`](https://github.com/muhlba91/pulumi-provider-proxmoxve) v8.x community provider
- Self-hosted Pulumi state backend (PostgreSQL)
- Gitea Actions for CI/CD
## Prerequisites
- [Pulumi CLI](https://www.pulumi.com/docs/install/) installed
- Node.js 18+ and npm
- Access to a Proxmox node with an API token
- A self-hosted Pulumi state backend (PostgreSQL connection string)
- Gitea instance for CI/CD (optional for local use)
## Getting started
### 1. Clone and install
```bash
git clone <your-repo-url>
cd proxmox-infra
npm install
```
### 2. Configure credentials
All secrets are stored as encrypted Pulumi config values — never in plain environment variables or committed files.
```bash
# Set Proxmox API credentials
pulumi config set --secret pve1Endpoint https://<proxmox-host-1>:8006
pulumi config set --secret pve1ApiToken <user>@pam!<token-id>=<uuid>
pulumi config set --secret pve2Endpoint https://<proxmox-host-2>:8006
pulumi config set --secret pve2ApiToken <user>@pam!<token-id>=<uuid>
# Set VM credentials
pulumi config set --secret k3sVmPassword <vm-password>
pulumi config set --secret sshPvePublicKey "ssh-ed25519 AAAA..."
```
Pulumi encrypts these values into `Pulumi.dev.yaml` using your `PULUMI_CONFIG_PASSPHRASE`.
### 3. Set the state backend
```bash
export PULUMI_BACKEND_URL=postgresql://<user>:<pass>@<host>/<db>
export PULUMI_CONFIG_PASSPHRASE=<your-passphrase>
```
### 4. Preview and deploy
```bash
# See what will change before touching anything
pulumi preview
# Sync Pulumi state with actual Proxmox state (run after any manual GUI changes)
pulumi refresh --yes
# Deploy
pulumi refresh --yes && pulumi up --yes
```
## CI/CD (Gitea Actions)
The workflow at `.gitea/workflows/deploy-proxmox-infra.yaml` runs automatically:
| Event | Action |
| --------------------- | ------------------------------ |
| Pull request → `main` | `pulumi preview` (read-only) |
| Push to `main` | `pulumi refresh` + `pulumi up` |
| Manual trigger | `pulumi refresh` + `pulumi up` |
### Required Gitea secrets
Configure these under **Settings → Actions → Secrets** in your Gitea repo:
| Secret | Description |
| -------------------------- | -------------------------------------------------- |
| `PULUMI_BACKEND_URL` | PostgreSQL connection string for the state backend |
| `PULUMI_CONFIG_PASSPHRASE` | Passphrase to decrypt secrets in `Pulumi.dev.yaml` |
| `PULUMI_DEV_YAML` | Base64-encoded content of `Pulumi.dev.yaml` |
`Pulumi.dev.yaml` is gitignored because it contains your encryption salt. Whenever it changes (e.g. after adding or rotating a secret), re-encode it and paste the output into the Gitea secret:
```bash
base64 -w 0 proxmox-infra/Pulumi.dev.yaml
```
## Adapting this as a template
1. Fork or copy the repo
2. Update node names (`pve`, `pve-bckp`) and datastore IDs in `index.ts` to match your setup
3. Add or remove VMs from the `nodeConfigs` array
4. Set your own secrets with `pulumi config set --secret`
5. Point the CI/CD workflow at your own Git instance
## Roadmap
- LXC container management
- Docker / Compose stack provisioning
- Network and firewall rules
- Automated k3s bootstrapping (kubeconfig export)
- Additional worker nodes and storage volumes
- Migrate secrets management to [OpenBao](https://openbao.org/) — replace `PULUMI_CONFIG_PASSPHRASE` and manual `Pulumi.dev.yaml` encoding with a self-hosted vault
- Add a third bare metal proxmox instance to create an actual 3 node parity.