Building a Grafana app plugin

A Grafana app plugin bundles pages, dashboards and data sources into one internal product. This page explains when that is worth building, how routing has changed since Grafana 11, and how to distribute it to several instances.

On this page

When building a Grafana app plugin is worth the effort

An internal platform team should build a Grafana app plugin only when it needs one place in Grafana that bundles pages, dashboards and data sources under a single identity, and when dashboards, folders and provisioning alone cannot give it that. An app plugin is a packaged unit of pages and configuration that Grafana loads as a plugin, so it suits a platform that behaves like a product inside Grafana. For most teams the decision starts with a simpler question: could the same content be delivered as provisioned dashboards in a folder, with a data source definition and a list of links? If that works and nobody needs custom pages or logic, the app plugin adds build, signing and upgrade work that a dashboard set does not.

An app plugin packages custom pages, bundled dashboards, data source configuration and, where needed, a backend component that runs server-side logic. It does not replace the underlying data sources, which still need their own configuration, and it does not replace access control in the Grafana instance. Users still see what their roles and folder permissions allow, so the plugin is a delivery and navigation layer, not a security boundary. Confirm in your own environment which Grafana edition and access model you run, because the authentication and team sync features your platform depends on vary by edition, as the Grafana authentication documentation sets out in Configure authentication.

Teams usually implement an app plugin when several of these hold: they want custom pages that dashboards cannot express, they want versioned releases of a set of content that ships together to multiple instances, or they need a backend that handles calls to an internal system on behalf of users. Before committing, the platform team should list every page and dashboard, name the owner of each, and confirm which Grafana version each target instance runs, since plugin APIs change between releases. Ask whether a plugin is needed for any of the requirements above, and record that answer in the scope before design starts.

What a Grafana app plugin does

A Grafana app plugin groups related pages, dashboards, data sources and settings under one entry in the Grafana menu. Each component below solves a different part of packaging an internal platform, and the set you need depends on what your users already do in Grafana today.

Custom pages

Pages are React views that the app plugin adds to Grafana, such as a service catalog, an on-call summary or a runbook index. They let you present internal workflows in Grafana without asking users to find the right dashboard. Check which page types and routes your target Grafana version supports, because routing changed in Grafana 11. Confirm the current page options in the plugin documentation before you design navigation.

Bundled dashboards

An app plugin can ship dashboards that are imported when the plugin is enabled, so every instance starts with the same views of your platform. This suits standard panels for an internal system that you want on each instance. Decide early who owns bundled dashboards after installation, since edits made in the UI may diverge from the plugin version. Verify how dashboard import and updates behave for your version.

Bundled data sources

A plugin can include data sources that connect Grafana to an internal API or database, so the app and its queries arrive as one package. This keeps connection settings and query editors together with the pages that use them. Data source plugins have their own build and backend requirements, which affect how much code you maintain. Check the data source plugin documentation before you bundle one.

Configuration pages

Configuration pages let administrators set values such as base URLs, feature toggles and default dashboard selections after installation. They keep environment-specific settings out of the code, which matters when the same plugin runs on several instances. Plan which values are secrets and how they are stored, since the configuration page is the first place operators will look. Confirm the supported settings fields in the current plugin docs.

Optional backend code

An app plugin can include a backend component, typically written in Go, for work the browser cannot do safely, such as calling internal services with credentials or running scheduled checks. Backend code adds build, signing and upgrade work, and it runs on the server, so review its permissions carefully. Ask your engineering team whether a frontend-only app meets the need before you commit to a backend. Verify the current backend capabilities for your version.

Choosing app plugin scope and architecture

