Grafana
Platform:
| Channel | Revision | Published | Runs on |
|---|---|---|---|
| 2/stable | 180 | 16 Mar 2026 | |
| 2/candidate | 180 | 13 Mar 2026 | |
| 2/beta | 180 | 13 Mar 2026 | |
| 2/edge | 180 | 11 Mar 2026 | |
| dev/edge | 190 | 30 Jun 2026 | |
| 12.4/edge | 191 | Today | |
| 1/stable | 160 | 10 Oct 2025 | |
| 1/candidate | 160 | 10 Oct 2025 | |
| 1/beta | 160 | 10 Oct 2025 | |
| 1/edge | 160 | 10 Oct 2025 |
juju deploy grafana-k8s --channel 2/stable --trust
charms.grafana_k8s.v1.grafana_source
-
- Last updated 29 Jun 2026
- Revision Library version 1.0
Overview.
This document explains how to integrate with the Grafana charm
for the purpose of providing a datasource which can be used by
Grafana dashboards. It also explains the structure of the data
expected by the grafana-source interface, and may provide a
mechanism or reference point for providing a compatible interface
or library by providing a definitive reference guide to the
structure of relation data which is shared between the Grafana
charm and any charm providing datasource information.
What changed in LIBAPI v1 (HA datasources)
v1 adds support for a single, load-balanced, application-level datasource, in
addition to the historical per-unit datasources. This fixes the long-standing problem
where, in an HA deployment behind an ingress, the datasource UID changed on every leader
re-election (because the UID embedded the unit number of whichever unit happened to be
advertising), silently breaking every dashboard and alert that referenced it.
A provider now chooses its datasource topology explicitly via two flags:
app_datasource(defaultTrue): the leader publishes one load-balanced address in the application databag (grafana_source_app_host). Grafana mints exactly one datasource for the whole application, with a UID of the formjuju_{model}_{model_uuid}_{application}— note there is no unit number, so the UID is stable across leader re-elections. This is the correct shape for a clustered, HA backend behind a single load-balanced address (Mimir/Loki/Tempo coordinators, Alertmanager).unit_datasources(defaultFalse): each unit publishes its own address in its unit databag (grafana_source_host). Grafana mints one datasource per unit, each with a UID of the formjuju_{model}_{model_uuid}_{application}_{unit_number}. This is the correct shape for a non-clustered backend where each unit is an independent datasource (e.g. a standalone Prometheus).
Both may be enabled at once (yielding one app-level datasource plus one per unit). If both are disabled, no address is published and a warning is logged.
Ingress pairing (provider-charm responsibility — this library never talks to the ingress library, it only receives the URL you give it):
app_datasource=True<-> use ingress-per-app; pass the app URL asapp_datasource_url.unit_datasources=True<-> use ingress-per-unit; pass each unit's own URL asunit_datasource_url(the value is per-unit, since the provider runs in every unit).
Breaking changes from v0
- The
source_urlandis_ingress_per_appconstructor arguments were removed. Useapp_datasource/unit_datasourcesandapp_datasource_url/unit_datasource_urlinstead. - The
update_source()method was replaced byupdate_app_source()andupdate_unit_source().
Upgrade implications
This is a coordinated rollout — Grafana and provider charms should be upgraded together.
- Both upgraded: the datasource UID changes once (from
juju_..._{n}tojuju_..._{app}), which breaks dashboards/alerts referencing the old UID a single time; users must re-point them once. This is a one-off, deliberate break rather than the recurring one it replaces. - Old Grafana + new provider (app mode): the old v0 consumer ignores
grafana_source_app_hostand sees an emptygrafana_source_host, resulting in no datasource. Upgrade Grafana to restore the datasource. - New Grafana + old provider: the old fragile per-unit behaviour continues (no change until the provider is also upgraded).
Provider Library Usage
The Grafana charm interacts with its datasources using its charm
library. The goal of this library is to be as simple to use as
possible, and instantiation of the class with or without changing
the default arguments provides a complete use case. For the simplest
use case of a Prometheus (or Prometheus-compatible) datasource
provider in a charm which provides: grafana-source, creation of a
GrafanaSourceProvider object with the default arguments is sufficient.
The constructor arguments are:
`charm`: `self` from the charm instantiating this library
`source_type`: **required** — the Grafana datasource type (e.g. "prometheus",
"loki", "tempo", "mimir", "alertmanager")
`source_port`: the port the datasource listens on (default: "")
`relation_name`: the relation name (default: "grafana-source")
`refresh_event`: a `PebbleReady` event from `charm`, used to refresh
the address sent to Grafana on a charm lifecycle event or pod restart
(default: auto-detected from the charm's single container, if any)
`extra_fields`: additional fields for Grafana's `jsonData` (default: None)
`secure_extra_fields`: additional fields for Grafana's `secureJsonData` (default: None)
`app_datasource`: publish a single app-level datasource (default: True)
`unit_datasources`: publish per-unit datasources (default: False)
`app_datasource_url`: explicit URL for the app-level datasource (default: None)
`unit_datasource_url`: explicit URL for this unit's datasource (default: None)
The values of app_datasource_url / unit_datasource_url should be a fully-resolvable
URL for a valid Grafana source, e.g., http://example.com/api or similar.
If your configuration requires any changes from these defaults, they may be set from the class constructor. It may be instantiated as follows:
from charms.grafana_k8s.v1.grafana_source import GrafanaSourceProvider
class FooCharm:
def __init__(self, *args):
super().__init__(*args, **kwargs)
...
self.grafana_source_provider = GrafanaSourceProvider(
self, source_type="prometheus", source_port="9090"
)
...
The first argument (self) should be a reference to the parent (datasource)
charm, as this charm's model will be used for relation data, IP addresses,
and lifecycle events.
An instantiated GrafanaSourceProvider will ensure that, once a relation is
established, this application is added as a datasource in the Grafana configuration
using the Grafana datasource provisioning
specification via YAML files. Depending on the app_datasource/unit_datasources
flags, this is either a single application-level datasource, one datasource per unit,
or both.
This information is added to the relation data for the charms with the following structure:
Application databag (written by leader):
{
"grafana_source_data": { # JSON-encoded
"model": charm.model.name,
"model_uuid": charm.model.uuid,
"application": charm.model.app.name,
"type": source_type,
"extra_fields": {...},
"secure_extra_fields": {...},
},
# app-level address (when app_datasource=True):
"grafana_source_app_host": "{scheme}://{app}.{model}.svc.cluster.local:{port}{path}",
}
Unit databag (written by each unit when unit_datasources=True):
{
"grafana_source_host": "{scheme}://{fqdn}:{port}{path}",
}
This is ingested by :class:GrafanaSourceConsumer, and is sufficient for configuration.
Consumer Library Usage
The GrafanaSourceConsumer object may be used by Grafana
charms to manage relations with available datasources. For this
purpose, a charm consuming Grafana datasource information should do
the following things:
- Instantiate the
GrafanaSourceConsumerobject by providing it a reference to the parent (Grafana) charm and, optionally, the name of the relation that the Grafana charm uses to interact with datasources. This relation must confirm to thegrafana-sourceinterface.
For example a Grafana charm may instantiate the
GrafanaSourceConsumer in its constructor as follows
from charms.grafana_k8s.v1.grafana_source import GrafanaSourceConsumer
def __init__(self, *args):
super().__init__(*args)
...
self.grafana_source_consumer = GrafanaSourceConsumer(
self,
grafana_uid=self.unique_name,
grafana_base_url=self.external_url,
)
...
A Grafana charm also needs to listen to the
GrafanaSourceEventsevents emitted by theGrafanaSourceConsumerby adding itself as an observer for these events:self.framework.observe( self.grafana_source_consumer.on.sources_changed, self._on_sources_changed, ) self.framework.observe( self.grafana_source_consumer.on.sources_to_delete_changed, self._on_sources_to_delete_change, )
The reason for two separate events is that Grafana keeps track of removed datasources in its datasource provisioning.
If your charm is merely implementing a grafana-source-compatible API,
and is does not follow exactly the same semantics as Grafana, observing these
events may not be needed.
Index
class GrafanaSourceData
Description
This class represents the data Grafana provides others about itself. None
Methods
GrafanaSourceData. get_unit_uid( self , unit: str )
Description
Return the UID for a given unit. None
GrafanaSourceData. get_app_uid( self )
Return the UID for the application-level (load-balanced) datasource.
Description
Returns None if the remote Grafana did not assign an app-level datasource UID (e.g. the provider only published per-unit datasources, or the remote Grafana predates app-level datasource support).
class RelationNotFoundError
Description
Raised if there is no relation with the given name. None
Methods
RelationNotFoundError. __init__( self , relation_name: str )
class RelationInterfaceMismatchError
Description
Raised if the relation with the given name has a different interface. None
Methods
RelationInterfaceMismatchError. __init__( self , relation_name: str , expected_relation_interface: str , actual_relation_interface: str )
class RelationRoleMismatchError
Description
Raised if the relation with the given name has a different direction. None
Methods
RelationRoleMismatchError. __init__( self , relation_name: str , expected_relation_role: RelationRole , actual_relation_role: RelationRole )
class SourceFieldsMissingError
Description
An exception to indicate there a missing fields from a Grafana datsource definition. None
class GrafanaSourcesChanged
Description
Event emitted when Grafana sources change. None
Methods
GrafanaSourcesChanged. __init__( self , handle , data )
GrafanaSourcesChanged. snapshot( self )
Description
Save grafana source information. None
GrafanaSourcesChanged. restore( self , snapshot )
Description
Restore grafana source information. None
class GrafanaSourceEvents
Description
Events raised by :class:`GrafanaSourceEvents. None
class GrafanaSourceProvider
Description
A provider object for Grafana datasources. None
Methods
GrafanaSourceProvider. __init__( self , charm: CharmBase , source_type: str , source_port , refresh_event , relation_name: str , extra_fields , secure_extra_fields , app_datasource: bool , unit_datasources: bool , app_datasource_url , unit_datasource_url )
Construct a Grafana charm client.
Arguments
a :class:CharmBase object which manages this
:class:GrafanaSourceProvider object. Generally this is
self in the instantiating class.
an optional (default prometheus) source type
required for Grafana configuration. The value must match
the DataSource type from the Grafana perspective.
an optional (default 9090) source port
required for Grafana configuration. Used to build the datasource
address when an explicit URL is not provided.
string name of the relation that is provides the Grafana source service. It is strongly advised not to change the default, so that people deploying your charm will have a consistent experience with all other charms that provide Grafana datasources.
a :class:CharmEvents event (or a list of them) on which the IP
address should be refreshed in case of pod or
machine/VM restart.
a :dict: which is used for additional information required
for some datasources in the jsonData field
a :dict: which is used for additional information required
for some datasources in the secureJsonData
whether to publish a single, load-balanced datasource for the whole application in the application databag. Defaults to True. The resulting datasource UID is stable across leader re-elections.
whether to publish one datasource per unit, each keyed by unit number. Defaults to False.
an optional fully-resolvable URL for the application-level
datasource (e.g. an ingress-per-app URL). If unset, a Kubernetes
load-balanced service address is constructed from the application name.
Pass the base URL only: for source_type == "mimir" the library
appends the /prometheus query path itself.
an optional fully-resolvable URL for this unit's
datasource (e.g. an ingress-per-unit URL). If unset, the unit's own FQDN
is used. This value is per-unit. Pass the base URL only: for
source_type == "mimir" the library appends the /prometheus query
path itself.
Description
The :class:GrafanaSourceProvider object provides an interface
to Grafana. This interface supports providing additional
sources for Grafana to monitor. For example, if a charm
exposes some metrics which are consumable by an ingestor
(such as Prometheus), then an additional source can be added
by instantiating a :class:GrafanaSourceProvider object and
adding its datasources as follows:
self.grafana = GrafanaSourceProvider(self)
self.grafana.add_source(
address=<address>,
port=<port>
)
The provider can publish two distinct kinds of datasource, controlled by the
app_datasource and unit_datasources flags:
An application-level datasource (
app_datasource=True, the default): the leader publishes a single, load-balanced address in the application databag. The consumer mints exactly one datasource for the whole application with a UID that does NOT include a unit number, so it is stable across leader re-elections. This is the correct representation for an HA, clustered backend (e.g. a Mimir/Loki/Tempo coordinator, or Alertmanager) sitting behind a single load-balanced address. When behind an ingress, pair this with ingress-per-app and pass the app ingress URL asapp_datasource_url.Per-unit datasources (
unit_datasources=True): each unit publishes its own address in its unit databag, and the consumer mints one datasource per unit, each keyed by unit number. This is the correct representation for a non-clustered backend where each unit is an independent datasource (e.g. a standalone Prometheus). When behind an ingress, pair this with ingress-per-unit and pass each unit's own ingress URL asunit_datasource_url(the value is per-unit, since this object runs as a separate instance in every unit).
Both flags may be set at once (yielding one app-level datasource plus one per-unit datasource for each unit). If both are False, no datasource address is published at all and a warning is logged.
GrafanaSourceProvider. update_app_source( self , app_datasource_url )
Update the application-level datasource URL and re-publish relation data.
Description
Useful when the load-balanced address (e.g. an ingress-per-app URL) becomes available or changes after this object is constructed.
GrafanaSourceProvider. update_unit_source( self , unit_datasource_url )
Update this unit's datasource URL and re-publish relation data.
Description
Useful when this unit's address (e.g. an ingress-per-unit URL) becomes available or changes after this object is constructed. The value is per-unit.
GrafanaSourceProvider. get_source_data( self )
Get the Grafana data assigned by the remote end(s) to this datasource.
Description
Returns a mapping from remote application UIDs to GrafanaSourceData.
GrafanaSourceProvider. get_source_uids( self )
Get the datasource UID(s) assigned by the remote end(s) to this datasource.
Description
DEPRECATED: This method is deprecated. Use the get_source_data instead.
Returns a mapping from remote application UIDs to unit names to datasource uids.
class GrafanaSourceConsumer
Description
A consumer object for working with Grafana datasources. None
Methods
GrafanaSourceConsumer. __init__( self , charm: CharmBase , grafana_uid: str , grafana_base_url: str , relation_name: str )
A Grafana based Monitoring service consumer, i.e., the charm that uses a datasource.
Arguments
a :class:CharmBase instance that manages this
instance of the Grafana source service.
an unique identifier for this grafana-k8s application.
the base URL (potentially ingressed) for this grafana-k8s application.
string name of the relation that is provides the Grafana source service. It is strongly advised not to change the default, so that people deploying your charm will have a consistent experience with all other charms that provide Grafana datasources.
GrafanaSourceConsumer. upgrade_keys( self )
Description
On upgrade, ensure stored data maintains compatibility. None
GrafanaSourceConsumer. update_sources( self , relation )
Re-establish sources on one or more relations.
Arguments
a specific relation for which the datasources have to be
updated. If not specified, all relations managed by this
:class:GrafanaSourceConsumer will be updated.
Description
If something changes between this library and a datasource, try to re-establish datasources.
GrafanaSourceConsumer. sources( self )
Description
Returns an array of sources the source_consumer knows about. None
GrafanaSourceConsumer. sources_to_delete( self )
Description
Returns an array of source names which have been removed. None
GrafanaSourceConsumer. set_peer_data( self , key: str , data: Any )
Description
Put information into the peer data bucket instead of StoredState. None
GrafanaSourceConsumer. get_peer_data( self , key: str )
Description
Retrieve information from the peer data bucket instead of StoredState. None