Terraform, Ansible and a Go CLI for a Proxmox homelab. Every host is built from this repo. A broken host is rebuilt from it, not fixed by hand.
- What anvil is
- Why the name
- What it sets up
- Install
- Quick start
- Commands
- Make targets
- Configuration
- Documentation
- Repository layout
- Roadmap
- Known issues
- Contributing
- License
anvil builds and runs a homelab from code. One repository holds the whole lab:
- Terraform creates the virtual machines on Proxmox VE.
- Ansible configures the hosts and every service on them.
- The
anvilCLI wraps both. It checks first, shows what would change, asks, and then applies. It also installs Proxmox on a new server from bare metal, rotates API keys and checks every night that the lab still matches the code. - Vault holds the secrets and the site values: addresses, names, logins. The repository is public and holds none of them.
A new server goes from bare metal to a running Proxmox host with anvil commands. A host that breaks is rebuilt from the repository, not repaired by hand. It runs a real lab, two Dell servers and a Cisco switch, and anyone can reuse it with their own site values.
A blacksmith shapes metal on an anvil. The anvil itself does not change: the same block shapes piece after piece, and a piece that cracks goes back on it and is shaped again.
This repository is that block for the lab. Hosts and services are shaped on it. When one breaks, it is rebuilt from the same code, the same way every time.
- Terraform (bpg/proxmox) defines the VMs and the SDN.
- Ansible configures the Proxmox host and the guests.
- HashiCorp Vault holds the secrets and the site values.
- Terraform state lives in an S3 bucket on a single-node Ceph cluster on the same host.
- The
anvilCLI runs the make targets: the whole lab, one service, checks, backups and restores.
It is a work in progress. See the Roadmap.
| Host | Runs |
|---|---|
pve |
Proxmox VE 8.4, LAN gateway and NAT, NetBird, Ceph |
frigate |
Frigate, Mosquitto, Gotify, nginx, Docmost, cloudflared |
netbird |
NetBird routing peers (cloud and self-hosted), the self-hosted NetBird server, Redis, ntfy, Pulse, the apt repository, cloudflared |
ragnarok |
ARR stack, IPTV and its guide server, Filestash, cloudflared |
vault |
HashiCorp Vault, in the secrets enclave (no internet) |
vaultwarden |
Vaultwarden, in the secrets enclave (no internet, one mail hole) |
apps |
Pi-hole and Unbound, the Caddy service gateway, Homepage |
identity |
Authentik (single sign-on), on the second server. The LDAP directory is being built |
docs/services.md describes each one.
pve, runs the lab |
pve2, backups and new work (being installed) |
|
|---|---|---|
| Server | Dell PowerEdge T130 tower | Dell PowerEdge R620, 1U |
| CPU | Xeon E3-1220 v6, 4 cores | 2 × Xeon E5-2640 v2, 16 cores, 32 threads |
| RAM | 32 GB DDR4 ECC | 128 GB DDR3 ECC |
| Disks | 4 TB + 1 TB (Ceph OSD) | 4 × 1 TB + 4 × 2 TB, ZFS: mirror, mirror, RAIDZ1 |
| Network | 2 × 1 GbE | 2 × 10 GbE + 2 × 1 GbE |
| Out-of-band | iDRAC8 | iDRAC7 |
Switch: Cisco SG300-52, managed as code. Details, disk layout and limits: docs/hardware.md.
anvil is one binary for macOS and Linux, amd64 or arm64.
docs/install.md has every method and how to verify a download.
One command:
curl -fsSL https://memetics19.github.io/anvil/install.sh | shIt installs to /usr/local/bin, or ~/.local/bin when /usr/local/bin is not writable.
ANVIL_VERSION and ANVIL_INSTALL_DIR pick the version and the directory.
Homebrew:
brew tap memetics19/anvil https://github.com/memetics19/anvil
brew install anvilDebian and Ubuntu, from the signed apt repository at apt.shreeda.xyz:
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://apt.shreeda.xyz/anvil.gpg | sudo tee /etc/apt/keyrings/anvil.gpg >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/anvil.gpg] https://apt.shreeda.xyz stable main" |
sudo tee /etc/apt/sources.list.d/anvil.list
sudo apt update && sudo apt install anvilFedora and RHEL, with the .rpm from Releases:
sudo dnf install ./anvil-<version>-1.x86_64.rpm # or .aarch64.rpmFrom source, with Go:
git clone https://github.com/memetics19/anvil && cd anvil
make cli # builds build/anvil
sudo install -m 755 build/anvil /usr/local/bin/anvilYou need a Mac, because the scripts keep secrets in its Keychain, and a Proxmox VE host. Install the tools in docs/setup.md, and add the Keychain items and the site dict it describes. Then:
anvil fetch # clones this repo into ~/.local/share/anvil/repo
anvil doctor # checks tools, Keychain items and Vault, read-only, with a fix for each problem
anvil up # builds the whole lab, in the order of the first buildanvil up runs the steps of First build as stages. Each
stage runs its check first and applies only what differs. On a running lab it is a
converge, and a host that matches the code reports changed=0.
| Command | What it does |
|---|---|
anvil up [service ...] |
Build or converge the whole lab, or deploy named services |
anvil doctor, anvil init |
Check the tools, the Keychain items and Vault, read-only. doctor --fix removes Mac routes that hide the VPN |
anvil status |
A read-only dashboard of the lab |
anvil check <target> |
Dry run a target: --check --diff, tf-plan or the switch plan |
anvil apply <target> |
Check a target, then apply it after confirmation |
anvil drift |
The nightly check. Pushes to ntfy when the lab drifted. --install schedules it |
anvil services |
List the services, VMs and lab targets that check, apply and up accept |
anvil backup |
Run make backup |
anvil restore <part> |
The documented restore steps, with confirmation |
anvil provision-iso <name> |
Build the Proxmox auto-install ISO for a new server |
anvil provision <name> |
Install Proxmox on a new server, a dry run unless --install |
anvil pools <name> |
Create the ZFS data pools of a newly installed server |
anvil increase-disk <vm> <size> |
Grow a VM's disk while it runs |
anvil rotate <secret> |
New API keys for Radarr, Sonarr or Prowlarr, and everything that uses them |
anvil media <command> |
Search, download and follow movies and series (Radarr, Sonarr) |
anvil fetch, anvil update |
Clone the repo for a packaged install, or move it to this binary's version |
anvil version |
Print the version, commit, build date, Go version and platform |
anvil completion <shell> |
Print a bash, zsh or fish completion script |
Every command takes --help, --no-color and, where it runs make, --dry-run.
docs/cli.md describes each one.
anvil runs these make targets. They also work on their own. These two need no secrets:
make lint secrets-scanMost work is a dry run, then the real run:
make vm HOST=apps ARGS="--check --diff"
make vm HOST=apps
make tf-plan
make tf-apply
make backupdocs/usage.md lists every target.
- The
sitedict holds the addresses, MACs, VM IDs and the domain. Its canonical copy is in Vault atkv/homelab/site. - Before Vault exists, the scripts read a bootstrap copy from the Keychain item
site-bootstrap. site/site.example.ymlshows the structure.- Secrets are in Vault under
kv/homelab/<host>/<app>. docs/secrets.md lists them.
The docs site is https://memetics19.github.io/anvil/. make docs builds it locally.
make docs-publish also serves it privately on the gateway as
https://docs.<gateway-domain> (usage).
| Page | Covers |
|---|---|
| install.md | Installing anvil, and how releases are cut |
| setup.md | The operator Mac, Keychain items, the site dict, the first build, rebuilding on a new box, the hand steps |
| usage.md | Make targets, adding a service, secrets, backups, safety rails, upgrades, tests, troubleshooting |
| cli.md | Every anvil command |
| provisioning.md | Installing Proxmox VE on a new server with the automated installer ISO, Redfish or IPMI |
| hardware.md | The two servers and the switch: models, disk layout, roles and limits |
| packer.md | The plan for Packer VM images: design, checks, phases and risks |
| services.md | What each host and role does |
| secrets.md | Every Vault path and key |
| cisco-switch.md | The Cisco switch: what make switch manages and what is set by hand |
ansible/
inventory/ hosts by name, group_vars/all/site.yml resolves `site`
playbooks/
roles/
cli/ the anvil CLI (Go), make cli builds build/anvil
cmd/anvil/ main: the build variables and the command packages
internal/ app (commands, options, help), run, repo, one package per area
docs/ the docs site pages, docs/assets/ holds the demo GIF and its VHS tape
Formula/ the Homebrew formula, updated by each release
iptv/ the IPTV channel list
packaging/ the .deb and .rpm post-install script
scripts/ env.sh, plan-guard.sh, the installer, backup and Keychain helpers
site/ site.example.yml
terraform/ VMs, SDN, import blocks
tests/ shell tests, run by CI
vault/policies/ Vault policies
To re-record the demo GIF, run from the repo root:
docker run --rm -v "$PWD":/vhs ghcr.io/charmbracelet/vhs docs/assets/anvil.tapeDone:
- Secret scanning, linters, CI
- Trivy on every pull request and weekly. The repository and the release binary block on a fixable HIGH or CRITICAL finding. Every pinned container image is reported.
- Inventory, SSH bootstrap, Proxmox host base role
- Host network with automatic rollback
- Single-node Ceph and S3 gateway for Terraform state
- Terraform: adopt the existing VMs and SDN, guard against destroys
- Vault VM, Keychain-held unseal keys, snapshot backups
- Secrets and site values in Vault
- Guest roles: base, Docker, node_exporter, cloudflared
- Workload roles for
frigate,ragnarokandnetbird - Private service gateway (Caddy, wildcard DNS-01, Pi-hole records)
- Pi-hole and Unbound for the home network
- Homepage dashboard
- Vaultwarden with encrypted backups and a restore drill
- Secrets enclave: Vault and Vaultwarden on an isolated network, reachable only from the operator's devices, firewalled by data-driven routed segments
- VLANs 10/20/30 with routing, DHCP, NTP and an inter-VLAN firewall. The switch and a VLAN-capable access point are hand steps.
- Bare-metal provisioning: Proxmox auto-install ISO, delivered by Redfish, IPMI or PXE, brand agnostic (docs/provisioning.md). Tested end to end on VMs. The first real server is the next one.
- Releases: semver tags and a changelog from conventional commits (release-please), published as GitHub Releases, starting with v0.1.0
- An
anvilCLI over the make targets (docs/cli.md). A fresh box run is still untested. - Packages: .deb and .rpm built with nfpm, a Homebrew formula and a one-line installer, from GoReleaser on each release, with this repo as the Homebrew tap (docs/install.md)
- A signed apt repository for the .deb,
apt.shreeda.xyz, published by the homelab from each release (docs/install.md) - cloudflared pinned: one
cloudflared_version, installed from Cloudflare's apt repository and held. Upgrades go one connector at a time while the others keep the tunnel up. The token is read from a root-only file, not the process list.
Planned:
- Monitoring: VictoriaMetrics, Grafana, exporters, alerts to ntfy
- Patch tracking: PatchMon for pending and security updates on every host (on the second server)
- Single sign-on (Authelia) in front of the gateway
- Self-hosted NetBird next to the hosted one. The server and dashboard run. The routing peer waits for a route that passes gRPC, which the Cloudflare tunnel does not.
- Split the generic core (roles, Terraform modules, scripts) from this lab's site layer, so it works on another Proxmox box
- OS agnostic operator machine: Linux, macOS or Windows (WSL). Secrets move from the
macOS Keychain to a pluggable backend (Vault plus an age-encrypted bootstrap file, the
Linux secret service, or the macOS Keychain). The scripts drop macOS-only commands
(
security,networksetup,pbcopy, BSDsedandstat). - The rebuild runbook, tested on a second server
- Packer images: VM templates built from code, scanned and boot-tested, so new VMs start ready. Plan: docs/packer.md
- Frigate's camera has not answered since 2026-01-13, so there are no new recordings.
- Frigate's
genaiprovider points at an Ollama address that does not answer. /homeonragnarokis 98% full.- The Ceph dashboard's Object Gateway zone and multisite pages return 500 on Proxmox. See Ceph.
Issues and pull requests are welcome. CONTRIBUTING.md has the checks to run, the commit style, and how pull requests are merged and stacked.
