Why signing private Grafana plugins for internal use needs a deliberate plan
Signing an internal Grafana plugin is how your team tells Grafana that a plugin built for your own organization is trusted, so the instance can load it without switching on unsigned plugins across the board. Two parties are involved: the internal team that builds and owns the plugin, which prepares the package and holds the signing credential, and the team that operates the Grafana instances, which decides which plugins load and where. Treat signing as a control, not a formality, because the signature ties the plugin to the instances it was approved for.
This module covers the decisions that come before and after the signing step: which access policy token the signing process uses, which rootUrls the signature must cover for each instance, how the package is kept consistent across development, staging and production, and what happens when a Grafana URL changes and the plugin must be signed again. Teams usually take it up once a custom panel, data source or app plugin is ready for more than one developer's workstation. Signing sits inside the wider plugin development and distribution work, so the build, testing and rollout choices covered elsewhere on this page should be settled first.
Before you commit to a plan, confirm the exact signing commands, token scopes, manifest fields and any Grafana Cloud steps against the current Grafana plugin documentation for your Grafana version, because these details change between releases. Ask who will own the access policy token, where it will be stored, how a lost or rotated token is replaced, and who approves a change to an instance URL. Those answers determine how much re-signing your team will do over the life of the plugin.
What plugin signing does for a self-hosted Grafana
Grafana only loads plugins it can verify when unsigned plugins are blocked. Signing binds a plugin build to a publisher identity and to the Grafana instances it is allowed to run on. The four parts below are the ones that decide whether a private plugin loads, and each one is a place where a mismatch shows up as a plugin that silently refuses to start.
The plugin signature
A signature is generated over the plugin's files, so it proves the bundle has not changed since it was signed. Any rebuild, including a change to a single asset or a regenerated manifest, produces a new package that must be signed again. Treat each release artifact as its own signed object, and record which build each deployed instance is running so you can tell whether a failure comes from a stale signature or a modified file. Confirm the exact signing command and manifest requirements in the plugin docs for your Grafana version.
The access policy token
Signing uses a token tied to an access policy, and that token is the credential that authorizes publishing signatures under your organization's identity. Create a dedicated token for signing rather than reusing an administrator credential, store it in your secrets manager, and restrict who can read it. Rotate it on a schedule and when staff change. Confirm the required token scope and where the signing flow expects it in the current plugin documentation.
Root URLs bind the signature
The rootUrls list records which Grafana base URLs a signed plugin may run on. Each instance must be reachable at one of those exact URLs, including scheme, host and port, or Grafana rejects the signature. Plan the list before signing, especially for staging, production, reverse-proxy hostnames and any disaster recovery site. Any new hostname needs a new signature that includes it.
Re-signing when URLs change
Changing a domain, moving an instance behind a new proxy or adding a recovery site means the existing signature no longer matches the instance. Re-signing is a release step, not a fix applied on one server, so it belongs in the same pipeline that builds the plugin. Keep a record of which signed build covers which URL set, so a hostname change is tied to a known, repeatable action rather than an emergency edit.
Loading under the unsigned-plugin setting
Grafana's setting for allowing unsigned plugins controls whether unsigned code can load at all. Keep unsigned plugins blocked in production and confirm that the signed plugin loads without relaxing that setting. Check the plugin list and startup logs after every restart, because a failed signature check often shows up there rather than as a visible error on a dashboard. Verify the exact configuration key and its behavior in the Grafana configuration docs for your version.
Which signing option fits each internal plugin
This table compares how far a signature reaches and who holds the signing token, with the re-signing effort each choice creates. Read each row as a trade-off between fewer signatures to manage and tighter control over which URLs can load the plugin. The main takeaway is that every URL change forces a new signature, so list the instance URLs you expect to keep before you choose a pattern.
| Option | When it fits | Re-signing effort | Point to confirm |
|---|---|---|---|
| One instance, one rootUrl | A single Grafana server with one stable URL and one internal plugin. | Low. Re-sign only if that URL changes or the plugin is rebuilt. | Confirm the URL Grafana uses for the instance matches the rootUrl exactly, including scheme and path. |
| One instance, several rootUrls | One server reachable under more than one address, such as an internal name and a load balancer address. | Moderate. Each added or changed address means a new signature covering the full list. | Confirm the signing flow accepts multiple rootUrls for your plugin type and Grafana version. |
| Several instances, one signature | Staging and production instances that share the same plugin build and the same URL pattern. | Low to moderate. One signature covers all listed URLs, but any new environment needs re-signing. | Confirm each instance URL is listed in the manifest before rollout, not after the plugin fails to load. |
| Several instances, one signature per instance | Environments with different URLs, owners or change windows that must be separated. | Higher. Each instance has its own build, signature and release step. | Confirm how your release pipeline stores and tags one signed package per environment. |
| Shared token owned by a platform team | One central team publishes internal plugins for all Grafana users in the organization. | Low for teams, moderate for the platform team, which signs every release and URL change. | Confirm who may create and rotate the access policy token and how the token is stored and revoked. |
| Per-team token | Product or data teams own their plugins and want to ship without waiting on a central queue. | Higher overall. Each team creates, stores, rotates and audits its own token. | Confirm the access policy can be scoped per team and whether token usage is logged for review. |
| Signing for Grafana Cloud | The internal plugin must load in a Grafana Cloud stack, where the access policy token is the signing credential. | Moderate. Re-sign when the stack URL changes or the token is rotated. | Confirm the stack URL and token scope in your Grafana Cloud account before building the manifest. |
| Self-hosted instances without a stable URL | Instances reached through changing hostnames, DNS aliases or ephemeral test environments. | High. Each new hostname can trigger a signature and a plugin reload. | Confirm a stable internal DNS name for each instance, and avoid signing ad hoc test hostnames. |
How to sign an internal plugin in six steps
This sequence takes an internal plugin from an unsigned build to a signed package that a production Grafana instance loads while unsigned plugins stay disabled. Each step produces a record your team can check before the next one starts.
Confirm the plugin ID and version
Start by reading the plugin.json file in the build and recording the plugin ID and version. The ID must match the plugin's identifier exactly, because the signature binds to it, and the version identifies the artifact you will sign. Write both values in the change record and confirm they match the package name and the build output your CI produces. The step is done when the ID and version appear in a single record that the release owner has approved, and no later build reuses that version number with different contents.
Create the access policy token with the right scope
Using a Grafana Cloud account, create an access policy and a token for plugin signing with only the scope the signing step needs. Store the token in your secrets manager, not in the repository or CI logs, and record who created it and when. Confirm the current scope names and token setup steps against the Grafana plugin signing documentation for your version, since these details change between releases. The output is a token stored in your secrets manager and a log entry naming its owner and scope.
List every instance URL as rootUrls
Collect every URL where the plugin will run, including production, staging, and any disaster recovery or regional instances, and enter each one as a rootUrls entry. Grafana checks the signature against these URLs, so a missing address means that instance will reject the plugin. Include the scheme and any path prefix exactly as users reach the instance, and review the list with the platform owner. The output is a reviewed, matched rootUrls list that is saved with the plugin release record.
Sign the build
Build the plugin package, then run the signing step with the access policy token and the rootUrls list from the previous step. Use the signing command documented for your plugin toolchain and do not sign a package built from uncommitted or unreviewed code. Keep the signed artifact and its manifest together, and record the checksum of the signed package. The output is a signed artifact plus a manifest and checksum that match the reviewed plugin ID, version, and rootUrls.
Verify the signed package on staging
Install the signed package on a staging instance whose URL is in the rootUrls list, and keep unsigned plugins disabled in that instance's configuration. Confirm that Grafana loads the plugin, that the plugin page and data source or panel work against test data, and that the plugin shows as signed in the plugin catalog. Then test a copy of the package with a URL missing from rootUrls, or confirm the expected rejection in a non-production environment, so you know the check is working. The output is a verification record listing the instance version, the signature status, and the results of each check.
Promote to production and record the release
Deploy the same signed artifact that passed staging to production, without rebuilding it, and confirm the production URL is on the rootUrls list. Check the plugin loads with unsigned plugins still disabled, then store the artifact, checksum, rootUrls list, and verification record together for audit and future upgrades. If a URL changes later, such as a new domain, repeat the steps from the rootUrls list onward with a new signature. The output is a production deployment record that ties the running plugin to its signed artifact and the verification evidence.
How rootUrls tie a signature to each instance
The rootUrls list is the approval boundary for a signed private plugin. When you sign a plugin, the list names the Grafana URLs that the signature covers, so the signature says this build was approved for those addresses and no others. The purpose is to stop a signed plugin from being copied to an instance that was never reviewed for it. For internal plugins, the list should include every production hostname, staging hostname and sub-path deployment that will load the plugin. Confirm in the Grafana plugin signing documentation exactly how the match is evaluated and whether it is enforced at load time, rather than assuming the behavior from this summary.
Changes to the address of an instance can break that match. A different host name, a new sub-path (for example, a prefix added by a reverse proxy), or a changed port each produce a URL that is absent from the signed list. Treat each of these as a change in identity, not a cosmetic edit. Illustrative example: a team signs for then moves the instance under a /grafana` path behind a proxy. The plugin may stop loading until the signed list covers that form. Test the exact address users type, including the scheme, port and any trailing slash, through the real proxy chain before you rely on the signature.
Every new instance, domain or environment needs its own entry, so list all intended URLs before the first signing. Adding a URL means updating rootUrls and signing the plugin again, because the existing signature does not cover the new address. Build this into your release pipeline: pull the URL list from environment configuration, record which signed package went to which instance, and verify the plugin loads after each deployment. Ask your team to confirm whether re-signing is required for every added URL in your Grafana version, and whether earlier signatures remain valid for the URLs they already cover.
When a signed plugin must be re-signed
Each Grafana instance that loads a private plugin has to appear in the plugin's signed rootUrls. A new hostname, such as a standby node or a second environment that gets its own address, is a new root URL, so the existing signature does not cover it. The plugin owner adds the new URL to the manifest, generates a new signature with the same access policy token and distributes the updated bundle to that instance.
- Add the URL before deployment: list the exact scheme, host and port that users type into the browser, not the internal address behind a load balancer.
- Keep one manifest per signed build: avoid editing a signed manifest by hand, because the signature no longer matches the content.
- Owner: the plugin owner signs, and the platform team confirms the hostname from its DNS and load balancer records.
- Verify: open the plugin on the new hostname, confirm it appears in the plugin catalog without an unsigned warning, and check the Grafana server log for signature errors.
Confirm the exact signing command and manifest fields against the current plugin signing documentation before you change a production build.
A domain change affects every instance that moves, so the plugin must be signed again for the new address and the old signature should be retired. Plan the change alongside the wider Grafana deployment work, including the reverse proxy, TLS certificates and any single sign-on redirect URLs, because those usually change at the same time.
- List every affected instance: include development, test and production instances and any disaster recovery copy.
- Sequence the cutover: publish the re-signed plugin before DNS or the proxy switches, so the plugin loads on the first request to the new name.
- Owner: the platform team owns DNS and the proxy, and the plugin owner owns the signature.
- Verify: test the plugin on the new domain and the old one during the transition period, and record which URLs the signed build covers.
Ask your Grafana deployment partner whether the old domain must keep working during cutover, because that decides whether the old URL stays in rootUrls.
Grafana often sits behind a reverse proxy that serves it under a sub-path, such as a shared portal address. When the path changes, the public URL Grafana sees is different even though the host is the same. The root URL in the signature includes the path, so a path change can break signature validation even when the hostname never changed.
- Compare the public URL, not the host: confirm the full address users open, including any sub-path.
- Check the Grafana root URL setting: the proxy path and the Grafana
root_urlsetting must agree with the URL in the manifest. - Owner: the platform team changes the proxy and Grafana settings, and the plugin owner re-signs.
- Verify: load the plugin through the proxy path, check the browser console for failed asset requests and confirm the plugin does not show as unsigned.
Review your proxy configuration with the people who maintain it, because a path rewrite can change asset URLs as well as the root URL.
A new plugin version is a new build, so it needs a new signature even when the root URLs stay the same. Signing covers the plugin files, which means any change to the bundle, including a small frontend fix, invalidates the previous signature. Treat each release as a signed artifact that goes through the same test path as the one before.
- Re-sign every release: do not copy the signature file from an earlier version into a new build.
- Keep the version in the manifest current: the signed version number should match the folder and archive name you deploy.
- Owner: the plugin developer builds and signs, and the release process records the signed artifact.
- Verify: install the new build on a test instance, confirm the version shown in the plugin catalog and check the plugin's own tests before promoting it.
Keep old signed builds available so that a rollback does not need a new signing step during an incident.
A Grafana major upgrade does not always change the signature, but it can change the plugin APIs and the platform behavior that your plugin depends on. Grafana publishes major releases once a year, with minor releases in between, and each minor release is supported for a fixed period. Schedule the signing review with the upgrade plan, so the plugin is tested against the target version before the production instance moves.
- Read the upgrade notes for plugin changes: check whether the target version changes frontend or backend plugin requirements.
- Test on a staging copy: run the upgrade against a staging instance that uses the same root URL and proxy path as production.
- Owner: the platform team schedules the upgrade, and the plugin developer confirms compatibility and re-signs if the build changes.
- Verify: load each private plugin after the upgrade, check the Grafana log for signature and compatibility warnings, and confirm dashboards that use the plugin still render.
Confirm the support window for your current Grafana version with the vendor documentation, so the re-signing work fits inside the upgrade schedule.
What drives the effort of signing and re-signing
Signing effort rises with the number of Grafana instances, how often their addresses change, and how much of the process depends on people rather than pipelines. The cards below cover the decisions that set that effort and the habits that keep it small, so the work stays a routine step in each release rather than a recurring emergency.
Number of instances and environments
Each Grafana instance that loads the plugin needs its own address in the signed manifest, so every development, test, staging and production instance multiplies the work. A plugin that runs on five instances is signed against five root URLs, and each one must be checked when the plugin is built. Ask which instances will load the plugin before the first signing, and keep that list in the same repository as the plugin so it is reviewed with every release. Stable hostnames for each environment reduce the number of signing events you will face over the plugin's life.
How often URLs change
Re-signing is triggered by any change to a root URL, including a new load balancer hostname, a domain move or a switch to a new TLS endpoint. Each change means a new signature for the affected plugin and a redeploy to every instance that uses it. Treat hostnames as infrastructure contracts, agree who may change them, and record the dependency between DNS changes and plugin signing. Teams that rename hostnames during migrations often discover the signing dependency late, so put it on the migration checklist.
Whether signing runs in CI
A manual signing step depends on one person who knows the token, the command and the target URLs. Running signing in the build pipeline turns each release into a repeatable job that produces the same artifact from the same inputs. The pipeline should read the root URLs from version-controlled configuration, fail when they are missing, and store the signed package as a build output. Decide early whether the access policy token lives in the CI secret store, and who can change that secret.
Plugins sharing one token policy
A single access policy token can sign several plugins, so the number of plugins sharing a policy affects both the number of signing events and the blast radius of a leaked token. Grouping plugins under one policy reduces administration but widens what one credential can do. Separate policies per team or per plugin family limit exposure but add tokens to rotate. Review this trade-off with your security team and document which plugins share each policy.
Access governance over the token
The access policy token is the credential that proves a plugin was signed by your organization, so its custody determines whether signatures can be trusted. Decide who can create, read, rotate and revoke it, and whether those actions are logged in your identity platform or secret manager. Rotation affects every pipeline that holds the token, so plan the cut-over before revoking an old one. Ask the vendor or your Grafana Cloud administrator to confirm how token scopes and expiry are managed for your account type.
Verification evidence required
Auditors and internal reviewers often ask how a signed plugin was approved, built and deployed. Each signing run should leave a record: the plugin version, the root URLs signed, the pipeline run, the approver and the instance where the plugin was loaded. Keeping this record together reduces the time spent reconstructing history during a review. Confirm the retention period your organization requires for build and change records, and make sure the signing log is kept with them.
Questions teams ask before signing private Grafana plugins
Is signing a private Grafana plugin a one-time task or ongoing work?
Setup is one-time, but signing recurs with every plugin release. The initial work covers the access policy token, the signing step in your build pipeline, and the root URL list for each instance. After that, each new plugin version needs a fresh signature, and any change to an instance's public address requires re-signing. The cost is mostly engineering time, paid either to your own staff or to a partner under a written scope. Ask any provider to separate the one-time setup from the per-release and per-URL-change work, so you can see what recurs and who carries it.
How long does plugin signing take, and what drives the duration?
The duration depends on how many plugins you sign, how many Grafana instances receive them, and how your build pipeline is already set up. A single plugin that signs for one instance is far less work than several plugins across development, test and production instances, each with its own URL. Other drivers include whether your team already has a CI pipeline for plugin builds, how quickly your security team approves a token with the right scope, and how often your plugins ship. Confirm your own inventory before asking for a schedule, because the schedule follows from the list of plugins and instances.
What does the team need to provide before signing starts?
Signing depends on access that only your organization can grant. Prepare these items before work begins:
Can one internal engineer sign private plugins without outside help?
Yes, if that engineer has administrative access to the Grafana Cloud account that issues the token, can change the plugin build pipeline, and understands how each instance is reached by users. The main risk is dependence on one person. Tokens need rotation, root URLs change with DNS and proxy work, and a new release can fail signing on a Friday afternoon. Teams often pair the owner with a second engineer and write down the re-signing procedure. DeployOpen can plan and build that procedure under a fixed scope, or cover it under a retainer for ongoing releases and URL changes. Either way, the written handover should name who holds the token.
What happens to signed plugins during Grafana upgrades?
A Grafana upgrade does not automatically invalidate a plugin signature, but it can change what the plugin needs to work. Grafana releases minor versions roughly every other month and patch releases at least monthly. Each minor release is supported for 9 months, and the last minor release of a major version is supported for 15 months, according to the Grafana upgrade guidance. Plan a check of each signed plugin against the target version before the upgrade, and re-test after it. Read the Grafana upgrade guidance to set your upgrade window.
Why does a signed plugin stop loading after an instance URL changes?
A signature records the root URLs the plugin is allowed to run on, so a plugin signed for one address will not load on another. Changing a DNS name, moving behind a new reverse proxy, or adding a load balancer hostname for a high availability setup can all change the address users reach Grafana at. When that happens, you must add the new root URL and sign the plugin again. Confirm the exact address your users and Grafana itself see before signing, and build the re-signing step into any change that touches the public hostname, so the plugin does not disappear silently after a network change.
Does signing require a paid Grafana Cloud plan?
Signing needs a Grafana Cloud account to issue the access policy token, so the plan you choose matters for budgeting. According to the Grafana pricing page, Grafana Cloud has a free tier, a Pro tier that starts with a 19 US dollar monthly platform fee plus usage, and an Enterprise tier with a minimum commitment of 25,000 US dollars per year. Confirm with Grafana Labs whether the free tier can create a token with the scope signing requires, because that determines whether a paid plan is needed. Your self-hosted Grafana instances do not need a Grafana Cloud subscription to run the signed plugins.
What should a quote for plugin signing include?
A useful quote lists the work against a written inventory rather than a lump sum. It should cover the plugins and versions in scope, every Grafana instance and its root URLs, the changes needed in your build pipeline, how the access policy token is created, stored and rotated, and the re-signing procedure for URL changes. It should also state the handover documents, the upgrade checks included, and whether support is fixed scope or a retainer. Ask what is excluded, such as plugin code changes or DNS work, and who approves each step. DeployOpen agrees scope, deliverables and timelines in writing before work starts, and quotes follow from that scope.
How plugin signing connects to the rest of the stack
A signed plugin passes through several systems before Grafana loads it: the build pipeline that produces the artifact, the deployment method that places it on each node, the token that authorizes signing, and the plugin's own configuration. A change in any one of them can invalidate the signature or break loading, so each connection needs an owner and a check.
Plugin build and CI pipeline
The signature belongs to a specific build output, so the signing step should run in CI immediately after the production build, on the same artifact that gets deployed. If a developer rebuilds locally, edits the bundle or changes the plugin ID, the signature no longer matches and the plugin must be signed again. Ask your team to confirm that the pipeline signs the packaged output and publishes that exact archive to every environment.
Provisioning and deployment method
On virtual machines, containers or Kubernetes, the signed plugin directory must reach every Grafana node before the service restarts. Grafana provisioning loads data sources, dashboards and plugins from YAML or JSON files at startup, so keep the plugin entry and its settings in the same versioned repository as the deployment. Confirm how your image or Helm release copies the plugin files into the plugins path.
Grafana Cloud access policy token
When the target instance is Grafana Cloud, the signing step authenticates with a Grafana Cloud access policy token rather than an instance admin login. The token should be scoped to plugin signing only and issued to a service identity your team controls, not a personal account. Ask who can create, rotate and revoke it, and what happens to signed plugins if the token owner leaves.
Secret storage for signing tokens
The access policy token should live in your CI platform's secret store or a dedicated vault, and never in the plugin repository, a provisioning file or a container image layer. Limit which jobs and branches can read it, so a pull request from a fork cannot sign an unreviewed build. Set a rotation schedule and confirm that the rotation procedure includes updating the pipeline secret and re-signing the current release.
Plugin data source or app configuration
The signed manifest lists the root URLs where the plugin may run, and the plugin's own settings point to endpoints that differ by environment. Keep the internal API URL, the app's base path and any stored settings in per-environment configuration, so a build signed for staging is not promoted to production with staging endpoints. For an app plugin, confirm that the routes and bundled pages resolve under the instance's configured root URL.
What you should receive and keep after a signing engagement
The handover should give your team the signed plugin artifacts together with their rootUrl lists, so each signed build can be traced to the exact set of Grafana instances it was approved for. Ask for a written record that names the access policy token used to sign each build, the scope it carried, the plugin version and the date of signing. Without that record, a later failure to load a plugin is hard to diagnose, because nobody can say which signature covers which URL. Ask also for the unsigned source reference or build commit, so the signed output can be reproduced and checked against the code that was reviewed.
The handover should include a re-signing runbook written for your operators, not only for the engagement team. It should list the steps to sign again when a rootUrl changes, such as a new hostname, a move to a different domain or an added instance, and it should say who approves the change and where the new artifact is deployed. Ask for a verification procedure to run on each instance after deployment: confirm that Grafana starts with the plugin loaded, that the plugin's signature status is shown as expected and that a test request from an unlisted URL is refused. Confirm these steps against your own Grafana version, since loading behavior and the location of the plugin directory can differ between releases.
Your team must own the access policy token from the start. It should sit in a secrets store you control, with a named owner, a defined rotation schedule and a rule that revoking it does not break running instances. After handover, the internal team maintains the signed artifacts, the rootUrl list, the signing record and the runbook, and it re-verifies each instance after every Grafana upgrade and every change to the plugin. Plan for that recurring work from the first day, because an expired or lost token, or an undocumented URL change, is the most common reason a signed internal plugin stops loading.
How DeployOpen scopes and delivers plugin signing
DeployOpen treats plugin signing as part of a wider Grafana plugin delivery, not a one-off command. We agree the scope in writing first, then build, document and hand over the signing workflow so your team can re-sign plugins when URLs or Grafana instances change.
How we scope
- Inventory every internal plugin, its type, and the Grafana instances that will load it
- Record the exact root URLs each instance serves, including staging and any restricted networks
- Agree in writing who holds the access policy token and how it is stored and rotated
How we build
- Set up the signing pipeline inside your infrastructure so tokens never leave your environment
- Configure the rootUrls for each instance and verify that unsigned plugins stay blocked
- Test loading on every target instance before any plugin is released to users
How we hand over
- Document the signing steps, token ownership and the re-signing procedure when a URL changes
- Walk your platform and security engineers through the workflow and the recovery steps
- Deliver written runbooks and configuration records that your team can keep under version control
How we support
- Engagements are fixed-scope, with deliverables and timelines agreed in writing before work starts
- Ongoing support, upgrades and re-signing across Grafana releases are available as a retainer
- Ask us to confirm plugin compatibility with the Grafana version you plan to run next

