One ru-vps housekeeping sweep (2026-09-02/03): - playbooks/ru-vps-base.yml (new): adopt the Caddy compose stack into Ansible (pinned image by digest, homelab-caddy.service), and manage the corosync-qnetd UFW rule - allow 5403/tcp from homelab_pve_egress_ip, drop the stale rule for the retired ZeroTier 10.122.62.0/24. The qdevice had gone silent because its only allowed path was the decommissioned ZeroTier network. - group_vars/all/main.yml: homelab_pve_egress_ip (the NATed home egress the PVE nodes reach corosync-qnetd from - a direct path that does not depend on the OpenVPN tunnel). Marked dynamic: a change silently re-breaks the qdevice. - playbooks/status.yml: CLUSTER QUORUM section (pvecm status per PVE node) so a repeat failure is visible. Also drops the memoir-bot unit list and moves the gitea offsite-restic unit to the gitea host (see the OpenTofu-migration commit). - playbooks/ru-vps-zerotier-decommission.yml (new): stop the zerotier container, disable ssh-zt22.service, remove the interface/9993/9001/10.122.62.0/24 UFW rules. Node identity and data are kept; removal is a separate step. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012uoq5AVK8mkBgg83Mq6o5V
HomeLab Ansible
Ansible is the control plane for HomeLab infrastructure changes.
Contract
- The operator keeps passwords, SSH access and network reachability working.
- The agent changes infrastructure only through inventory, roles and playbooks in this directory.
- Manual server changes are allowed only for break-glass recovery; afterwards they must be captured in Ansible.
- Secrets stay outside git. Use
.env, vault files or local prompt input, not committed variables.
Layout
inventory/hosts.yml— canonical host list and host-specific facts.playbooks/— entry points for tasks.roles/— reusable configuration units.
Current Groups
ru-vps— public VPS, JumpHost, qdevice, OpenVPN server.pve_nodes— Proxmox hosts:cloud-pc,mini-pc.lxc_infra— infrastructure LXC containers, including the outbound-onlygyroinvestment allocator host.monitoring_server— monitoring LXC; Prometheus stack is frozen and Uptime Kuma is active.monitoring_exporters— hosts exposing Node Exporter metrics.monitoring_smart_exporters— Proxmox nodes exposing SMART metrics.vpn_openvpn— OpenVPN transport hosts:ru-vps,ovpn-mini.shell_hosts— hosts with unified bash config:ru-vps,cloud-pc,mini-pc.servers— all managed hosts.
First Checks
Install control-node dependencies locally:
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
ansible-galaxy collection install -r requirements.yml -p collections
Run from ansible/:
ansible-playbook playbooks/check.yml
Controlled Updates
Service updates are manual and use pinned tag@sha256:digest image references only; floating tags and auto-update agents are not used.
Run the dedicated playbook for the target service from ansible/:
.venv/bin/ansible-playbook playbooks/vaultwarden-update.yml
.venv/bin/ansible-playbook playbooks/gitea-update.yml
.venv/bin/ansible-playbook playbooks/adguard-update.yml
.venv/bin/ansible-playbook playbooks/mihomo-update.yml
.venv/bin/ansible-playbook playbooks/grimmory-update.yml
Update flow is always: fresh backup/audit first, then the update playbook, then health verification.
- Gitea, Vaultwarden and Grimmory use app-aware offsite restic backups/audits before update.
- AdGuard and Mihomo use fresh PBS LXC backups before update.
grimmory-update.ymlcurrently validates the existing pinned release; any future actual app image upgrade must be preceded by release-note and migration review.
Mihomo Hardening
Use the dedicated hardening playbook only when explicitly approved:
.venv/bin/ansible-playbook playbooks/ru-vps-mihomo-harden.yml -e ru_vps_mihomo_harden_confirm=true
It rotates the live Mihomo SOCKS credentials on ru-vps, locks the proxy to loopback, and removes the public UFW exposure for ports 7890 and 7891.
The rotated credentials are not recoverable for clients unless you roll back the saved config backup.
For Proxmox API playbooks, create ignored .env from .env.example and load it:
cp .env.example .env
. ./.env
.venv/bin/ansible-playbook playbooks/pve-ovpn-mini.yml
Or bootstrap the token from mini-pc with sudo:
.venv/bin/ansible-playbook playbooks/bootstrap-pve-api-token.yml -K
Create the separate read-only PVE token used by the monitoring exporter:
.venv/bin/ansible-playbook playbooks/bootstrap-monitoring-pve-token.yml
OpenVPN transport:
.venv/bin/ansible-playbook playbooks/openvpn-vps-mini.yml -K
.venv/bin/ansible-playbook playbooks/openvpn-check.yml
Monitoring is provisioned in two steps after loading the monitoring secrets from ignored .env or Ansible Vault:
. ./.env
.venv/bin/ansible-playbook playbooks/pve-monitoring.yml
.venv/bin/ansible-playbook playbooks/monitoring.yml
pve-monitoring.yml creates CT 146 (monitoring, 192.168.1.30) on cloud-pc. monitoring.yml configures exporters, the ru-vps probe vantage point, and the central Prometheus stack. It is frozen while Uptime Kuma is in use; do not run it unless restoring Prometheus monitoring.
Gyro Investment Allocator
pve-gyro.yml creates unprivileged CT 150 (gyro, 192.168.1.35) on mini-pc. gyro.yml installs Python 3.13+, pinned uv, the gyro service user, a container-local GitHub deploy key, restrictive firewall rules, and a weekday systemd timer.
UFW is the currently enforced isolation layer: inbound is denied except SSH from LAN/OpenVPN, and east-west outbound is denied except the Mihomo HTTP proxy. The equivalent CT 150 Proxmox firewall is staged, but the cluster-wide PVE firewall remains disabled; do not enable it without auditing every node and guest with firewall=1.
The role keeps deployment and the timer disabled by default. The active host vars deploy git@github.com:ada-dmitry/t_tech-gyro.git with GitHub's verified ED25519 host key; the timer still requires the ignored Vault file:
cp inventory/host_vars/gyro/vault.yml.example inventory/host_vars/gyro/vault.yml
ansible-vault encrypt inventory/host_vars/gyro/vault.yml
After encrypting the secrets, set gyro_timer_enabled: true in main.yml and apply with --ask-vault-pass. DRY_RUN_OVERRIDE remains true until real trading is explicitly approved.
. ./.env
.venv/bin/ansible-playbook playbooks/pve-gyro.yml
.venv/bin/ansible-playbook playbooks/gyro.yml --ask-vault-pass
The timer runs at 11:00 Europe/Moscow from Monday through Friday and uses OnFailure= for a best-effort Telegram alert. CT 150 is included in the daily mini-pc PBS job and backup freshness audit.
Uptime Kuma uses the existing monitoring LXC and stops/disables homelab-monitoring without deleting its configuration or data. Its UI is available only from the LAN at http://192.168.1.30:3001; create monitors and notification settings in the UI.
.venv/bin/ansible-playbook playbooks/uptime-kuma.yml
Emergency Reverse SSH
pve-emergency-bot.yml creates CT 148 (emergency-bot, 192.168.1.32) on mini-pc. emergency-access.yml configures the bot, a TTL-limited reverse tunnel from mini-pc to ru-vps, and the restricted SSH identities used between them.
Before applying, set EMERGENCY_BOT_TOKEN, EMERGENCY_ALLOWED_USER_IDS, EMERGENCY_VPS_HOST_KEY, and EMERGENCY_MINI_PC_HOST_KEY in ignored .env or Ansible Vault. The host-key variables must be verified public host keys, not values obtained during deployment.
. ./.env
.venv/bin/ansible-playbook playbooks/pve-emergency-bot.yml
.venv/bin/ansible-playbook playbooks/emergency-access.yml
From an authorized private Telegram chat, use the Enable SSH, Status, and Stop buttons or /emergency ssh, /emergency status, and /emergency stop. /emergency ssh enables a 60-minute tunnel only; it does not expose a public port. Connect while it is active with:
The bot uses the LAN Mihomo HTTP proxy at 192.168.1.27:7890 because direct Telegram TCP access is unavailable from HomeLab.
ssh -i ~/.ssh/id_ed25519_homelab_ansible -o IdentitiesOnly=yes -J vps -p 22010 ansible@127.0.0.1
The target account is ansible; it has no password login. Use the existing private key ~/.ssh/id_ed25519_homelab_ansible.
Bootstrap the Ansible service account on shell hosts:
.venv/bin/ansible-playbook -i inventory/hosts.yml playbooks/bootstrap-ansible-user.yml -K
When a task needs privilege escalation:
ansible-playbook playbooks/<name>.yml -K
Workflow
- Describe the desired infrastructure change.
- Add or update a role/playbook.
- Run the smallest safe check or playbook.
- Document non-obvious decisions in the project notes.