ADR: 0386 Last verified: 2026-04-09
This runbook takes you from "I have a server" to "platform fully operational." It is the single document you need to read.
| Requirement | Details |
|---|---|
| Server | Dedicated or virtual x86_64 server with Debian 13 (Trixie) |
| RAM | 32 GB minimum (16 GB absolute minimum for reduced services) |
| Disk | 500 GB NVMe recommended |
| Network | Public IPv4 address, SSH access |
| Domain | A domain you control (for DNS records and TLS) |
| Dev machine | macOS or Linux with Ansible, Python 3.12+, Docker, SSH |
If you just want to try the platform locally without a server:
git clone <repo-url> && cd proxmox-host_server
make init-local # Generate SSH keys and secrets
make docker-dev-up # Start 4 containers (8 GB RAM)
make docker-dev-converge # Deploy services via AnsibleSee docker-dev/README.md for details.
# Clone the repo
git clone <repo-url> && cd proxmox-host_server
# Install git hooks and dependencies
make setup
# Generate the .local/ overlay with SSH keys and secrets
make init-localAfter make init-local completes, it prints a list of external secrets you
must provide (API keys, domain registrar tokens, etc.). Fill those in before
proceeding.
Review the provider profiles in config/provider-profiles/:
| Profile | File | When to use |
|---|---|---|
| Hetzner Dedicated | hetzner-dedicated.yaml |
Hetzner bare-metal server |
| Generic Debian | generic-debian.yaml |
Any Debian 13 server |
| Home Lab | homelab.yaml |
Local development or home server |
Each profile lists the exact steps for that provider. The steps below follow the generic path.
- Ensure the server is running Debian 13 (Trixie)
- Ensure root SSH access works
- Inject the bootstrap SSH key:
# Copy the generated public key to the server
ssh-copy-id -i .local/ssh/bootstrap.id_ed25519.pub root@<SERVER_IP>
# Verify key-based access works
ssh -i .local/ssh/bootstrap.id_ed25519 root@<SERVER_IP> 'echo OK'Copy the inventory template and customize:
cp inventory/hosts.yml.example inventory/hosts.ymlEdit inventory/hosts.yml:
- Replace
your-proxmox-hostwith your hostname - Replace
<SERVER_IP>with your server's IP address - Rename guest hostnames to match your naming convention
- Adjust guest IP addresses if your subnet differs from
10.10.10.0/24
make install-proxmoxThis runs playbooks/site.yml with the Proxmox installation tags. It:
- Adds Proxmox repositories
- Installs the PVE kernel and packages
- Creates the
opsuser with sudo - Configures networking (bridges for guest VMs)
- Sets up Tailscale/Headscale VPN (if configured)
- Hardens SSH access
Verify:
make verify-bootstrap-proxmoxExpected output: Proxmox VE version, storage pools, ops user present, KVM available.
make configure-network
make configure-tailscale # Optional: VPN access
make harden-accessmake provision-guestsThis creates all guest VMs defined in your inventory via the Proxmox API.
Verify:
make verify-bootstrap-guestsExpected output: All guests show REACHABLE with their OS version.
make converge-siteThis is the big one. It deploys all services across all VMs: PostgreSQL, Keycloak (SSO), OpenBao (secrets), Nginx (edge proxy), Grafana (monitoring), and all application services.
For a faster first pass, converge only the critical path:
make bootstrap-minimalVerify:
make verify-platform- DNS: Create A records pointing
*.yourdomain.comto your server's public IP - TLS: Certificates are managed by step-ca; verify with
make validate-certificates - SSO: Log into Keycloak at
https://auth.yourdomain.comand create operator accounts - Monitoring: Access Grafana at
https://grafana.yourdomain.com
The bootstrap SSH key isn't on the target host. Run:
ssh-copy-id -i .local/ssh/bootstrap.id_ed25519.pub root@<SERVER_IP>Use make init-local FORCE=true to create missing files without overwriting existing ones.
Some .local/ files trip the preflight scanner. Temporarily rename them:
mv .local/open-webui/provider.env .local/open-webui/provider.env.bak
mv .local/serverclaw/provider.env .local/serverclaw/provider.env.bak
# Run your make target
# Then restore:
mv .local/open-webui/provider.env.bak .local/open-webui/provider.env
mv .local/serverclaw/provider.env.bak .local/serverclaw/provider.env- Verify the VM is running:
ssh ops@<PROXMOX_IP> 'qm list' - Check the VM's IP matches inventory:
ssh ops@<PROXMOX_IP> 'qm guest cmd <VMID> network-get-interfaces' - Check firewall rules aren't blocking the bridge network
Ansible is idempotent. Simply re-run the failed target:
make converge-site # Safe to re-runFor a specific service:
make converge-<service-name>