A frontend-only app runs entirely in the browser and talks to Grafana and to existing data sources through Grafana's APIs. It fits pages, navigation and dashboard packaging where no secrets, server-side calls or scheduled work are needed. A frontend app with a Go backend adds a compiled binary per operating system and architecture, runs server-side code that can hold credentials, and is required when the plugin must answer queries, run health checks or take part in alerting. The backend roughly doubles the release matrix and adds a build toolchain your team must keep current.

  • Frontend only: lowest build and release effort, no binaries to sign per platform.
  • Frontend with backend: needed for credential handling, server-side API calls and alerting-related logic.
  • Release matrix: a backend build must be produced for each target platform, which multiplies test and packaging time.
  • Upgrade exposure: backend code depends on Grafana's Go SDK, so it must be retested on each Grafana major version.
  • Signing: every backend binary is part of the signed artifact, so changing one forces a re-sign.

Confirm with your security team whether any call must run server-side before choosing the backend option.

How routing changed in Grafana 11 and what it means for app pages

The main routing change for app plugins is that Grafana 11 moved the frontend to React Router 6. An app plugin that registers its pages with React Router 5 patterns, such as Switch, Route components passed as component props, or useHistory, has to be ported before it runs on Grafana 11. Grafana serves app pages under the /a/<plugin-id>/ path, so the routes inside your plugin's root component must be relative to that base. Check the root component, every Route declaration, and the includes entries in plugin.json that point to page paths. Confirm the exact compatibility behavior in the Grafana 11 upgrade notes for the version you deploy, because minor releases have adjusted plugin-facing routing APIs.

The practical effect shows up in links. Absolute paths that assume the old structure, or hardcoded /a/<plugin-id>/ prefixes, can break after the upgrade or behave differently behind a reverse proxy that sets a sub-path. Links from app pages into dashboards should reference the dashboard UID, not the slug, and should be built with the navigation helpers the runtime provides rather than string concatenation. Preserve the time range, template variables, and any query parameters that a user expects to carry across when they move between your app page and a dashboard. Ask the team that owns the Grafana instance which root_url and proxy path it uses, since that setting changes every link your plugin generates.

Test routing as part of the upgrade, not after it. Build a short test matrix that covers direct loads of every page URL, refresh on nested pages, browser back and forward, deep links from outside Grafana, and dashboard round trips. Automated browser tests, including the approach described for plugin end-to-end testing elsewhere in this guide, catch most regressions, but deep links and proxy sub-path behavior still need a manual check in a staging instance that matches production. Repeat the matrix for each Grafana minor release you support, because a route that works on one version can fail on the next when the router or the plugin runtime changes.

How an app plugin moves from scaffold to production

The sequence takes an internal platform idea from named use cases to a signed app plugin running on dev, staging and production Grafana instances. Each step ends with an artifact or check the team can review before moving on.

  1. Define use cases and plugin owners

    Start by listing the internal workflows the app must serve, such as a service catalog page, a runbook dashboard set or a team onboarding flow. For each use case, name the owner who approves changes, the users who depend on it, and the data sources and dashboards it bundles. Confirm whether each use case needs a page, a data source or both, because that decision sets the rest of the build. This step is done when the team has a written scope list with owners and a clear reason for choosing an app plugin over plain dashboards or a sibling plugin type.

  2. Scaffold the plugin project

    Generate the plugin skeleton with Grafana's plugin scaffolding tooling so the project follows the current folder layout, build configuration and plugin metadata. Set the plugin ID, name and organization in the plugin metadata file, and record the Grafana versions the team intends to support. Keep the project in version control from the first commit. The step closes when the project builds locally, the development server loads the plugin in a test Grafana instance, and the metadata matches the scope list.

  3. Build pages, routes and bundled assets

    Implement the app's pages and their routing, along with any bundled dashboards, navigation entries and static assets the plugin ships. Routing changed in Grafana 11, so confirm the routing approach against the Grafana version you target rather than copying older examples. Keep page logic in components that can be tested in isolation. The step is complete when every page in the scope list opens from the app navigation, bundled dashboards import without errors, and the team has reviewed the screens with the use-case owners.

  4. Add data source and backend components

    Where a page needs data that Grafana does not already reach, add a data source plugin or a backend component to the app. Decide early whether queries must work in alerting and shared dashboards, because that requirement pushes the design toward a backend. Document how credentials reach the backend and who rotates them. The step produces a working query path from the app to the internal system, tested with a non-production account, and a short note listing every external call the plugin makes.

  5. Test against target Grafana versions

    Run the automated test suite and end-to-end tests against each Grafana minor version the team supports, using the test tooling that ships with the plugin scaffold. Include the upgrade path in the plan: Grafana releases new minor versions roughly every other month, and each minor version receives support for 9 months, so the test matrix needs a named owner who updates it. Check navigation, bundled dashboards, permissions and any data source queries on each version. The step closes with a test report per version and a list of known gaps that the owner has accepted in writing.

  6. Sign and package the plugin

    Build the release artifact, then sign it for private use following the signing procedure for internal plugins. Each signature is tied to the root URLs of the instances that will load the plugin, so confirm the list of production, staging and development URLs before signing. Record the version, the signed package location and the signing token's owner in the release notes. The step is done when the signed package installs on a clean test instance without enabling unsigned plugins.

  7. Roll out to dev, staging and production

    Install the signed package on the development instance first, then staging, then production, and check each stage against the test report from the previous steps. Provisioning files can define the app's settings so each instance receives the same configuration, which reduces manual drift. Confirm that the users in the scope list can reach their pages on each instance. The rollout is complete when the owner signs off on production, the rollback package is stored with the release, and the team has recorded how a future root URL change will trigger re-signing.

