ObservabilityXXV · Grafana Data SourcesGrafanaDataSources
Tags and Filtering
What you'll learn
- Attach metadata tags to a Grafana data source via jsonData.tags and read them back through the API
- Explain what the BuiltIn marker does and why it changes how a data source appears in selectors
- Recognise the legacy Loki and MSSQL naming convention and when it still matters in Grafana 11.x
- Trace how a data source UID propagates into the proxy URL and what breaks when the UID changes
- Use a tag-driven variable query in a dashboard to switch between environment-specific data sources
Prerequisites
Verified against Prometheus 2.55.x · Alertmanager 0.28.x · node_exporter 1.8.x · blackbox_exporter 0.26.x · Grafana 11.x · Loki 3.x · Tempo current · OpenTelemetry Collector 0.110.x · Grafana Alloy current · Docker Engine 28.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-13
A single dashboard renders cleanly in staging and silently
returns “no data” in production. The dashboards are identical.
The data source names are identical. The only difference is the
data source UID — staging’s UID is prom-stg-eu and
production’s UID is prom-prod-eu. The dashboard references
the name, the variable query filters by a tag, and the
environment never resolved.
The same dashboard imports a Loki data source whose name
field is Loki (capitalised, with the explicit Grafana legacy
form). A new data source with name: loki (lowercase, the
canonical form) is added six months later. The alerting engine
silently picks the wrong source for some rule groups. The
lesson this time is about the metadata attached to a data
source: tags, the BuiltIn marker, the legacy naming
conventions that still affect some features, and the UID that
governs the proxy URL.
What it is
A Grafana data source is more than a name, a URL, and a credential. It also carries:
- A UID — the stable identifier used in the proxy URL, in dashboards, and in derived-field links. UID changes break every reference.
- A name — the human-readable label in selectors. Names can be renamed safely; UIDs cannot.
- Tags — a list of strings in
jsonData.tags. Tags are how a dashboard variable filters “show me the Prometheus for this environment”. Tags do not affect the proxy URL. - A
BuiltInmarker — Grafana’s own preinstalled data sources (TestData, Grafana Cloud integrations, the---prefixed service data sources) carry this marker. The UI uses it to hide built-in sources from selectors by default. - A legacy naming convention — Grafana’s oldest data source types (Loki, MSSQL, Elasticsearch, Prometheus, InfluxDB, MySQL, Postgres, Graphite) used capitalised display names. Grafana 11.x still recognises these forms in a few internal places. A new data source with the lowercase form is the canonical shape; the capitalised form is a compatibility alias.
Why a sysadmin cares
Data source metadata is the configuration surface where dashboards, alerting, and Explore decide which upstream they talk to. Four production shapes appear repeatedly:
- UID drift after a rename. An operator renames a data
source from
prom-prod-eutoprom-prod-eu-westbecause the region split. The name change is harmless. The UID change breaks every dashboard and derived-field reference in the platform. - Tags that no dashboard reads. Tags are added for future-proofing (“we will filter by environment later”) and are never actually consumed. The metadata cost is paid on every reload; the benefit is zero.
- BuiltIn data sources leaking into selectors. A TestData
data source with the
BuiltInmarker is supposed to be hidden from selectors, but a panel that filters bytag = "prometheus"shows it anyway because the filter does not exclude BuiltIn sources. - Legacy
LokiandMSSQLnames causing dual resolution. Two data sources with the same name (one capitalised, one lowercase) are treated as two distinct sources by Grafana 11.x, but a feature that resolves by display name picks one and a feature that resolves by internal type picks the other. Alerting rules silently pick the wrong one.
How it works: metadata in motion
grafana.ini provisioning yaml runtime api
----------- ---------------- ------------
name: prom-prod-eu
uid: prom-prod-eu -----> GET /api/datasources
type: prometheus |
jsonData: v
tags: [prod, eu] {
tlsAuthWithCACert: "id": 1,
true "uid": "prom-prod-eu",
} "name": "prom-prod-eu",
secureJsonData: "type": "prometheus",
tlsCACert: |... "tags": ["prod", "eu"],
basicAuthPassword: ${...} "isDefault": true
} }
Three observations:
- Tags are stored on the data source, not on the
dashboard. A dashboard variable query of the form
prometheus(the data source type) and filtertag = prodreturns the data sources that match both. Tags are how dashboards ask “give me the production Prometheus” without naming it. - UIDs govern the proxy URL. Every panel and Explore
query resolves to
/api/datasources/proxy/uid/<uid>/.... The UID is set at provisioning time and is stable for the life of the data source. The data source proxy is the authoritative answer to “which Grafana-configured upstream does this query target”. - BuiltIn is a UI-side convention. Grafana’s preinstalled
data sources (TestData, Grafana Cloud integrations, the
---prefixed services) carryBuiltInin their tags. The data source selector UI hides them by default. They remain reachable through their UID.
How to configure it
# /etc/grafana/provisioning/datasources/prometheus.yml
apiVersion: 1
datasources:
- name: prom-prod-eu
uid: prom-prod-eu # stable; never reuse across instances
type: prometheus
access: proxy
orgId: 1
url: https://prom-prod-eu.internal:9090
isDefault: true
editable: false
jsonData:
# Tags drive the dashboard variable filter.
# Convention: environment + region + role.
tags:
- prod
- eu
- metrics
tlsAuth: false
tlsAuthWithCACert: true
tlsSkipVerify: false
timeInterval: 15s
httpMethod: POST
secureJsonData:
tlsCACert: |
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUJx...
-----END CERTIFICATE-----
basicAuthPassword: ${PROM_PASSWORD}
- name: prom-stg-eu
uid: prom-stg-eu # different UID; same shape
type: prometheus
access: proxy
orgId: 1
url: https://prom-stg-eu.internal:9090
isDefault: false
editable: false
jsonData:
tags:
- staging
- eu
- metrics
tlsAuth: false
tlsAuthWithCACert: true
timeInterval: 15s
httpMethod: POST
secureJsonData:
tlsCACert: |
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUJx...
-----END CERTIFICATE-----
basicAuthPassword: ${PROM_STG_PASSWORD}
The dashboard variable that consumes the tags:
# In a Grafana dashboard JSON, a variable of type "datasource":
{
"name": "ds",
"type": "datasource",
"query": "prometheus",
"filters": [
{ "tags": ["eu"] }
],
"current": { "text": "prom-prod-eu", "value": "prom-prod-eu" }
}
A few production notes on the options:
uidis set in YAML and never reused. Two data sources with the same UID is a provisioning error; Grafana rejects the second one with “data source with the same UID already exists”.- Tags are strings, lowercase, hyphen-separated. Grafana
does not enforce a format, but the convention
env-regionis the shape that most dashboards filter on. - A tag that no variable filter consumes is dead metadata. Remove tags that nothing reads.
isDefaultselects the implicit target for new panels. Setting it on more than one Prometheus per Grafana instance produces surprising behaviour in Explore.editable: falseprevents click-ops from drifting the metadata. Production data sources should not be editable in the UI.
How to validate it
# READ-ONLY: list all data sources with their tags.
curl -fsS -u grafana-admin:$GRAFANA_ADMIN \
http://grafana.internal:3000/api/datasources | jq '.[] | {uid, name, type, tags}'
# [
# {
# "uid": "prom-prod-eu",
# "name": "prom-prod-eu",
# "type": "prometheus",
# "tags": ["prod", "eu", "metrics"]
# },
# {
# "uid": "prom-stg-eu",
# "name": "prom-stg-eu",
# "type": "prometheus",
# "tags": ["staging", "eu", "metrics"]
# }
# ]
# READ-ONLY: the proxy URL uses the UID, not the name.
curl -fsS -u grafana-admin:$GRAFANA_ADMIN \
--data-urlencode 'query=up' \
http://grafana.internal:3000/api/datasources/proxy/uid/prom-prod-eu/api/v1/query
# {"status":"success","data":{"resultType":"vector","result":[...]}}
# READ-ONLY: filter the API by tag (returns data sources whose
# tags include the requested value).
curl -fsS -u grafana-admin:$GRAFANA_ADMIN \
'http://grafana.internal:3000/api/datasources?tag=prod' | jq '.[] | .uid'
# "prom-prod-eu"
# CONFIGURATION: reload Grafana provisioning.
sudo systemctl reload grafana-server
A clean validation: the UID is set, the tags are present, the proxy URL works, and the data source selector in the UI shows the source when the dashboard variable filter matches.
How it can fail
The most expensive data-source metadata failure modes from real production incidents.
- UID regenerated after a rename. An operator renames
prom-prod-eutoprom-prod-eu-westand the UID is omitted from the YAML. Grafana regenerates the UID from the new name. Every dashboard that references the old UID renders “data source not found”. The symptom is widespread panel failures correlated with the rename commit. - Tags inconsistent across environments. Production
carries
tags: [prod, eu]; staging carriestags: [staging, eu]. A dashboard variable filters byeuand resolves correctly in both. A different variable filters byprodand resolves correctly in production but fails to match in staging because staging has noprodtag. The symptom is “dashboard works in prod, fails in staging”. - BuiltIn marker added by mistake. A new data source
includes
BuiltInin its tag list. The data source selector UI hides it. Dashboards that reference the source by UID work; new panels cannot be created against it. The symptom is “the data source is configured but the UI refuses to show it”. - Legacy capitalised name conflicts with lowercase
form. Two data sources exist:
Loki(legacy form) andloki(canonical form). The alerting engine resolves by internal type and picks the lowercase; a feature that resolves by display name picks the capitalised. The symptom is “alerting routes to one source, dashboards route to the other”. - Tag added after the dashboard variable is written. A
new tag
metrics-gen2is added to the data source after the dashboard variable filter is committed. The variable does not pick up the new tag because the filter is hard- coded. The symptom is “the data source has the right tags but the dashboard does not see them”. - Reused UID across instances. A staging data source is provisioned with the same UID as a production data source (a copy-paste error). Grafana rejects the second provisioning with an error. The symptom is “staging provisioning fails; the operator reverts; production silently drifts”.
How to troubleshoot it
The diagnostic order is “is the UID stable?”, “are the tags correct?”, “is the BuiltIn marker accidental?”, “is the legacy naming conflict resolved?”.
- Confirm the UID.
GET /api/datasources/uid/<uid>andGET /api/datasources(which lists every UID). If a dashboard is failing, the first check is whether the UID in the dashboard JSON matches the UID in the provisioning YAML. - Confirm the tags.
GET /api/datasources?tag=<value>. A missing tag returns no data sources; an extra tag returns too many. - Check the BuiltIn marker. Inspect the
tagsarray for the data source. RemoveBuiltInif it was added by mistake. - Resolve legacy naming conflicts. A capitalised
Lokinext to a lowercaselokiis a configuration bug. Pick one and remove the other. - Inspect the dashboard variable filter. The variable filter is JSON inside the dashboard JSON. Compare it against the tag list in the data source YAML; a missing tag in the YAML is a silent failure.
- Inspect Grafana’s logs.
/var/log/grafana/grafana.logrecords provisioning reloads. A “data source with the same UID already exists” error appears as a single line with the conflicting UID.
Security implications
- Tags are not security boundaries. A tag of
proddoes not restrict who can query the data source. RBAC controls the access; tags filter the dashboard selector. - UIDs are guessable. A Grafana with predictable UIDs
(
prom-prod-eu) lets an attacker enumerate data sources. Consider UUIDs for high-value data sources, especially when the data source URL is itself sensitive. editable: falseprevents the team from accidentally loosening the TLS posture through the UI. The team can still query the data source; they cannot reconfigure it without a provisioning reload.- BuiltIn is a UI affordance, not a security feature. A data source marked BuiltIn is reachable by UID regardless of its visibility in the selector.
Performance implications
- Tag-based variable queries are not cached. Every dashboard refresh re-queries the data source selector. Tags are inexpensive; the cost is dominated by the variable regex.
- UID-based proxy URLs are stable. The proxy route table is rebuilt on provisioning reload; in-between reloads, the route is a constant-time hash lookup.
BuiltInfiltering is UI-only. It does not affect query performance; it only affects which sources the selector displays.- Legacy naming aliases are resolved once at provisioning time. The cost is paid at reload, not at query time.
Production guidance
- Pin the UID in YAML. Renaming a data source is fine; changing the UID breaks every reference.
- Use a tag convention:
env-region-role. Document it in the team’s instrumentation guide. - Mark TestData and the preinstalled
---prefixed data sources withBuiltIn; never mark a production data source as BuiltIn. - Pick one form for each legacy name. Either
Lokiorloki, not both. - Validate UID stability in CI. A pre-commit hook that compares the dashboard JSON’s data source references against the provisioning YAML’s UIDs catches the silent regeneration shape.
- Reload Grafana provisioning through the GitOps pipeline.
- Run a periodic exercise: pick one dashboard per environment, confirm the variable filter resolves to the right UID, and confirm the proxy URL works end-to-end.
Verification
You should now be able to answer:
- What does the
BuiltInmarker do, and what is the right shape for it in production data sources? - Why does a UID change break every dashboard reference, while a name change does not?
- What does a tag-driven dashboard variable query actually filter on, and which fields of the data source does it read?
- When does the legacy capitalised name (Loki, MSSQL) still matter in Grafana 11.x?
Quiz
Knowledge check · 8 questions
Q1. Which field of a Grafana data source is the stable identifier that the data source proxy URL uses?
Q2. Adding `BuiltIn` to the tag list of a production data source is a reasonable way to hide it from the data source selector UI.
Q3. A dashboard variable of type `datasource` filters by `tag = prod`. Which fields of the data source does the filter read?
Q4. Which of these are valid reasons to change the UID of a data source?
Q5. Name the Grafana 11.x API endpoint that returns the list of data sources filtered by tag.
Q6. A data source is named `Loki` (capitalised) and another is named `loki` (lowercase). The alerting engine resolves by internal type and picks the lowercase; a feature resolves by display name and picks the capitalised. What is the right production posture?
Q7. Tags are an authentication or authorisation boundary in Grafana.
Q8. A staging data source was provisioned with the same UID as production. What does Grafana do at provisioning reload?
Passing score: 75%. Answers are checked in this browser.