What’s new with Grafana variables: less clutter, easier reuse across dashboards, and more
Template variables are one of the most used features in Grafana, providing users with a dropdown to turn one dashboard into hundreds: pick a different cluster, region, or service, and every panel updates.
For most of Grafana's history, variables had a single home at the top of the dashboard. Each dashboard had one flat list of variables, and that list was shared by every panel on it. But as dashboards grew in size and number, those controls became harder to organize and maintain. The top of a dashboard could get crowded with dropdowns that only applied to certain panels, while shared controls were copied across dashboards and had to be updated individually.
To address these challenges, we’ve rolled out two new features in Grafana:
Section-level variables for rows and tabs (generally available): these variables let you place controls near the panels they affect, reducing clutter at the top of the dashboard.
Cross-dashboard variables (public preview): these are variables you can define once and reuse across dashboards, keeping configurations consistent and easier to maintain.
In this post, we’ll take a closer look at each variable type, its use cases, and how to get started.
Section-level variables: define and manage variables by row or tab
Section-level variables let you define variables and filters for a specific row or tab, so they apply only to the panels inside that section. Placing a control next to the panels it affects makes it clear what a selection will change, and it keeps the top of the dashboard uncluttered.
Let’s say you want two $instance dropdowns, one for your API gateway panels and one for your database panels. Traditionally, with one flat namespace, you’d end up with names like $api_instance and $db_instance, or you’d split the dashboard into two.
Now, with section-level variables, the API gateway section and the database section can each have its own $instance variable with its own query and selected value. Both sections still share the dashboard time range, and nothing else on the dashboard changes.
Here’s how section-level variables work:
Resolution goes from nearest to farthest. When a panel uses a variable, Grafana first checks the section the panel is in for a matching variable, then any parent sections (rows and tabs can nest), then the dashboard.
Filters work, too. You can add Filter and Group by controls at the section level, so a section can filter by its own labels.
The query editor knows about scope. Autocomplete only suggests variables the panel you're editing can actually see.
Restructuring is safe. If you ungroup a row or tab, its section variables move up a level instead of disappearing.
To add a section-level variable, edit the dashboard, select a row or tab, and open Variables in the options pane.
Cross-dashboard variables: define and manage variables across multiple dashboards
Section variables solved scope inside a dashboard. The bigger request has always been scope across dashboards.
Cross-dashboard variables let you define shared controls once and reuse them across dashboards, reducing duplicate work and keeping configurations consistent.
They come in two scopes:
Global variables are defined at the organization level and can be used on any dashboard.
Folder variables are defined on a folder and can be used on the dashboards in that folder.
Here's what cross-dashboard variables looks like in practice: say, for example, a few team dashboards all use the same environment picker with the values dev, staging, and prod. With a cross-dashboard variable, you define environment once. When you add a new environment, or change the query behind it, every dashboard that uses the variable gets the update, without needing to edit each dashboard individually.
Cross-dashboard variables achieve this by separating two things: the definition (the variable's type, query, and options) is shared and managed in one place, while the value someone selects (for example, prod) is chosen on each dashboard as they use it.
Managing cross-dashboard variables
Go to Dashboards > Variables to create, edit, move, and delete cross-dashboard variables. The editor is the same one you already use for dashboard variables, with the same types (Query, Custom, Data source, Text, and so on). The only new field is Folder:
Leave it at the root to create a global variable.
Pick a folder to create a folder variable.
To change a variable's scope, move it between folders or move it to the root.
Choosing variables per dashboard
A dashboard doesn't use any cross-dashboard variables until you choose them. In the dashboard's settings, pick which global and folder variables it should use:
Specific variables: only the ones you pick appear on the dashboard. Variables created later in that scope don't appear until you add them.
All variables in a scope: every global variable, or every variable in the dashboard's folder, appears. New ones show up automatically.
Precedence: nearest scope wins
With four places a variable can come from, there's one simple rule: the nearest definition wins.
If a dashboard defines its own region, it overrides the global region. A section can override both.
On the dashboard, cross-dashboard variables appear alongside the dashboard's own variables and work the same way: you can select values, sync them to the URL, and use them in queries, titles, and links. In edit mode, they're read-only on the dashboard. To change the definition, go to Dashboards > Variables.
Managing cross-dashboard variables as code
Under the product name, a cross-dashboard variable is a Variable resource with its own API. It isn't stored inside any dashboard, but it is tied to dashboards: it lives in the dashboard API group (dashboard.grafana.app) next to Dashboard, and it does nothing until a dashboard opts into it.
Because it's a separate resource, it's served by the same Kubernetes-style API platform as v2 dashboards and folders. You can create, list, and update variables like any other Grafana resource, without touching a dashboard.
A Variable uses the same shape as a variable inside a dashboard, so anything you already know about dashboard variable JSON carries over:
{ "apiVersion": "dashboard.grafana.app/v2beta1", "kind": "Variable", "metadata": { "annotations": { "grafana.app/folder": "<folder-uid>" } }, "spec": { "kind": "CustomVariable", "spec": { "name": "environment", "query": "dev,staging,prod", "current": { "text": "prod", "value": "prod" }, "description": "Deployment environment" } } } Leave out the grafana.app/folder annotation to make it global.
During the public preview, Variable is served at v2beta1. It will move to v2 when cross-dashboard variables reach general availability.
Provisioning with Terraform
Because Variable is a standard API resource, you can manage it with the Grafana Terraform provider's grafana_apps_generic_resource. Here's a folder, a folder variable, and a dashboard that opts into it, all in one Terraform configuration:
resource "grafana_apps_generic_resource" "payments_folder" { manifest = { apiVersion = "folder.grafana.app/v1" kind = "Folder" metadata = { name = "payments" } spec = { title = "Payments" } } } resource "grafana_apps_generic_resource" "payments_env" { manifest = { apiVersion = "dashboard.grafana.app/v2beta1" kind = "Variable" metadata = { annotations = { "grafana.app/folder" = "payments" } } spec = { kind = "CustomVariable" spec = { name = "environment" query = "dev,staging,prod" current = { text = "prod", value = "prod" } } } } depends_on = [grafana_apps_generic_resource.payments_folder] } resource "grafana_apps_generic_resource" "payments_overview" { manifest = { apiVersion = "dashboard.grafana.app/v2" kind = "Dashboard" metadata = { name = "payments-overview" annotations = { "grafana.app/folder" = "payments" "grafana.app/useCrossDashboardVariables" = jsonencode({ global = ["region"] folder = ["environment"] }) } } spec = { title = "Payments overview" # ... panels and layout } } depends_on = [grafana_apps_generic_resource.payments_env] } The opt-in setting also works naturally as code. Leave out the annotation, and the dashboard uses no cross-dashboard variables. List names, and it uses exactly those. Nothing appears on a provisioned dashboard unless the config says so.
The same API also works with kubectl, curl, or any tool that talks to Grafana's /apis endpoints.
Wrapping up
These updates mean Grafana variables have one consistent model, from a single section of a dashboard to your whole organization:
Variable scope | Defined on | Stored in | Applies to |
Section | A row or tab | The dashboard (v2 schema) | Panels in that section |
Dashboard | The dashboard | The dashboard (v2 schema) | The whole dashboard |
Folder | A folder | Its own | Opted-in dashboards in the folder |
Global | The stack | Its own | Opted-in dashboards anywhere |
The result is less clutter where you build dashboards, and less copy-and-paste where you maintain them.
Here are two quick ways to get started:
Give two sections of a dashboard their own controls. Edit a dashboard, select a row or tab, and open Variables in the options pane. Add an
instancevariable to each section and see how each one only affects its own panels.Share a variable across a few dashboards. Go to Dashboards > Variables and create a variable such as
environment. Then open the settings for two or three dashboards and choose it under cross-dashboard variables.
To learn more, see the docs for section-level variables and cross-dashboard variables. We'd love to hear how you use them. Please reach out in our Community Slack.