Distributing an app plugin to several Grafana instances

Package the app plugin once per version and deploy that same artifact to every instance, so that what staging tests is byte-for-byte what production loads. The build produces a single plugin directory with its plugin.json and compiled assets, and the team should record the version and checksum in the release notes. Signing is the step that binds this artifact to where it runs. Grafana checks the signature against the root URLs declared in the manifest, which means a plugin signed for one instance URL will not load on another unless it is re-signed for that URL. Plan for this before the first rollout: each environment (development, staging, production, and any disaster recovery site) needs its own root URL entry, and the team should decide whether to sign one build per environment or to sign once with all required URLs. The mechanics of the access policy token and re-signing are covered on the signing page, so this section only sets the design constraint.

Keep environment differences out of the artifact. Instance-specific values such as API base URLs, tenant identifiers, feature toggles and secrets should come from the app's jsonData and secureJsonData settings, which are provisioned per instance through a provisioning file or a configuration management tool, not compiled into the bundle. Store the provisioning files in version control next to the deployment code and keep secret values in your secrets store, not in the repository. Before a release, confirm with the plugin's maintainers which settings are read at startup and which need a Grafana restart, because that determines the rollout order. Reviewers should also check that no hostnames or credentials leaked into the built assets.

Keep every instance on a Grafana version the plugin declares support for. The plugin's plugin.json states the minimum Grafana version it needs, and the team should test the build against each Grafana minor release present in the fleet, not only the newest one. Grafana's upgrade guidance describes major releases once a year, minor releases every other month and patch releases at least monthly, and each minor release is supported for 9 months, so a fleet that lags behind will drift onto unsupported versions. Set a rule that all instances move to a new Grafana minor within a fixed window after it is released, and treat a plugin release as blocked until it passes on the oldest version still in the fleet. Confirm the current release list and support dates in the Grafana upgrade documentation before you set the window.

What drives effort and cost for an app plugin

This table compares the main drivers of effort for an internal Grafana app plugin, with what raises the work and what keeps it down. No duration or fee is given because none has been supplied, so use the drivers to shape a written scope and ask for a quote that prices each one.

