Skip to content

feat(grafana): add support for multiple Grafana instances - #7527

Merged
vinzscam merged 16 commits into
backstage:mainfrom
andreahlert:feat/grafana-multiple-instances
May 5, 2026
Merged

feat(grafana): add support for multiple Grafana instances#7527
vinzscam merged 16 commits into
backstage:mainfrom
andreahlert:feat/grafana-multiple-instances

Conversation

@andreahlert

@andreahlert andreahlert commented Feb 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #1293

Adds the ability to configure multiple Grafana instances, allowing organizations with separate Grafana deployments (e.g., production and staging) to use them all within Backstage.

  • New grafana.hosts[] configuration with per-host domain, proxyPath, unifiedAlerting, and search settings
  • New grafana/host-id entity annotation to associate entities with specific Grafana instances
  • Merged API client to handle unified and legacy alerting per-host (not globally)
  • Full backward compatibility with existing single-instance grafana.domain configuration
  • Proxy path collision detection to prevent silent misconfiguration

Changes

Core:

  • config.d.ts - Added hosts[] config schema
  • src/types.ts - Added GrafanaHost interface
  • src/constants.ts - Added GRAFANA_ANNOTATION_HOST_ID and hostIdFromEntity()
  • src/api.ts - Merged two API client classes into one multi-host client with isUnifiedAlerting() per-host resolution
  • src/config.ts - Shared readHosts() utility with validation
  • src/plugin.ts / src/alpha/apis.ts - Updated factories using shared config

Components:

  • AlertsCard.tsx - Uses per-host isUnifiedAlerting() instead of global config
  • DashboardsCard.tsx - Passes hostId to API calls

Tests (+24 new tests):

  • api.test.ts - Host resolution, isUnifiedAlerting per-host, fallback, error cases
  • config.test.ts - Config reading, validation, proxy path collision
  • constants.test.ts - hostIdFromEntity with/without annotation
  • AlertsCard.test.tsx - Integration tests for per-host unified alerting selector flow

Docs:

  • setup.md - Multi-instance configuration guide
  • alerts-on-component-page.md / dashboards-on-component-page.md - grafana/host-id usage

Configuration example

proxy:
  '/grafana/production/api':
    target: https://grafana-prod.host/
    headers:
      Authorization: Bearer ${GRAFANA_PROD_TOKEN}
  '/grafana/staging/api':
    target: https://grafana-staging.host/
    headers:
      Authorization: Bearer ${GRAFANA_STAGING_TOKEN}

grafana:
  hosts:
    - id: production
      domain: https://monitoring-prod.company.com
      proxyPath: /grafana/production/api
      unifiedAlerting: true
    - id: staging
      domain: https://monitoring-staging.company.com
      proxyPath: /grafana/staging/api
      unifiedAlerting: false
# catalog-info.yaml
metadata:
  annotations:
    grafana/host-id: production
    grafana/dashboard-selector: my-service
    grafana/alert-label-selector: service=my-service

Test plan

  • All existing tests pass (8 suites, 37 tests)
  • TypeScript compilation passes
  • Lint passes
  • API reports regenerated without warnings
  • Build succeeds
  • Verify single-instance config still works (backward compat)
  • Verify multi-instance config routes to correct Grafana instances
  • Verify entity without grafana/host-id falls back to default host

@andreahlert
andreahlert requested a review from a team as a code owner February 8, 2026 09:08
@andreahlert
andreahlert requested review from vinzscam and removed request for a team February 8, 2026 09:08
@backstage-goalie

backstage-goalie Bot commented Feb 8, 2026

Copy link
Copy Markdown
Contributor

Changed Packages

Package Name Package Path Changeset Bump Current Version
@backstage-community/plugin-grafana workspaces/grafana/plugins/grafana minor v0.18.0

@backstage-goalie

Copy link
Copy Markdown
Contributor

Thanks for the contribution!
All commits need to be DCO signed before they are reviewed. Please refer to the the DCO section in CONTRIBUTING.md or the DCO status for more info.

