Setup Quick Start
This page is the shortest path from “no Horizon” to “Horizon in front of a running OAP”. It uses the released binary layout first. For containerized deployments, use Container Image.
Prerequisites
- Apache SkyWalking OAP 11.x (native). OAP 10.x runs the data-plane stack only with
templates.mode: readonly, and subject to minor-specific trace and endpoint limitations. OAP 10 does have persistent UI-template management through legacy query-port GraphQL, but Horizon implements only OAP 11’s/ui-management/templates*REST protocol; the defaultlivemode therefore cannot read the v10 template store, and rather than render a layer whose published template it cannot read, it blocks layer-driven pages (most visibly Traces) behind the template-store banner. The admin-port features — Inspect, DSL Management, Live Debugger, Alarm Rule editor, and Cluster Status → Admin pane — are v11-only. See Compatibility → OAP Version for the exact feature matrix and v10 recipe. - Network reachability from the Horizon BFF to the OAP query port (
:12800). The admin port (:17128) is additionally required for OAP 11 admin features and live template mode, but not for OAP 10 readonly operation. See Network Ports. - A recent LTS Node.js runtime for the binary tarball. Source builds also need pnpm (pinned via Corepack).
Five-step start
1. Unpack Horizon
Unpack the binary tarball (substitute the release version you downloaded for <version>):
tar -xzf apache-skywalking-horizon-ui-<version>-bin.tar.gz
cd apache-skywalking-horizon-ui-<version>-bin
The binary is self-contained: server.js, node_modules/, static/, bundled templates, and the config horizon.yaml are already present. There is no pnpm install step. horizon.yaml is env-driven — every field is a ${HORIZON_…:default} variable, so you can leave the file as-is and set only the environment variables you need (starting with your OAP address), or edit the file directly.
2. Point Horizon at OAP
Edit the oap block:
oap:
queryUrl: http://<oap-host>:12800
adminUrl: http://<oap-host>:17128
zipkinUrl: http://<oap-host>:9412/zipkin # only if using Zipkin
If OAP requires basic auth (the public demo does):
oap:
auth:
username: skywalking
password: skywalking
3. Add at least one local user
With no users configured, Horizon starts but no login can succeed. Generate an Argon2id hash with the source checkout helper or any Argon2id-capable password tool:
pnpm --filter bff cli:hash
Paste the hash into auth.local.users:
auth:
backend: local
local:
users:
- username: admin
passwordHash: "$argon2id$v=19$..."
roles: [admin]
For LDAP setup instead, see Access Control → LDAP Backend.
4. Start the BFF
From inside the unpacked binary directory:
HORIZON_CONFIG=./horizon.yaml node server.js
Horizon defaults to 127.0.0.1:8081. For production, bind to 0.0.0.0 and put TLS termination in front:
server:
host: 0.0.0.0
port: 8081
session:
cookieSecure: true
5. Open the UI
Browse to http://<bff-host>:8081/. Log in with the user you created. The first thing to check is the Cluster Status page (/operate/cluster):
- Query pane should be green — version, timezone, health score visible.
- Admin pane should be green if you set
SW_ADMIN_SERVER=defaultand the rest of the selectors on OAP.
If either pane is red or yellow, see Cluster Status Check Sequence for triage.
Container start
For Docker or Kubernetes, mount the same horizon.yaml and /data state volume:
docker run -d --name horizon \
-p 8081:8081 \
-v "$PWD/horizon.yaml:/app/horizon.yaml:ro" \
-v horizon-state:/data \
ghcr.io/apache/skywalking-horizon-ui:<version>
See Container Image for image tags, Kubernetes YAML, log handling, and probes.
Source build
Use source builds when you are developing Horizon itself:
pnpm install
pnpm package
HORIZON_CONFIG=./horizon.yaml node dist/server.js
Production checklist
-
server.host: 0.0.0.0and TLS terminator in front. -
session.cookieSecure: true. -
auth.local.usersempty in production (use LDAP) or all passwords are strong + hashes never in version control. -
audit.filewrites to durable storage (not a container tmpfs). -
debugLog.enabled: false(or rotate aggressively). - OAP credentials, LDAP bind password, and break-glass hash use
${ENV_VAR}interpolation, not literal values. - Container readiness probe wired to the public
GET /api/health(not/api/oap/info, which is authenticated and returns 401 to an unauthenticated probe).
Hot reload
horizon.yaml is watched. Most changes apply without restarting the BFF:
- Auth backend switch: applies on next login.
- RBAC role redefinition: applies on next route call.
- OAP URL change: applies on next outbound call.
Some changes still require a BFF restart:
server.host/server.port(the listener has already bound).templates.mode(the template source is chosen at boot).- Anything that changes the capability cache — flipping a feature on the OAP side that Horizon probes once per process.
An edit that fails validation does not apply — the BFF logs an error naming each failing field and keeps serving the previous valid config. See horizon.yaml Reference → Hot reload behavior for the full list of live vs. restart-required fields.
Where things go
| Artifact | Path (default) | Override |
|---|---|---|
| Config | ./horizon.yaml |
HORIZON_CONFIG= |
| Audit log | ./horizon-audit.jsonl |
audit.file |
| Wire debug log | ./horizon-wire.jsonl |
debugLog.file |
| Bundled overview / layer templates | inside the BFF bundle | not user-editable as files; edit via admin pages (edits are stored in OAP’s ui_template store) |
All paths are resolved relative to the BFF working directory.