DriverWhat raises effortWhat keeps it down
Pages and bundled dashboardsMany pages, nested routes, dashboards that each need their own variables, provisioning and permission checksA small set of pages with clear purposes, dashboards kept as provisioned JSON, and shared components reused across pages
Backend code and internal API authenticationA backend component that calls internal APIs, token exchange, per-user authorization and secrets handlingUsing existing data sources for reads, one service account with narrow scope, and no custom backend unless a page needs it
Grafana versions and instancesSupporting several Grafana minor versions, separate instances per environment, and differing data source configurationsPinning one supported Grafana line per fleet, aligning environments, and standardizing provisioning so each instance is configured the same way
Signing and release processPrivate signing per root URL, re-signing when URLs change, and manual copying of build artifacts to each instanceScripted builds and signing in CI, one root URL convention, and a documented release procedure with a rollback path
Test coverageNo automated tests, many routes and browser flows to check by hand, and changes that affect several instancesUnit tests for logic, end-to-end tests for critical pages, and a staging instance that mirrors production
Maintenance across Grafana releasesFrequent Grafana releases, deprecated APIs, changes to routing, and plugin dependencies that move ahead of your buildTracking the Grafana release notes, keeping the plugin SDK and tooling current, and scheduling regular test runs against new releases

Questions teams ask before building a Grafana app plugin

What does it cost to build a Grafana app plugin?

The cost is the engineering effort to design, build, test, sign and hand over the plugin, and it depends mostly on how many pages, data sources and backend services the app bundles and how many internal APIs it must call. Ask for a quote that lists each page, dashboard and data source as a separate line, the backend work (if any), the test and signing steps, and the handover. Ask whether the quote includes ongoing maintenance, because the recurring cost is usually the larger share over time. No public price applies to this work, and DeployOpen does not publish a price list.

How long does a Grafana app plugin take to build?

Duration follows scope: the number of pages, the number of internal systems the app reads from, whether a Go backend is needed for server-side calls or alerting, and how many Grafana instances the plugin must run on. Each internal API that lacks documentation or a test environment adds discovery time before build work starts. Ask for a timeline in the written scope with milestones for design, build, test on a non-production instance and handover, and ask what the schedule assumes about your reviewers and test access.

What does our internal team need to provide?

Plan to supply four things: the internal APIs or databases the app will call, with documentation and a non-production endpoint; a named owner for each system and for the plugin itself; one or more test Grafana instances that match production versions and database setup; and accounts with the access needed to test the app with realistic roles. Missing any one of these is the most common cause of schedule slips, so confirm each item before the scope is signed.

Can an internal team build a Grafana app plugin alone?

Yes, if the team has frontend skills in TypeScript and React, and Go skills if a backend is needed, and if someone owns the plugin after launch. Grafana provides scaffolding and documentation for plugin development, and an internal team can learn from it. The risk lies in the parts that are easy to underestimate: signing for private use, verifying the plugin across several Grafana releases, and testing permissions. Assess those skills honestly, and consider a fixed-scope engagement for the first version if the team lacks them.

How do Grafana upgrades affect a custom app plugin?

Each Grafana release can change plugin APIs, frontend libraries and routing, so an app plugin needs regression testing on a non-production instance before each upgrade. Grafana releases a major version once a year, minor releases every other month and patch releases at least monthly, and each minor release is supported for 9 months. Plan a recurring test cycle that follows this cadence, and decide who approves the plugin version that goes to production. Ask any partner how upgrade testing and updates are priced and scheduled.

When is a dashboard or data source plugin enough?

A dashboard, or a data source plugin used with existing panels, is enough when users only need to view data in standard panels, when a data source already connects to the system, or when a shared dashboard set and provisioning cover the need. An app plugin is worth building when you need custom pages, navigation, a single bundled experience across dashboards and data sources, or workflows that tie several systems together. Start with the simplest option that meets the requirement, and move up only when the simpler option creates real friction for users.

How do we distribute an app plugin to several Grafana instances?

Distribution means packaging the plugin once, signing it for private use, and installing the same signed build on each instance, with configuration applied through provisioning. Signing ties the plugin to the root URLs of each instance, so every new URL requires a fresh signature. Ask your delivery team how the build pipeline, signing steps and rollout to each instance are handled, and confirm the access policy token and instance list before work begins.

How the app plugin connects to the rest of the Grafana stack

