This guide gets you from zero to a running HAProxy UI in under 10 minutes. You will install dependencies, bring up a local HAProxy + Dataplane API stack, point the UI at it, and apply a real configuration change.
Who is this for?
Platform engineers and SREs evaluating HAProxy UI for their edge / ingress tier
Developers who want to try the graph editor without touching a production cluster
Prerequisites
Node.js 20+ and pnpm 9+
macOS, Linux, or Windows for the browser build
For the desktop build only: a Rust toolchain (rustup, stable channel) and the platform prerequisites below
Docker + Docker Compose for the local Dataplane stack (optional — the app runs fully against a built-in mock fixture with no cluster at all)
Step 1 — Get the code
git clone https://gitlab.starstorm.dev/starstorm/ha-proxy-ui.gitcd ha-proxy-uipnpm install
Platform prerequisites for the desktop build:
macOS — Xcode Command Line Tools (
xcode-select --install)Linux —
libwebkit2gtk-4.1-dev,libgtk-3-dev,libayatana-appindicator3-dev,librsvg2-dev,libsoup-3.0-dev,libssl-dev,patchelf,build-essential,curl,wget,fileWindows — WebView2 runtime (preinstalled on Windows 10+)
Step 2 — Run against the mock first
The fastest way to see the editor work is with no cluster at all. An instance with no baseUrl is served by mockClient, an in-memory fixture with a 150 ms simulated latency.
pnpm dev
Open http://localhost:5173. You should see the topology canvas with the fixture frontends, backends and servers already laid out. Drag a node, open its inspector, edit a field — everything works. Nothing has been sent anywhere.
Tip: The canvas is pan-and-pinch, not drag-and-drop-to-reparent in this build. Click a node to focus its inspector panel; edges follow from the Frontend → Backend → Server structure.
Step 3 — Bring up a local Dataplane stack
To exercise the real read/write/apply path you need an actual HAProxy and its Dataplane API. The bundled compose file runs haproxy:3.1 plus haproxytech/dataplaneapi:3.1:
docker compose up -d
Port | Service |
| HAProxy |
| Dataplane API |
| HAProxy stats page |
The local stack is seeded with HTTP Basic auth admin:admin and is otherwise unauthenticated-in-spirit — a development convenience only.
Step 4 — Point the UI at it
Copy the environment template and point the dev server at the Dataplane through the Vite proxy. This is the recommended browser setup because the browser then only ever makes same-origin requests — no CORS preflight, nothing to configure on the Dataplane side.
cp .env.example .envecho 'PUBLIC_DATAPLANE_BASE_URL=/dataplane' >> .envpnpm dev
The Vite dev server forwards /dataplane/* to http://localhost:5555/*. To send it somewhere else:
DATAPLANE_PROXY_TARGET=http://elsewhere:5555 pnpm dev
Leave PUBLIC_DATAPLANE_API_TOKEN empty for the local stack. Then create an instance in the sidebar with the URL /dataplane and Basic credentials admin:admin. The moment an instance has a baseUrl, the UI switches from mockClient to realClient.
Step 5 — Verify a read / write / apply cycle
Confirm the read path first — the canvas should now show your cluster's actual backends rather than the fixture.
curl -s http://localhost:5555/v3/services/haproxy/configuration/version -u admin:admin
Then, in the UI:
Open a backend node and change its balance algorithm.
The pending-changes bar appears at the bottom with your unsaved edit.
Open the diff panel — you should see a red/green diff against the live configuration.
Click Apply. The write goes to the Dataplane API scoped to a transaction, then a graceful reload is triggered.
Re-open the backend — the change persisted.
If the apply is rejected because the live config moved underneath you, you will get a conflict dialog offering Reload & re-diff rather than a generic error. See Operations & Troubleshooting.
Step 6 (optional) — Run the desktop build
The desktop app is the same UI packaged as a native window, with a Rust backend that brokers every outbound call.
pnpm dev:tauri # native window with HMR against the Vite dev serverpnpm build:tauri # signed installer for the host OS
In Tauri mode you do not need the Vite proxy or any CORS configuration: requests originate in Rust via reqwest, so any URL the host can reach works directly, including the absolute http://localhost:5555. Enter the absolute URL in the instance dialog. Tokens are resolved from the OS keychain inside Rust and never cross the webview boundary. Full checklist: docs/verify-desktop.md.
What's next
Architecture Overview — the CanonicalConfig model, the parse/normalize/assemble pipeline, and the Rust broker boundary
Configuration Guide — every inspector, every balance algorithm, and the fields behind them
Operations & Troubleshooting — apply, conflicts, GitOps, snapshots, and error remediation
Spec coverage gate — how the app proves it models the whole Dataplane v3 schema