Grafana Dashboard Variables: Dynamic Filtering Across Multiple Data Sources
How Grafana dashboard variables interpolate into panel queries, with a worked Prometheus env→service→instance chain, per-data-source quoting rules, and the multi-value and performance pitfalls to avoid.
19 Mar 2026, 00:58 UTC

The short answer
Grafana variables let one dropdown at the top of a dashboard rewrite every panel query at view time, so a single dashboard serves many hosts, services, or environments instead of one copy per filter value. The mechanism is simple: a variable holds a value (or list of values), and every occurrence of $varname in a panel query is replaced with that value before the query is sent to the data source. The parts that trip people up are how multi-value variables are formatted, how to chain one variable off another, and how quoting differs between data sources.
This guide assumes Grafana 9.x or later; the variable UI has been stable since Grafana 8, but menu labels differ slightly in older versions.
How interpolation actually works
When Grafana renders a panel, it runs a preprocessing pass over the query text. Any token matching $varname, ${varname}, or [[varname]] is replaced with the variable's current value, formatted according to the variable's settings and any format suffix you supply (for example ${varname:csv} or ${varname:regex}). The substituted string is what the data source actually receives — Grafana does not send the variable separately.
Two consequences follow from this:
- The variable is just string substitution. If the substituted text is not valid in the target query language, the query fails at the data source, not in Grafana.
- Multi-value variables are formatted as a list. By default Grafana joins multiple selections with a pipe and wraps them in parentheses, e.g.
(api|worker), which is regex-friendly. Use a format suffix when you need something else.
Worked example: environment → service → instance chain
A common pattern is three chained variables so users pick an environment, then see only the services in that environment, then only the instances running that service. Assume a Prometheus data source where metrics carry env, service, and instance labels.
Create the variables under Dashboard settings → Variables → New (requires Editor or Admin role on the dashboard):
- env — Type: Query, Data source: your Prometheus, Query:
label_values(up, env). Enable Multi-value and Include All if you want a global view. - service — Type: Query, Query:
label_values(up{env=~"$env"}, service). This is the chain: the service list is re-queried wheneverenvchanges, filtered to the selected environments. Set Refresh to "On time range change" or "On dashboard load" depending on how dynamic your labels are. - instance — Query:
label_values(up{env=~"$env", service=~"$service"}, instance).
Then a panel query uses all three:
rate(http_requests_total{env=~"$env", service=~"$service", instance=~"$instance"}[5m])Note the =~ (regex match) instead of =. This is required for multi-value variables in Prometheus, because the substituted value (api|worker) is a regex, not a literal string. With = and multiple selections, the query silently matches nothing — one of the most common variable bugs.
Quoting rules differ per data source
This is where multi-value variables most often break:
| Data source | Single value | Multi-value |
|---|---|---|
| Prometheus | job="$job" | job=~"$job" (regex) |
| InfluxDB | host =~ /^$host$/ | same regex form |
| SQL (Postgres/MySQL) | WHERE env = '$env' | WHERE env IN ($env) — Grafana quotes each element automatically inside IN |
For SQL sources, do not wrap $env in quotes yourself when using IN; Grafana expands a multi-value variable to 'a','b','c' and your manual quotes would produce invalid syntax. If you need explicit control, use a format suffix such as ${env:csv} or ${env:singlequote} and build the clause yourself.
Verifying the setup
After creating each variable:
- Open the dashboard and confirm the dropdown populates. An empty dropdown usually means the label query returned nothing — run the same
label_valuesquery in Explore to check. - Change a selection and confirm panels update without a manual refresh.
- To see exactly what was sent, open the panel menu → Inspect → Query and look at the expanded query. This is the definitive check for quoting and multi-value formatting problems.
Limits and common mistakes
- Load-time cost. Every Query-type variable executes its query when the dashboard loads (and chained variables re-execute on every parent change). A dashboard with six variables over a high-cardinality source can spend seconds just populating dropdowns. Mitigate by narrowing label queries with a matcher (e.g.
label_values(up{job="node"}, instance)) and avoiding Refresh: On time range change unless needed. - Chained-variable dead ends. If a parent selection yields no children, the child dropdown is empty and panels go blank. This is expected behavior, but consider an All option with a catch-all regex (
.*) so dashboards degrade gracefully. - Static variable types. Constant and Custom variables never update themselves; the list is whatever you typed in settings. They are fine for fixed sets (regions, environments) but silently go stale for anything dynamic.
- Regex metacharacters in values. Because multi-value values are interpolated as regex in Prometheus-style queries, label values containing characters like
.or+can over-match. Use${var:regex}escaping behavior deliberately, or anchor with^...$where the query language allows. - URL state. Selections are carried in URL parameters (
?var-env=prod), which is useful for deep links but means shared links bake in the sharer's selections.
Used with these constraints in mind, variables turn a dashboard from a static report into a small self-service tool — one definition, filtered on demand by whoever is looking at it.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.