- Add adyrem.duckdns.org/Anki → claude-code VM:4002 via Traefik - Add anki.homelab DNS entry to Pi-hole - Mount Jellyfin transcode cache to /media/transcodes (HDD) instead of anonymous Docker volume on root disk; prevents root disk from filling up Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Homelab
Proxmox VE homelab running on an Intel i7-8700 / 32 GB machine. All infrastructure is managed via Ansible. Secrets are encrypted with ansible-vault.
Infrastructure Overview
| Component | Type | IP | Purpose |
|---|---|---|---|
| Proxmox host | bare metal | 192.168.1.10 | Hypervisor, WireGuard VPN server |
| gitea | VM 100 | 10.10.1.125 | Self-hosted Git + CI runner |
| monitoring | VM 101 | 10.10.1.137 | Prometheus, Grafana, Loki, Alertmanager |
| windows | VM 102 | — | Visual Studio (SPICE via Proxmox UI) |
| pihole | LXC 103 | 10.10.1.2 | DNS + ad blocking |
| traefik | LXC 104 | 10.10.1.3 | Reverse proxy + Let's Encrypt |
| claude-code | VM 105 | 10.10.2.10 | Persistent Claude Code session VM |
Networks:
vmbr110.10.1.0/24 — infra (Gitea, monitoring, Pi-hole, Traefik)vmbr210.10.2.0/24 — dev (Claude Code VM)vmbr310.10.3.0/24 — desktop (Windows VM, isolated)
Public endpoints:
https://adyrem.duckdns.org/git— Gitea- Grafana:
http://10.10.1.137:3000(VPN/LAN only) - Proxmox UI:
https://10.10.1.10:8006(VPN/LAN only; usehttps://10.10.10.1:8006over WireGuard)
Prerequisites
- Ansible installed locally (
pip install ansible ansible-lint) - Vault password stored at
~/.config/homelab/vault_pass - WireGuard connected (for remote access) or on the LAN
cd ansible
export ANSIBLE_VAULT_PASSWORD_FILE=~/.config/homelab/vault_pass
Day-to-day Operations
SSH aliases (defined in ~/.ssh/config)
ssh proxmox # Proxmox host (port 2222, user adyrem)
ssh gitea-vm # Gitea VM (via ProxyJump)
ssh monitoring-vm # Monitoring VM (via ProxyJump)
ssh pihole-ct # Pi-hole LXC (via ProxyJump)
ssh traefik-ct # Traefik LXC (via ProxyJump)
ssh claude-code-vm # Claude Code VM (via ProxyJump)
Running playbooks
cd ansible
ansible-playbook playbooks/host.yml # Proxmox host config
ansible-playbook playbooks/gitea.yml # Gitea VM
ansible-playbook playbooks/monitoring.yml # Monitoring stack
ansible-playbook playbooks/pihole.yml # Pi-hole
ansible-playbook playbooks/traefik.yml # Traefik
ansible-playbook playbooks/claude-code-vm.yml
ansible-playbook playbooks/site.yml # All Arch VMs (common + node_exporter + promtail)
Adding a WireGuard client
See ansible/playbooks/wireguard-README.md for step-by-step instructions.
VPN Client Setup
WireGuard VPN is hosted on the Proxmox host (adyrem.duckdns.org:51820).
Linux
# Install WireGuard
sudo apt install wireguard # Debian/Ubuntu
sudo pacman -S wireguard-tools # Arch
# Create config at /etc/wireguard/wg0.conf (see wireguard-README.md for template)
sudo systemctl enable --now wg-quick@wg0
# Verify
ping 10.10.10.1 # Proxmox host
Windows
- Download and install the WireGuard app
- Open WireGuard → Add Tunnel → Add empty tunnel
- Copy the generated public key — register it as a peer (see wireguard-README.md)
- Paste the full client config, click Activate
macOS / Android / iOS
Import the config file into the WireGuard app. The config template is in ansible/playbooks/wireguard-README.md.
Split tunnel: only homelab traffic routes through the VPN (10.10.10.0/24, 10.10.1.0/24, 10.10.2.0/24). Internet traffic goes direct.
Full Rebuild from Bare Metal
1. Install Proxmox
Boot the Proxmox VE ISO. During install:
- Disk: SSD (ZFS, single disk)
- Hostname:
homelab - IP: 192.168.1.10/24, gateway 192.168.1.1
- Timezone: Europe/Zurich
2. Restore ZFS data from HDD backup
SSH into the freshly installed host as root:
# Import HDD pool
zpool import hdd
# Restore VM disks
zfs receive -F rpool/data < <(syncoid hdd/backups/rpool-data rpool/data)
# Restore Proxmox OS datasets (optional — only if SSD was lost)
zfs receive -F rpool/ROOT/pve-1 < <(syncoid hdd/backups/pve-root rpool/ROOT/pve-1)
Or use syncoid directly for incremental restore:
syncoid --recursive hdd/backups/rpool-data rpool/data
3. Configure Proxmox host via Ansible
On first run, the playbook connects as root (before hardening):
cd ansible
ansible-playbook playbooks/host.yml
This creates adyrem, hardens SSH (port 2222, no root login), sets up the claude-code SFTP user, WireGuard, and DuckDNS. Root SSH access ends after this run.
Subsequent runs use adyrem (already set in group_vars/proxmox_hosts/vars.yml).
4. Configure network and firewall
Edit /etc/network/interfaces on Proxmox to restore the DNAT rules (port 80/443 → Traefik, port 53 → Pi-hole). Template is tracked at proxmox/network-interfaces.
Apply host firewall rules:
ansible-playbook playbooks/host.yml
5. Start infrastructure VMs/CTs and run playbooks
# Start VMs (or they start automatically if autostart was set)
# Run infra playbooks
cd ansible
ansible-playbook playbooks/pihole.yml
ansible-playbook playbooks/traefik.yml
ansible-playbook playbooks/gitea.yml
ansible-playbook playbooks/monitoring.yml
ansible-playbook playbooks/claude-code-vm.yml
6. Set up sanoid/syncoid for ZFS snapshots
Handled automatically by playbooks/host.yml (the sanoid role). No manual steps needed.
7. Restore Grafana password
Grafana bootstraps with password admin. Log in at http://10.10.1.137:3000 and change it when prompted.
8. Reconfigure Internet-Box
- Port forward UDP 51820 → 192.168.1.10 (WireGuard)
- Port forward TCP 80 → 192.168.1.10 (Let's Encrypt HTTP-01)
- Port forward TCP 443 → 192.168.1.10 (HTTPS)
Restore a VM from ZFS Snapshot
List available snapshots:
zfs list -t snapshot -r rpool/data
Roll back a VM disk to a snapshot:
# Stop the VM first (e.g., VM 100)
qm stop 100
# Roll back to snapshot
zfs rollback rpool/data/vm-100-disk-0@autosnap_2026-05-15_00:00:00_daily
# Start the VM
qm start 100
Restore a VM from the HDD backup pool (e.g., after SSD failure):
zfs send hdd/backups/rpool-data/vm-100-disk-0@<snapshot> | zfs receive rpool/data/vm-100-disk-0
Provisioning a New Dev VM
Dev VMs run on vmbr2 (10.10.2.0/24) so Claude Code has no path to infra.
- Create the VM in the Proxmox UI (or clone an existing one). Assign a static IP on
vmbr2. - Add it to the inventory:
# ansible/inventory/hosts.yml
dev_vms:
hosts:
my-new-vm:
ansible_host: 10.10.2.X
- Run the base playbook:
ansible-playbook playbooks/site.yml --limit my-new-vm
- Add an SSH alias in
~/.ssh/configand inansible/roles/claude_code/templates/ssh_config.j2, then re-runansible-playbook playbooks/claude-code-vm.yml.
Adding a New VM to Monitoring
Node Exporter and Promtail are installed by site.yml on all Arch VMs automatically.
For the VM to appear in Prometheus, add it to the scrape config in ansible/roles/monitoring/templates/prometheus.yml.j2 under the appropriate job, then re-run:
ansible-playbook playbooks/monitoring.yml
Grafana dashboards are provisioned automatically from the datasource. For custom dashboards, export the JSON from Grafana UI and save to files/grafana-dashboards/.
Secrets Management
Secrets are encrypted with ansible-vault. The vault password lives at ~/.config/homelab/vault_pass (never committed).
# Encrypt a new secret
ansible-vault encrypt_string 'mysecret' --name 'vault_my_var'
# Edit an existing vault file
ansible-vault edit ansible/inventory/group_vars/all/vault.yml
# Run a playbook with vault
ansible-playbook playbooks/gitea.yml # picks up vault_pass from env var set above