An app plugin sits between users, the data sources it queries, the identity provider that signs them in, and the delivery path that puts it on each instance. Each connection has its own owner and its own failure mode, so the team should map them before writing the first page. The cards below cover the points that most often surprise teams.

Data sources the app queries

A Grafana app plugin does not connect to your systems on its own. Its pages call data sources that are already configured on the instance, such as Prometheus, Loki or a SQL database, and the plugin inherits whatever permissions those data source configurations grant. Decide whether pages should reference data sources by UID in provisioned files, since UIDs differ between instances. Confirm which data sources each instance has and who owns their credentials.

Identity provider for sign-in

Users reach the app's pages through the same sign-in that protects the rest of Grafana, so the identity provider decides who sees them. Open source editions support LDAP, generic OAuth, Entra ID, Okta, GitHub, GitLab, Google OAuth, auth proxy and JWT. SAML and team sync are Enterprise or Grafana Cloud features. Confirm that your provider's group claims reach Grafana in the form your app's role checks expect.

Role checks inside the app

Grafana roles such as Viewer, Editor and Admin determine what a user can do with the app's pages and settings, and app plugins can require specific roles for their own configuration pages. Pages that expose internal data should not rely on navigation hiding alone. Map each internal page to the role it needs and test that a Viewer cannot reach administrative actions through a direct URL. Confirm which roles your identity groups map to on each instance.

Provisioning and as-code delivery

Grafana loads provisioned data sources, dashboards, plugins and alerting resources from YAML or JSON at startup, which is how an app's configuration and bundled dashboards can be placed without manual clicks. Grafana also documents Terraform and Git Sync as as-code options. Decide which of these owns the app's settings so that a manual edit on one instance does not drift from the others. Confirm which tool is the source of truth for each setting.

Instance configuration and plugin paths

The plugin must be placed in the instance's plugin directory and enabled through configuration, and the instance must allow loading of the plugin's signature level. Each instance may use a different install path, container image or Helm values, so the same build can behave differently across environments. Keep the plugin version, the instance configuration and the Grafana version in one record for each environment. Confirm which install method each environment uses.

CI pipeline that builds and signs

A CI pipeline compiles the frontend and any backend binaries, runs tests, packages the plugin and signs it for private use. Signing ties the package to root URLs, so a pipeline that signs for one instance URL will not satisfy another. Signing credentials should be stored in the CI secret store and rotated on a schedule your security team sets. Confirm which signing credential the pipeline uses and which root URLs it covers.

How DeployOpen works on this

DeployOpen plans, builds, tests, documents and supports self-hosted Grafana app plugins inside your own infrastructure. Work starts from a written scope that names the pages, dashboards, data sources and target instances involved, so the team can see what is being packaged before any code is written.

How we scope

  • Inventory the pages, dashboards and data sources the app plugin would package, and decide which belong in the app and which stay separate.
  • Confirm the Grafana versions and editions each target instance runs, so routing and plugin signing choices match the real estate.
  • Agree in writing which instances, environments and distribution paths are in scope, since engagements are fixed-scope.

How we build

  • Scaffold the app plugin, define its pages and routes, and check route paths against the Grafana version your instances run.
  • Wire in the data sources and dashboards the app depends on, and test the app with plugin-e2e against the agreed Grafana versions.
  • Keep configuration, plugin settings and environment-specific values out of the code so each instance can be configured separately.

How we hand over

  • Deliver the plugin build, source repository, test results and a runbook for installing and updating it on each instance.
  • Document the signing and root URL steps for each instance, plus the checks to run after a Grafana upgrade.
  • Walk your platform and engineering team through the app's structure, dependencies and known limits before handover is closed.

How we support

  • Offer ongoing support, upgrades and testing as a retainer, covering changes needed when Grafana releases move the routing or plugin APIs.
  • Review the app against each new Grafana minor or patch release before it reaches production instances.
  • Agree in the retainer which releases, instances and response expectations are covered, so support scope is written down.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.