Platform:

Channel Revision Published Runs on
2/stable 180 16 Mar 2026
Ubuntu 24.04
2/candidate 180 13 Mar 2026
Ubuntu 24.04
2/beta 180 13 Mar 2026
Ubuntu 24.04
2/edge 180 11 Mar 2026
Ubuntu 24.04
dev/edge 190 30 Jun 2026
Ubuntu 26.04
12.4/edge 191 Today
Ubuntu 26.04
1/stable 160 10 Oct 2025
Ubuntu 20.04
1/candidate 160 10 Oct 2025
Ubuntu 20.04
1/beta 160 10 Oct 2025
Ubuntu 20.04
1/edge 160 10 Oct 2025
Ubuntu 20.04
juju deploy grafana-k8s --channel 2/stable --trust

charms.grafana_k8s.v1.grafana_source

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 (default True): 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 form juju_{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 (default False): 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 form juju_{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 as app_datasource_url.
  • unit_datasources=True <-> use ingress-per-unit; pass each unit's own URL as unit_datasource_url (the value is per-unit, since the provider runs in every unit).
Breaking changes from v0
  • The source_url and is_ingress_per_app constructor arguments were removed. Use app_datasource/unit_datasources and app_datasource_url/unit_datasource_url instead.
  • The update_source() method was replaced by update_app_source() and update_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} to juju_..._{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_host and sees an empty grafana_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:

  1. Instantiate the GrafanaSourceConsumer object 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 the grafana-source interface.

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,
    )
    ...
  1. A Grafana charm also needs to listen to the GrafanaSourceEvents events emitted by the GrafanaSourceConsumer by 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.


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

charm

a :class:CharmBase object which manages this :class:GrafanaSourceProvider object. Generally this is self in the instantiating class.

source_type

an optional (default prometheus) source type required for Grafana configuration. The value must match the DataSource type from the Grafana perspective.

source_port

an optional (default 9090) source port required for Grafana configuration. Used to build the datasource address when an explicit URL is not provided.

relation_name

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.

refresh_event

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.

extra_fields

a :dict: which is used for additional information required for some datasources in the jsonData field

secure_extra_fields

a :dict: which is used for additional information required for some datasources in the secureJsonData

app_datasource

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.

unit_datasources

whether to publish one datasource per unit, each keyed by unit number. Defaults to False.

app_datasource_url

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.

unit_datasource_url

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 as app_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 as unit_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

charm

a :class:CharmBase instance that manages this instance of the Grafana source service.

grafana_uid

an unique identifier for this grafana-k8s application.

grafana_base_url

the base URL (potentially ingressed) for this grafana-k8s application.

relation_name

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

relation

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