Add the ability to configure multiple Grafana instances under a
`grafana.hosts` config key. Each host can have its own domain,
proxy path, and alerting mode. Entities are associated with a
specific instance via the `grafana/source-id` annotation.

Key changes:
- New `grafana.hosts[]` config schema with per-host settings
- New `grafana/source-id` entity annotation
- Merged GrafanaApiClient to handle both unified and legacy
  alerting per-host instead of globally
- AlertsCard resolves unifiedAlerting per-host, not from global config
- Shared readHosts() config utility with proxy path validation
- Full backward compatibility with single-instance `grafana.domain`

Signed-off-by: André Ahlert <andre@aex.partners>
@andreahlert
andreahlert force-pushed the feat/grafana-multiple-instances branch from 388de1b to 4eb589f Compare February 8, 2026 09:14
@andreahlert

Copy link
Copy Markdown
Contributor Author

Hey @awanlin, this implements the multiple Grafana instances support from #1293.

I based the approach on K-Phoen/backstage-plugin-grafana#76 but adapted it to the current codebase, with some improvements: per-host unifiedAlerting resolution (the original had a bug where AlertsCard used the global config), proxy path collision detection, and test coverage for the multi-instance flow.

I've been actively contributing to community-plugins lately - created the n8n workspace (#7522), submitted a few fixes for sentry (#7523, #7525, #7526), and now this. I've been focusing on workspaces that could use some extra attention, and I'd be happy to help maintain this one going forward.

@andreahlert

Copy link
Copy Markdown
Contributor Author

Hi @awanlin, this PR implements multi-instance Grafana support as requested in #1293. I've kept full backward compatibility with the existing single-instance config while adding the new grafana.hosts[] array.

I've also opened #7528 adding unit test coverage for the API clients (GrafanaApiClient and UnifiedAlertingGrafanaApiClient), which previously had none.

Would you be able to review or suggest another reviewer? Happy to address any feedback.

@awanlin

awanlin commented Feb 8, 2026

Copy link
Copy Markdown
Contributor

Hi @andreahlert, while we appreciate contributions, let's hold off on conversations regarding ownership changes.

I'd also suggest reading our Contributing guide as that covers the review process: https://github.com/backstage/community-plugins/blob/main/CONTRIBUTING.md#review-process. I'd also suggest you read over the AI Policy in the upstream Backstage repo as that very much applies here as well: https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md#ai-use-policy-and-guidelines.

This repo is volunteer run, reviews will be slow as they are based on Plugin Owner availability. Traditionally I only have time for them on Friday afternoons. I just happen to be doing some work and spotted your PRs yesterday and wanted to help set you on the right footing as they had some good and not good aspects to them. Today I'm just following up as I was ping multiple times and wanted to know why. 👍

@andreahlert

Copy link
Copy Markdown
Contributor Author

Hi @andreahlert, while we appreciate contributions, let's hold off on conversations regarding ownership changes.

I'd also suggest reading our Contributing guide as that covers the review process: https://github.com/backstage/community-plugins/blob/main/CONTRIBUTING.md#review-process. I'd also suggest you read over the AI Policy in the upstream Backstage repo as that very much applies here as well: https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md#ai-use-policy-and-guidelines.

This repo is volunteer run, reviews will be slow as they are based on Plugin Owner availability. Traditionally I only have time for them on Friday afternoons. I just happen to be doing some work and spotted your PRs yesterday and wanted to help set you on the right footing as they had some good and not good aspects to them. Today I'm just following up as I was ping multiple times and wanted to know why. 👍

Hey @awanlin, noted on all points.

About the AI policy and Contributing Guide: I went through it, I take code quality seriously and like to keep things well documented, which might come across as over-structured sometimes.

I wasn't trying to push for ownership either, just wanted to show I'm around and interested in helping out.

And yeah, the multiple pings that's an annoying habit of mine, sorry about that 😅. I promise I'll behave, I don't want to be that guy who makes you regret checking GitHub on a Saturday. I set aside one day a week for Backstage stuff but got a bit carried away and spent some extra hours because I was enjoying the work.

