Hypervisor + VM layer for the home cluster
  • Nix 93.8%
  • HCL 4.3%
  • Makefile 1.9%
Find a file
Tamjid Rahman 0c5a89c053 livesync: dedicated CouchDB VM for Obsidian Self-Hosted LiveSync
Personal vault sync across Mac / iPhone / NixOS desktop without
iCloud. Plugin runs E2EE client-side so CouchDB stores ciphertext
only; public exposure via livesync.tamjid.io is acceptable.

Two gotchas baked into the config + memory:
- The official apache/couchdb:3.4 entrypoint writes docker.ini into
  /opt/couchdb/etc/local.d/ at every start. Mounting our config there
  with :ro makes the bind layer read-only and the container dies
  silently (exit 1, no logs). Mount without :ro and name it zzz-* so
  it loads after docker.ini.
- The LiveSync plugin's "Fix" button POSTs require_valid_user = true
  to chttpd, which makes /_up return 401, fails the Consul health
  check, and drops the vhost from both Caddys. Explicit
  require_valid_user = false in local.ini wins on container restart.

Sized 1 CPU / 2 GB / 10 GB OS + 10 GB data — tiny enough to share a
hypervisor headroom slot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-14 14:49:47 -04:00
template livesync: dedicated CouchDB VM for Obsidian Self-Hosted LiveSync 2026-06-14 14:49:47 -04:00
terraform bring repo to current cluster state: postgres + observability + TrueNAS 2026-06-06 23:26:28 -04:00
.gitignore gitignore: untrack template/terraform.tfstate 2026-06-06 23:27:19 -04:00
inventory.yaml livesync: dedicated CouchDB VM for Obsidian Self-Hosted LiveSync 2026-06-14 14:49:47 -04:00
README.md bring repo to current cluster state: postgres + observability + TrueNAS 2026-06-06 23:26:28 -04:00

basha_infra

Hypervisor + VM layer for the home cluster. Owns machines; service repos own workloads. See ~/src/CLAUDE.md for the broader picture.

Layout

basha_infra/
├── inventory.yaml          # source of truth: Proxmox hosts, VMs, roles, host_volumes
├── template/               # NixOS Proxmox template builder
│   ├── flake.nix
│   ├── modules/
│   │   ├── proxmox-base.nix     # bootloader, networking, ssh — used by every VM
│   │   └── cluster-client.nix   # consul + nomad + docker + tailscale (client mode)
│   ├── hosts/
│   │   └── apps.nix             # per-VM nix config (hostname, meta.role)
│   └── Makefile                 # `make template` builds + uploads + registers
└── terraform/              # VM lifecycle
    ├── main.tf                  # bpg/proxmox: clones template per inventory.yaml
    ├── variables.tf
    └── outputs.tf

The control plane (Consul server, Nomad server, Vault) lives on the home NixOS box (~/src/nixos/), not here. VMs only run the client halves.

How it fits together

   template/             →  qmrestore → Proxmox template VMID 9001
                                              │
                                              │ qm clone
                                              ▼
   terraform/            →  cloned VMs (apps, postgres, …) per inventory.yaml
                                              │
                                              │ nixos-rebuild --target-host
                                              ▼
   template/hosts/<name>.nix lays the per-host config on top of the template

The template carries the cluster-client modules baked in, so a freshly cloned VM joins Consul + Nomad on first boot. Per-host overrides (hostname, meta.role, future host_volume mounts) are applied later via nixos-rebuild --target-host.

Bootstrap (first-time on a fresh Proxmox)

Prerequisites (one-off on Proxmox, see pveum/pvesm):

  • terraform@pve user with an API token + Administrator on /
  • local datastore has import and snippets content types enabled
  • Your laptop's SSH key in root@<proxmox>:~/.ssh/authorized_keys
# 1. Build + register the template (VMID 9001)
cd template && make template

# 2. Fill in terraform.tfvars
cd ../terraform
cp terraform.tfvars.example terraform.tfvars
$EDITOR terraform.tfvars

# 3. Apply — clones a VM per inventory.yaml entry
terraform init
terraform apply

# 4. After first boot, SSH in via LAN IP (guest agent reports it),
#    run `tailscale up --authkey=...` once. Then from the laptop:
nixos-rebuild switch --flake ../template#apps --target-host root@apps

Add a new VM

  1. Add an entry under vms: in inventory.yaml.
  2. Add template/hosts/<name>.nix and a nixosConfigurations.<name> entry in template/flake.nix.
  3. cd terraform && terraform apply clones the VM.
  4. nixos-rebuild switch --flake template#<name> --target-host root@<lan-ip>.

Update an existing VM

Edit template/modules/cluster-client.nix or template/hosts/<name>.nix, then redeploy without recreating the VM:

nixos-rebuild switch --flake ./template#<name> --target-host root@<name>

If the change should also affect future clones, rebuild the template too: cd template && make template. (Existing VMs are not auto-recreated.)

Remove a VM

  1. nomad node drain -enable -force <node-id> — evacuate jobs.
  2. Delete the entry from inventory.yaml.
  3. cd terraform && terraform apply — destroys the VM.

TODOs

  • Move terraform state to Garage S3 backend (see ~/src/CLAUDE.md).
  • Add postgres / garage / vault / forgejo hosts as stateful services migrate off the home box.
  • Thread tailscale auth via nixos-rebuild so the one-time manual tailscale up after clone isn't needed.