Vninja iconVninjaSep 1, 2026 ~3 min source read

Fronting a 3‑node Proxmox VE Cluster With Caddy

A concise, worked example showing a Caddy reverse‑proxy fragment that exposes a three‑node Proxmox VE cluster under one hostname, with health checks and an ip_hash load‑balancing policy to keep web UI pages and their WebSocket consoles pinned to the same node.

Fronting Proxmox VE With Caddy

Share this story

Send the public story page.

Useful takeaways from this story.

Use Caddy reverse_proxy with ip_hash to keep Proxmox web UI pages and their noVNC/xterm.js WebSocket consoles pinned to the same backend node.

Set lb_try_duration and lb_try_interval so Caddy retries another upstream for a bounded time before failing a request.

# 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.

Keep reading in the app

Open the app view to save this story, compare related coverage, and continue from the same source.

Open in app