I contribute to other repos too so I get how volunteer time works, so no rush on the review. Thanks for taking the time to point me in the right direction.

@vinzscam vinzscam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you!

Comment thread workspaces/grafana/plugins/grafana/src/config.ts Outdated
Comment thread workspaces/grafana/plugins/grafana/src/config.ts Outdated
Comment thread workspaces/grafana/plugins/grafana/src/api.ts
Comment thread workspaces/grafana/plugins/grafana/report.api.md Outdated
Comment thread workspaces/grafana/plugins/grafana/config.d.ts Outdated
Comment thread workspaces/grafana/plugins/grafana/config.d.ts
@andreahlert
andreahlert force-pushed the feat/grafana-multiple-instances branch from 2dee0ac to 750a182 Compare February 11, 2026 21:27
Comment thread workspaces/grafana/plugins/grafana/docs/setup.md Outdated
Comment thread workspaces/grafana/plugins/grafana/config.d.ts
Comment thread workspaces/grafana/plugins/grafana/config.d.ts
@andreahlert
andreahlert force-pushed the feat/grafana-multiple-instances branch 3 times, most recently from b86856b to 4f88f27 Compare February 23, 2026 07:20
@andreahlert
andreahlert force-pushed the feat/grafana-multiple-instances branch from f33b999 to 261ff2b Compare February 23, 2026 08:10
andreahlert and others added 8 commits February 23, 2026 07:02
- Remove custom GrafanaConfigApi interface, use ConfigApi from @backstage/core-plugin-api

- When both grafana.domain and grafana.hosts are set, ignore domain and log warning

- Remove @public from GrafanaApiClient class

- Rename grafana/source-id to grafana/host-id annotation

- Remove per-host grafanaDashboardSearchLimit/grafanaDashboardMaxPages, keep global only

- Mark global grafanaDashboardSearchLimit/grafanaDashboardMaxPages as @deprecated

Signed-off-by: André Ahlert <andre@aex.partners>
Co-authored-by: Vincenzo Scamporlino <vincenzos@spotify.com>
Signed-off-by: André Ahlert <andre@aex.partners>
Signed-off-by: André Ahlert <andre@aex.partners>
Signed-off-by: André Ahlert <andre@aex.partners>
Add eslint-disable comments for intentional use of deprecated config keys
(grafana.domain, grafana.proxyPath, grafana.unifiedAlerting,
grafanaDashboardSearchLimit, grafanaDashboardMaxPages) so CI list-deprecations
check passes while keeping backward compatibility.

- Fix indent of eslint-enable in config.ts
- Use dashboardSearchLimit/dashboardMaxPages in GrafanaApiClientOptions
  to match plugin.ts and apis.ts (fix type checking).

Signed-off-by: André Ahlert <andre@aex.partners>
Add defaultHostId to GrafanaApiClientOptions in report.api.md so
check api reports and generate API reference CI step passes.

Signed-off-by: André Ahlert <andre@aex.partners>
- Regenerated report.api.md and report-alpha.api.md using backstage-repo-tools\n- Includes the new defaultHostId property in GrafanaApiClientOptions\n- Fixes CI validation failure on 'check api reports and generate API reference'

Signed-off-by: André Ahlert <andre@aex.partners>
Regenerate report.api.md and report-alpha.api.md to match the current API Extractor output used in CI, fixing the build:api-reports:only --ci failure on PR 7527.

Signed-off-by: André Ahlert <andre@aex.partners>
Update EntityGrafanaAlertsCard and EntityGrafanaDashboardsCard signatures in report.api.md to match API Extractor output from CI (Node 22/24), resolving the remaining api report mismatch.

Signed-off-by: André Ahlert <andre@aex.partners>
Update report-alpha.api.md from API Extractor output generated under Node 22/24 to match CI expectations and fix build:api-reports:only --ci failures.

Signed-off-by: André Ahlert <andre@aex.partners>
@andreahlert
andreahlert requested a review from vinzscam March 2, 2026 20:17
Resolve add/add conflict in api.test.ts by combining both test suites
and format with prettier.

