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, and zipkinUrl — there is no per-port credential.
  • In horizon-wire.jsonl (when debugLog.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

  • zipkinUrl not updated for shared-port deploys. The default 9412 is the standalone OAP. Docker images typically route Zipkin under the same port as query (/zipkin).
  • adminUrl pointing at the query port. Admin endpoints 404 — UI surfaces “admin host unreachable”. Verify the port matches OAP’s admin-server.default.port (default 17128).
  • auth block 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. Check horizon-wire.jsonl.