# What this does This Caddy fragment exposes an entire three‑node Proxmox VE cluster via one hostname (pve.core.vninja.com) so you don't need separate URLs for pve01, pve02, and pve03. It uses the same wildcard TLS certs Caddy already issues in the homelab.
# The Caddy fragment Place the fragment (example below) in conf.d/pve.caddy as an Ansible‑managed file or similar. It reverse_proxies port 8006 on each node and includes health checks and load‑balancer settings.
# Why lb_policy must be ip_hash Proxmox's web UI becomes stateful when you open a console. The web page requests a VNC or terminal ticket, and the node that issued the ticket is the only node that can serve the resulting WebSocket. If you use round_robin, the WebSocket request often lands on a different backend that doesn't know the ticket and the console immediately fails.
ip_hash pins each client IP to a single backend for the life of that pinning. That guarantees the page and its console WebSocket go to the same node. The trade‑off is that if the pinned node dies mid‑session, the client will be rehashed to a different node and any open console must be reconnected. Proxmox's auth cookie is cluster‑signed, so reauthentication is usually not required—just reopen the console.
# Health checks and retry behavior
lb_try_duration and lb_try_interval control how long a single request will retry different upstreams before giving up. The example uses a 5s total retry window with 250ms retry intervals.
# Practical implications
- You get a single hostname and single certificate for admin access to the cluster.
- Console sessions are reliable because the reverse proxy pins clients to the node that issued the console ticket.
- If a pinned node crashes or reboots, users must reopen any consoles they had open, but they typically stay logged in.
# Short note on scope This fragment is a targeted example for a small private cluster in a homelab. It shows the minimal Caddy config pieces you need: reverse_proxy targets, ip_hash, health checks, and retry timing.