OAP Connection
Connectivity to the upstream Apache SkyWalking OAP cluster. Required for everything except the login page.
oap:
queryUrl: http://127.0.0.1:12800
adminUrl: http://127.0.0.1:17128
zipkinUrl: http://127.0.0.1:9412/zipkin
traceql:
url: http://127.0.0.1:3200
nativePath: /skywalking
zipkinPath: /zipkin
otlpPath: /otlp
timeoutMs: 15000
auth:
username: skywalking
password: "${HORIZON_OAP_PW}"
Fields
| Field | Type | Default | Required | Notes |
|---|---|---|---|---|
queryUrl |
URL string | http://127.0.0.1:12800 |
no | OAP GraphQL query endpoint. Load-balanceable — any OAP node answers. Used by all read pages. Must be a valid URL. |
adminUrl |
URL string | http://127.0.0.1:17128 |
no | OAP admin REST endpoint. Hosts runtime-rule, dsl-debugging, inspect, status, debugging/config endpoints. Single URL; OAP handles cluster-internal fan-out. |
zipkinUrl |
URL string | http://127.0.0.1:9412/zipkin |
no | Zipkin v2 REST endpoint. Used when a layer exposes the Zipkin trace store. Defaults assume the standalone Armeria binding; for Docker / shared-port deployments use <queryUrl>/zipkin. |
traceql.url |
URL string | http://127.0.0.1:3200 |
no | OAP’s TraceQL (Grafana Tempo API) service — host and port only, no path. One server answers for every datasource. OAP ships the module off, so a row reading from it says it cannot be reached until OAP enables it. url: '' in this file turns it off; an empty HORIZON_OAP_TRACEQL_URL falls back to the default. See TraceQL trace stores. |
traceql.nativePath |
string | /skywalking |
no | Context path of the datasource over the SkyWalking-native spans — OAP’s restContextPathSkywalking. |
traceql.zipkinPath |
string | /zipkin |
no | Context path of the datasource over the Zipkin spans — OAP’s restContextPathZipkin. |
traceql.otlpPath |
string | /otlp |
no | Context path of the datasource over the OTLP spans OAP stored as they arrived — OAP’s restContextPathOTLP. Needs receiver-otel.otlpTraceStorage: otlp. |
timeoutMs |
number | 15000 |
no | Per-request HTTP timeout (milliseconds) for all OAP calls. Applies to query, admin, Zipkin. Must be positive integer. |
auth.username |
string | — | required if auth block present |
Basic-auth username. Sent on every outbound OAP call. |
auth.password |
string | — | required if auth block present |
Basic-auth password. Sent on every outbound OAP call. Use ${VAR} interpolation, not a literal. |
How the BFF uses each URL
| URL | Hit by |
|---|---|
queryUrl |
GraphQL (version, getTimeInfo, checkHealth, listLayers, listServices, getMenuItems, listLayerLevels, execExpression, alarm queries, trace queries, log queries, topology queries, profiling queries). |
adminUrl |
/debugging/config/dump, /runtime/rule/*, /dsl-debugging/*, /inspect/metrics, /inspect/entities, /status/alarm/*, and — in live template mode — /ui-management/templates*. |
zipkinUrl |
Zipkin v2 trace queries, for a layer that exposes the Zipkin trace store. |
traceql.* |
TraceQL searches, tag and tag-value lookups, and trace-by-id reads, for a layer that exposes a TraceQL trace store. |
queryUrl is always required. adminUrl is required for OAP 11 admin features and for Horizon’s live template mode; it is not required for an OAP 10 deployment running templates.mode: readonly. Configured query and admin URLs are health-checked independently. See Cluster Status Check Sequence for the per-pane behavior.
TraceQL trace stores (oap.traceql)
OAP can answer for its traces in Grafana Tempo’s query language over Tempo’s HTTP API, with one datasource per underlying store: the SkyWalking-native spans, and the Zipkin spans (which also carry OpenTelemetry traces OAP converted). Each datasource is a separate context path on OAP’s TraceQL server, so Horizon takes one full URL each rather than deriving them — and either one alone is a valid configuration.
It is a separate server from the query port, off in OAP by default, and enabled there with:
SW_TRACEQL=default
SW_TRACEQL_ENABLE_DATASOURCE_SKYWALKING=true
SW_TRACEQL_ENABLE_DATASOURCE_ZIPKIN=true
SW_TRACEQL_ENABLE_DATASOURCE_OTLP=true
Its default port is 3200, with the context paths /skywalking, /zipkin and /otlp. Since OAP 11.1.0, OpenTelemetry traces are stored natively by default and read only through /otlp. Point Horizon at the ones you enabled:
oap:
traceql:
url: http://<oap-host>:3200
nativePath: /skywalking
zipkinPath: /zipkin
otlpPath: /otlp
One endpoint, one path per datasource. OAP’s TraceQL service binds a single host and port (restHost / restPort, 3200 by default) and serves each datasource under its own context path, so Horizon takes the endpoint once and the paths default to OAP’s own. Change a path only if you changed restContextPath* on OAP.
Setting url is the whole switch. It defaults to http://127.0.0.1:3200, as the other OAP hosts default to the local OAP. Horizon probes each datasource: one this OAP does not enable answers 404 and is reported on the Cluster Status page with what to switch on, beside the ones that are genuinely down. Setting url: '' in the configuration file turns the feature off (an empty HORIZON_OAP_TRACEQL_URL falls back to the default) — a layer’s sidebar row still appears if its template names the store, and the tab states that no URL is configured rather than searching something else. A configured URL that does not answer is reported the same way, on the page rather than in a log.
Which layers expose these stores is a layer-template decision, not a connection one — see Layer Dashboard Templates → traces and Traces.
Basic auth handling
When auth.username and auth.password are set:
- Every outbound HTTP request includes
Authorization: Basic <base64(user:pass)>. - The header is applied identically to
queryUrl,adminUrl, andzipkinUrl— there is no per-port credential. - In
horizon-wire.jsonl(whendebugLog.enabled: true), the header is redacted by default. See debugLog.
Production deployments should pull credentials from the environment rather than committing them to horizon.yaml:
oap:
auth:
username: "${HORIZON_OAP_USER}"
password: "${HORIZON_OAP_PW}"
OAP capability probing
Horizon introspects selected optional GraphQL fields on first use and caches the result per BFF process lifetime. This currently provides an alarm-query fallback; it is not a general compatibility layer for every schema difference. See OAP Version for the exact v10 limitations.
| Capability | Probed |
|---|---|
queryAlarms (modern alarm query with server-side layer filter) |
First alarms request. If missing → falls back to legacy getAlarm and filters client-side. |
getMenuItems field set (per OAP version) |
First menu request. |
The cache is per-process. After a BFF restart, the next request re-probes.
Hot reload
Changes to any oap.* field are picked up on file change. The next outbound call uses the new value. Exception: capability cache is process-lifetime — flipping a feature on OAP that requires re-introspection needs a BFF restart.
Common mistakes
zipkinUrlnot updated for shared-port deploys. The default9412is the standalone OAP. Docker images typically route Zipkin under the same port as query (/zipkin).adminUrlpointing at the query port. Admin endpoints 404 — UI surfaces “admin host unreachable”. Verify the port matches OAP’sadmin-server.default.port(default 17128).authblock present but credentials wrong. OAP responds 401 on every query — UI shows “OAP unreachable” because Horizon does not distinguish 401 from 5xx in the banner. Checkhorizon-wire.jsonl.