Grafana Plugin
TraceHouse ships as a Grafana app plugin (dmkskd-tracehouse-app). It reuses the standalone frontend (same page components, same shared packages) and wraps it with the plugin scaffolding needed to run inside Grafana's sandbox: plugin.json, module.ts, webpack config, and adapter shims.
The key difference from the standalone app is how ClickHouse connectivity works. The standalone app connects directly (via the built-in proxy or user-provided credentials), while the Grafana plugin routes all queries through Grafana's ClickHouse datasource plugin. ClickHouse connectivity is managed entirely by Grafana's datasource configuration, not by TraceHouse itself.
All pages are available as Grafana navigation entries: Overview, Engine Internals, Cluster, Explorer, Time Travel, Events, Queries, Merges, Replication, and Analytics.
Running with Docker
The easiest way to run TraceHouse inside Grafana is with the full Docker Compose profile:
just docker-start-full
This builds the plugin first (just grafana-plugin-build), then starts ClickHouse, Prometheus, and Grafana together. Grafana is available at http://localhost:3001.
The Grafana service mounts the built plugin from grafana-app-plugin/dist/ and auto-installs the ClickHouse datasource plugin. Anonymous auth is enabled for local development.
To start the same stack without installing or mounting the TraceHouse app plugin, use:
just docker-start-full-no-plugin
This still starts ClickHouse, Prometheus, Grafana, and Tempo, installs the Grafana ClickHouse datasource plugin, and provisions the local ClickHouse datasource. TraceHouse is left absent so it can be installed separately from the Grafana plugin catalog.
Testing against different Grafana versions
just docker-start-full accepts an optional Grafana image tag:
just docker-start-full 11.6.3
just docker-start-full 13.0.3
just docker-start-full latest
The default is 13.0.3. The background and refresh recipes accept the same argument:
just docker-start-full-bg 11.6.3
just docker-refresh-grafana-plugin 11.6.3
just docker-refresh-grafana-plugin-fast 11.6.3
When switching across major versions or downgrading Grafana, clear the persisted Grafana data volume to avoid database migration conflicts:
just docker-stop
docker volume rm tracehouse_tracehouse-grafana-data
just docker-start-full 11.6.3
Running the exact release plugin locally
just docker-start-full mounts your local grafana-app-plugin/dist/ directory. To reproduce what users are testing from a GitHub release, run Grafana with the packaged release ZIP instead:
just docker-start-full-release v0.16.12
This downloads the versioned plugin ZIP, such as dmkskd-tracehouse-app-0.16.12.zip, from the release, extracts it under /private/tmp, and mounts that extracted plugin directory into Grafana. It does not rebuild the plugin locally.
You can also run a ZIP you already have:
just docker-start-full-plugin-zip /path/to/dmkskd-tracehouse-app-0.16.12.zip
Both commands accept the optional Grafana version argument:
just docker-start-full-release v0.16.12 13.0.3
just docker-start-full-plugin-zip /path/to/dmkskd-tracehouse-app-0.16.12.zip dev-preview-react19
Development with watch mode
For iterating on the plugin:
# Terminal 1: start infra (builds the plugin once, then starts everything)
just docker-start-full
# Terminal 2: watch mode, rebuilds on file changes
just grafana-plugin-dev
Reload the Grafana page after each rebuild.
How the Integration Works
- Plugin type: Grafana
appplugin, registered viaAppPluginfrom@grafana/runtimeinmodule.ts - Pages: Declared in
plugin.jsonunderincludes[]. Each page gets a Grafana nav entry at/a/dmkskd-tracehouse-app/<slug> - Data flow: Queries go through Grafana's
/api/ds/querybackend endpoint viaGrafanaAdapter(inpackages/core), using whichever ClickHouse datasource the user selects - Configuration: Admin settings (refresh rates) stored in Grafana plugin
jsonData, accessed at runtime viaPluginConfigContext - Theme bridge: Reads
config.theme2.isDarkfrom Grafana and sets thedata-themeattribute on<html>so the frontend's CSS variables match Grafana's theme - Shared code: Pages and utilities come from workspace packages (
@tracehouse/core,@tracehouse/ui-shared) and fromfrontend/src/via webpack aliases. The plugin reuses the same page components as the standalone app
Workarounds
- No React Router: Grafana's plugin sandbox doesn't provide a Router context. The plugin stubs out
react-router-domentirely (seesrc/stubs/react-router-dom.tsx) and replaces navigation hooks with a customLocationContext-based implementation (src/hooks/useAppLocation.ts) - Store aliasing: The standalone app's
connectionStoreandClickHouseProviderare aliased at webpack level to Grafana-compatible shims that source the connection from the selected datasource instead of user-entered credentials useUrlStatevialocationService: Analytics state syncs to URL query params using Grafana'slocationService, so links like/a/dmkskd-tracehouse-app/analytics?tab=misc&preset=3are fully shareable, same as in the standalone app- AMD output format: Grafana's plugin loader requires AMD modules. The webpack config sets
output.library.type: 'amd'withuniqueName: 'dmkskd-tracehouse-app'for namespace isolation - Node.js polyfill stubs: Webpack config stubs all Node.js core modules (
stream,zlib,crypto, etc.) tofalsesince the plugin runs in the browser but some transitive dependencies reference them - Unsigned plugin allowlist: For local development,
GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=dmkskd-tracehouse-appis set in the Docker Compose config
Building the Plugin
# One-off build
just grafana-plugin-build
# Output: grafana-app-plugin/dist/ (module.js, plugin.json, README.md)