Signed-off-by: André Ahlert <andre@aex.partners>
@andreahlert
andreahlert force-pushed the feat/grafana-multiple-instances branch from bb6fdd5 to e4e7f4a Compare March 11, 2026 20:52
@andreahlert
andreahlert requested a review from robbat2 March 11, 2026 21:01
@andreahlert andreahlert self-assigned this Mar 16, 2026
@awanlin

awanlin commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Hi @andreahlert, a conflict crept in, maybe a rebase should help sort that out 👍

@awanlin

awanlin commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Hi @vinzscam, can you give this another look, please?

Sync with latest upstream changes:
- Update createApiRef to new .with() syntax
- Regenerate report.api.md with updated ApiRef type signature
- Pick up grafana dependency bumps (fast-xml-parser, flatted)

Signed-off-by: André Ahlert <andre@aex.partners>
@andreahlert
andreahlert force-pushed the feat/grafana-multiple-instances branch from 130a828 to 0bf723f Compare April 8, 2026 20:27

@robbat2 robbat2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vinzscam please approve

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds multi-instance Grafana support to the @backstage-community/plugin-grafana workspace plugin by introducing host-aware configuration + entity-level host selection, while keeping the legacy single-instance config working.

Changes:

  • Introduces grafana.hosts[] + grafana.defaultHost config parsing/validation and wires it into both classic and @alpha API factories.
  • Extends the Grafana API/client to resolve a host per request and handle unified vs legacy alerting on a per-host basis.
  • Updates entity cards/components + docs/tests to pass and document a grafana/host-id annotation.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
workspaces/grafana/plugins/grafana/src/types.ts Adds GrafanaHost type used across config/client.
workspaces/grafana/plugins/grafana/src/plugin.ts Switches plugin API factory to readHosts() + new multi-host client options.
workspaces/grafana/plugins/grafana/src/index.ts Exports new public GrafanaApiClientOptions type.
workspaces/grafana/plugins/grafana/src/constants.ts Adds grafana/host-id annotation constant + helper.
workspaces/grafana/plugins/grafana/src/constants.test.ts Adds tests for hostIdFromEntity.
workspaces/grafana/plugins/grafana/src/config.ts New config reader/validator for legacy + multi-host configs.
workspaces/grafana/plugins/grafana/src/config.test.ts New tests for config parsing/validation behavior.
workspaces/grafana/plugins/grafana/src/components/DashboardsCard/DashboardsCard.tsx Passes entity host id through to dashboard API calls.
workspaces/grafana/plugins/grafana/src/components/AlertsCard/AlertsCard.tsx Uses per-host isUnifiedAlerting() and passes host id to alerts API calls.
workspaces/grafana/plugins/grafana/src/components/AlertsCard/AlertsCard.test.tsx Adds integration tests ensuring host-aware unified/legacy selector behavior.
workspaces/grafana/plugins/grafana/src/api.ts Merges clients into a single multi-host client + adds isUnifiedAlerting.
workspaces/grafana/plugins/grafana/src/api.test.ts Expands tests for host resolution + unified/legacy alerting behavior.
workspaces/grafana/plugins/grafana/src/alpha/entityCards.test.tsx Updates Grafana API mock to include isUnifiedAlerting.
workspaces/grafana/plugins/grafana/src/alpha/apis.ts Updates alpha API factory to use readHosts() and new client options.
workspaces/grafana/plugins/grafana/src/fixtures/entity.ts Adds a fixture entity including grafana/host-id.
workspaces/grafana/plugins/grafana/report.api.md Updates API report for new exports/types.
workspaces/grafana/plugins/grafana/docs/setup.md Documents multi-instance config + grafana/host-id.
workspaces/grafana/plugins/grafana/docs/dashboards-on-component-page.md Documents selecting host for dashboards via annotation.
workspaces/grafana/plugins/grafana/docs/alerts-on-component-page.md Documents selecting host for alerts via annotation.
workspaces/grafana/plugins/grafana/config.d.ts Extends config schema for hosts[] + defaultHost, deprecates legacy keys.
workspaces/grafana/.changeset/grafana-multiple-instances.md Declares a minor release with multi-instance support.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread workspaces/grafana/plugins/grafana/src/api.ts
Comment thread workspaces/grafana/plugins/grafana/docs/setup.md
Comment thread workspaces/grafana/plugins/grafana/src/constants.test.ts
Comment thread workspaces/grafana/plugins/grafana/src/config.ts Outdated
Comment thread workspaces/grafana/plugins/grafana/src/config.ts Outdated
Comment thread workspaces/grafana/plugins/grafana/src/config.ts Outdated
Copilot AI review requested due to automatic review settings May 5, 2026 09:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 21 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

