c436075c1b
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.
148 lines
5.5 KiB
Markdown
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.
|