Skip to content
Load balancers Front a pool of servers behind one endpoint with health checks and failover — managed across your whole fleet.

Load balancers

The Load balancers page (/load-balancers, in the Advanced area) fronts a single public endpoint and spreads incoming requests across a pool of backend servers — if one backend goes down, traffic shifts to the healthy ones with no downtime. Because the pool draws from several VPSes, a load balancer belongs to your whole account rather than to any one server. It’s part of the Advanced area and not tied to a specific plan in the current build.

  1. Click New load balancer to open the create drawer.
  2. Enter a Name (lowercase letters, digits and hyphens) and pick the Listening port443 or 80. Choosing 443 turns on TLS automatically.
  3. Choose the LB node — the managed server from your fleet that runs the engine and serves the public endpoint. It must be a full MZPanel-managed (agent) server.
  4. Pick the Engine:
    • Nginx reuses the Nginx already on the node. Passive health checks; no per-backend traffic stats.
    • HAProxy is a purpose-built LB with active health checks and real per-backend traffic stats. It’s installed on the node on first use.
  5. Pick the Balancing algorithmRound-robin (rotate evenly), Least connections (fewest open connections wins), or IP hash (pin each client IP to the same backend for sticky sessions without cookies).
  6. Build the Backend pool: check the servers you want in the pool, and set each one’s Port (default 8080) and Weight.
  7. Set the Health check — probe Path (/healthz), Interval, and the Healthy / Unhealthy thresholds that move a backend in and out of rotation.
  8. Leave TLS termination on to have a certificate issued automatically via Let’s Encrypt (backends then receive plain HTTP), or turn it off.
  9. Click Create load balancer. MZPanel writes the definition and pushes it to the LB node; the card shows Pending apply until the node reports back.

The page lists each load balancer as a card, with a stats strip on top showing the total count, Healthy backends (healthy / total) and Applied (applied / total).

  • Expand a card to see the backend pool: each backend’s health dot, address, weight, and when it was last checked.
  • Add backend — attach another fleet server to the pool from the inline picker.
  • Drain backend — take a backend out of rotation for maintenance without removing it; it keeps existing connections but receives no new traffic.
  • Remove backend — detach a server from the pool.
  • Edit — change the name, algorithm, listening port, TLS, and health-check path and interval. The engine and LB node are fixed once created.
  • Delete — tear the load balancer down. The public endpoint stops serving immediately; the backend servers themselves are untouched.

The control plane (Postgres) is the source of truth for each load balancer’s definition — a deliberate exception to MZPanel’s “the box owns its state” rule, because an LB spans the fleet and no single VPS owns it (the same model as uptime monitors). The data-plane runs on the one LB node server you chose.

  • Every mutation (create, edit, add/remove/drain a backend, delete) is org-scoped through the /v1/load-balancers API, then re-applied to the node.
  • On apply, MZPanel builds a self-contained spec and dispatches an lb.apply job to the node’s agent, which runs mz lb apply natively — it renders the Nginx upstream or HAProxy config, validates it, and reloads. The card’s state moves Pending apply → Active, or Degraded with the error if the render/reload fails. Delete dispatches an lb.remove job first, then removes the row.
  • Backend health comes from the HAProxy stats socket (HAProxy engine) or the uptime prober (Nginx engine) — both land in each backend’s health, which the dots and the healthy-count reflect. A card reads Degraded if it has no backends or any unhealthy backend.
  • If the LB node is offline, the job is queued and the load balancer stays Pending apply until the node reconnects, at which point the queued apply runs.

Load balancers are a control-plane resource, so you drive them through the MZPanel API rather than an on-box mz command — the node-side mz lb apply / mz lb remove commands are dispatched for you and take a machine-built spec, not hand-typed flags.

  • GET /v1/load-balancers — list every load balancer and its backends.
  • POST /v1/load-balancers — create one with its backend pool.
  • POST /v1/load-balancers/:id/backends — attach a backend; PATCH …/backends/:bid drains or reweights it; DELETE …/backends/:bid removes it.
  • POST /v1/load-balancers/:id/apply — re-apply the current definition to the node.

See the API reference (API access is a Max-plan feature). The node-side commands are catalogued in the command reference.

  • The LB node must be a managed (agent) server. An agentless / Lite box can’t run the engine, so it can’t be picked as the node — the API rejects it.
  • Engine and LB node are locked after creation. Editing changes routing, TLS and health checks only; to move the endpoint to a different server or switch Nginx ↔ HAProxy, delete and recreate.
  • “Pending apply” that never clears usually means the node is offline. The apply job is queued and runs when the node reconnects; check the node’s connection first.
  • Nginx has no per-backend traffic stats. With the Nginx engine, health is passive and per-backend request rates aren’t measured (shown as ”—”, never faked). Choose HAProxy if you need active checks and real per-backend traffic numbers.
  • Draining ≠ removing. A drained backend always reads draining regardless of its probe and takes no new traffic, but stays attached — undrain it to return it to rotation.
  • Deleting frees the endpoint immediately but leaves the backend servers running; only the load balancer in front of them is removed.
  • Private network — a WireGuard mesh so the LB node reaches its backends privately.
  • Uptime monitoring — the prober that also feeds Nginx-engine backend health.
  • Your servers — the fleet the LB node and backends come from.