workspaces/grafana/plugins/grafana/src/components/DashboardsCard/DashboardsCard.tsx:112

  • useAsync is called without a dependency array even though the dashboards query depends on entity annotations (selector + host-id) and opts.additionalDashboards. If the entity/annotations change without a full remount, this hook won’t refetch and can show stale data or query the wrong Grafana instance. Consider supplying an explicit deps array (e.g. based on dashboardSelectorFromEntity(entity), hostIdFromEntity(entity), and opts.additionalDashboards).
  const grafanaApi = useApi(grafanaApiRef);
  const { value, loading, error } = useAsync(async () => {
    const dashboards = await grafanaApi.listDashboards(
      dashboardSelectorFromEntity(entity),
      hostIdFromEntity(entity),
    );
    if (opts?.additionalDashboards) {
      dashboards.push(...opts.additionalDashboards(entity));
    }
    return dashboards;
  });

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread workspaces/grafana/plugins/grafana/src/config.ts
Comment thread workspaces/grafana/plugins/grafana/src/api.ts Outdated
Comment thread workspaces/grafana/plugins/grafana/src/api.ts
Comment thread workspaces/grafana/plugins/grafana/src/components/AlertsCard/AlertsCard.tsx Outdated
Copilot AI review requested due to automatic review settings May 5, 2026 10:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 21 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread workspaces/grafana/plugins/grafana/src/config.ts
Comment thread workspaces/grafana/plugins/grafana/src/config.ts
Comment thread workspaces/grafana/plugins/grafana/src/components/AlertsCard/AlertsCard.tsx Outdated
Comment thread workspaces/grafana/plugins/grafana/src/constants.ts
- Use typed ConfigApi getters for grafana.hosts parsing
- Validate unique host ids and non-empty id/domain fields
- Fix eslint-disable scope for deprecation rule
- Guard isUnifiedAlerting against synchronous throws in AlertsCard
- Remove unused identityApiRef dependency in alpha API
- Use exported constant in tests instead of literal string

Signed-off-by: André Ahlert <andre@aex.partners>
GrafanaApiClient is a public export and can be constructed directly,
bypassing readHosts validation. Throw explicitly on duplicate host ids
in initClients so silent overwrites surface as misconfiguration.

Signed-off-by: André Ahlert <andre@aex.partners>
- Validate `defaultHostId` exists in constructor and stop silently falling back to first host when an explicit defaultHostId is unknown.
- Iterate every selector in legacy alerting instead of dropping all but the first when an array is passed.
- Skip alert fetching and add an explicit dependency array in `AlertsCard` so the request reruns when the entity annotations change and short-circuits when host resolution fails.
- Share `DEFAULT_PROXY_PATH` between `api.ts` and `config.ts` to keep the proxy collision validation aligned with the runtime default.

Signed-off-by: André Ahlert <andre@aex.partners>
@andreahlert
andreahlert force-pushed the feat/grafana-multiple-instances branch from dd530e0 to 5d843cb Compare May 5, 2026 11:04
@andreahlert

Copy link
Copy Markdown
Contributor Author

@vinzscam

@vinzscam vinzscam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vinzscam
vinzscam merged commit 25ddfa9 into backstage:main May 5, 2026
12 checks passed
@vinzscam

vinzscam commented May 5, 2026

Copy link
Copy Markdown
Member

thank you @andreahlert 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🚀 Grafana: Support to multiple instances

6 participants