Idioma: Español
IR-11 · observability
Pushkar Kumar 11 min de lectura
Datasource ${DS_PROMETHEUS} was not found: causa y solución
Por qué Grafana dice Datasource ${DS_PROMETHEUS} was not found en dashboards provisionados, cómo hallar cada placeholder con jq y cuatro arreglos para CI.
- observability
- prometheus
En esta página
- ¿Por qué Grafana dice “Datasource $${DS_PROMETHEUS} was not found”?
- ¿De dónde sale el nombre DS_PROMETHEUS?
- ¿Cómo confirmas qué placeholders hay en el archivo?
- ¿Cuál es tu causa?
- ¿Cómo se corrige para el provisioning y la API?
- ¿Borrar __inputs es la solución que no soluciona nada?
- ¿Cómo evitas que vuelva en CI?
- ¿Qué revisar antes de publicar un archivo de dashboard?
Exportaste un dashboard desde staging, subiste el JSON junto a tu configuración de provisioning y reiniciaste Grafana. El dashboard aparece en la carpeta correcta. Todos los paneles están vacíos, y cada uno lleva la misma esquina roja:
Datasource ${DS_PROMETHEUS} was not foundTu datasource de Prometheus existe y funciona en Explore. Este error de datasource no encontrado en Grafana, que mucha gente busca como “DS_PROMETHEUS not found”, no significa que falte el datasource. Grafana imprime tal cual la clave de búsqueda que recibió, y esa clave es un placeholder que nadie rellenó. En cuanto lo ves así, la solución es mecánica.
TL;DR
- La opción de exportar para compartir (“Export for sharing externally” en Grafana 10 a 11) cambia cada datasource por
${DS_<NAME>}y los enumera en__inputs. Solo el diálogo Import y la API de importación los rellenan.- El provisioning por archivos,
POST /api/dashboards/dby Terraform guardan el archivo tal cual, así que el placeholder llega hasta el panel.- Para encontrarlos:
jq -r 'paths(type == "string" and startswith("${DS_")) | map(tostring) | join(".")' dashboard.json.- Se corrige sustituyendo un
{ type, uid }real, fijando ese uid en el provisioning del datasource, añadiendo una variable de datasource llamadaDS_PROMETHEUSo enviando el dashboard a/api/dashboards/importconinputs.
¿Por qué Grafana dice “Datasource ${DS_PROMETHEUS} was not found”?
La referencia al datasource de un panel pasa por DatasourceSrv.get(), que procesa el uid con el servicio de plantillas antes de buscarlo. El servicio de plantillas devuelve sin cambios cualquier ${...} para el que no tenga variable. Así que la clave sigue siendo la cadena literal ${DS_PROMETHEUS}, ningún datasource tiene ese uid ni ese nombre, y loadDatasource() rechaza la petición con el mensaje que ves.
El placeholder llegó ahí durante la exportación. Con la opción de exportar activada, Grafana escribe esto:
{
"__inputs": [
{
"name": "DS_PROMETHEUS",
"label": "Prometheus",
"description": "",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
}
],
"panels": [
{
"type": "timeseries",
"title": "Request rate",
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"expr": "sum(rate(http_requests_total{job=\"$job\"}[5m]))"
}
]
}
],
"templating": {
"list": [
{
"type": "query",
"name": "job",
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"query": "label_values(up, job)"
}
]
}
}__inputs es una pregunta para quien importa: ¿cuál de tus datasources debe sustituir a DS_PROMETHEUS? El diálogo Import la formula. Después, el evaluador de plantillas del servidor reemplaza cada ${name} declarado por el valor que elegiste y elimina __inputs.
Nada más la formula. Un comentario del propio código de Grafana en export_inputs.go dice que una plantilla escrita mediante Terraform, provisioning o un POST directo “is stored verbatim” y que “DS_* inputs carry no value to substitute”. La petición para soportarlo en provisioning, grafana/grafana#10786, está abierta desde 2018.
Importante: Grafana 13.2.0 (#129213) empezó a resolver los inputs constantes
${VAR_*}en dashboards guardados mediante provisioning o un POST directo. Deja explícitamente sin resolver los inputsDS_*, así que en 13.2.2 este error se comporta exactamente igual que antes.
En Grafana 7.x y las primeras 8.x el mismo fallo decía Datasource named ${DS_PROMETHEUS} was not found, la redacción que citan la mayoría de los hilos de los foros. Una variable de consulta que apunta al placeholder aparece en cambio como un aviso de templating, que en los hilos se recoge como Error updating options: Datasource named ${DS_PROMETHEUS} was not found o Failed to upgrade legacy queries Datasource ${DS_PROMETHEUS} was not found.
¿De dónde sale el nombre DS_PROMETHEUS?
Del nombre visible del datasource, no de su tipo. El exportador lo construye como 'DS_' + ds.name.replace(' ', '_').toUpperCase(). Obtienes DS_PROMETHEUS solo porque el nombre por defecto es “Prometheus”. Un datasource llamado “Thanos” se exporta como ${DS_THANOS}.
Ese replace de JavaScript con un patrón de tipo cadena cambia solo el primer espacio. Un datasource llamado “Prometheus Prod EU” se exporta como ${DS_PROMETHEUS_PROD EU}, con el espacio incluido. Las referencias de library panels reciben el sufijo -FOR-LIBRARY-PANEL, y las variables constantes se convierten en inputs VAR_<NAME>.
La opción que produce todo esto ha cambiado de sitio entre versiones:
- Grafana 10 a 11: Export for sharing externally.
- Grafana 11.2 a 12.3 también la rotulan Export the dashboard to use in another instance (de 12.1 a 12.3 se llega por Export → Export as code).
- Grafana 12.4 y 13: Export → Export as code, opción Share dashboard with another instance, con un modelo Classic o V2 Resource en Advanced options.
Si exportas para tu propio repositorio de provisioning, deja la opción desactivada. La documentación sobre compartir detalla las opciones de cada versión.
¿Cómo confirmas qué placeholders hay en el archivo?
Tres comprobaciones, en este orden. Primero, enumera lo que el archivo espera que se rellene:
jq -c '.__inputs[] | {name, pluginId}' dashboard.json{"name":"DS_PROMETHEUS","pluginId":"prometheus"}Segundo, busca cada cadena que todavía contenga un placeholder, incluidas las de las variables de consulta. Mantén el filtro entre comillas simples para que la shell no expanda ${DS_...}:
jq -r 'paths(type == "string" and startswith("${DS_")) | map(tostring) | join(".")' dashboard.jsonpanels.0.datasource.uid
panels.0.targets.0.datasource.uid
templating.list.0.datasource.uidgrep -nF '${DS_' dashboard.json da la misma respuesta con números de línea. Tercero, enumera los uids que existen de verdad en la instancia de destino. GET /api/datasources devuelve id, uid, orgId, name y type, según la documentación de la API de data sources:
curl -sS -H "Authorization: Bearer $GRAFANA_TOKEN" "$GRAFANA_URL/api/datasources" \
| jq -r '.[] | [.uid, .name, .type] | @tsv'Para ahorrarte el jq, pega el JSON del dashboard en el Grafana Dashboard Validator. Con el archivo exportado de arriba informa de un error, bajo la regla unresolved-ds-input. Los avisos que lo acompañan se deben al extracto recortado, que no tiene uid, title, gridPos ni schemaVersion:
[error] unresolved-ds-input @ __inputs[0]
"__inputs" declares "DS_PROMETHEUS", an import placeholder that only the Grafana import dialog fills in.Su límite: solo lee el archivo. Un dashboard con un uid de aspecto real, como P1809F7CD0C75ACF3, pasa tanto si ese uid existe en tu instancia como si no, así que compruébalo contra /api/datasources.
¿Cuál es tu causa?
| Lo que ves | Causa más probable |
|---|---|
Hay __inputs y el archivo lo carga el provisioning o Terraform | Un archivo solo para importar usado como archivo provisionado |
No hay __inputs, pero sigue habiendo ${DS_...} en los paneles | Placeholders que quedaron tras borrar __inputs |
| El error nombra un uid de aspecto real, no un placeholder | El uid difiere entre instancias |
Existe la variable DS_PROMETHEUS, pero no es type: datasource | Variable con el tipo equivocado |
1. Se provisionó un archivo exportado. Señal: __inputs está presente y el dashboard entró por el proveedor de archivos, /api/dashboards/db o Terraform. Solución: sustituye un uid real o cárgalo mediante la API de importación. Verificación: el recuento de placeholders de más abajo da 0.
2. Placeholders sin __inputs. Señal: alguien borró __inputs, o un chart empaquetó el dashboard sin él. El diálogo Import ya no tiene nada que preguntar, y el error sobrevive a una importación desde la interfaz. Solución: sustituye o añade la variable de datasource. Verificación: los paneles se muestran tras recargar.
3. El uid no existe aquí. Señal: tras la sustitución, el error imprime un uid. Grafana genera un uid automáticamente cuando el provisioning no lo define, así que staging y producción no coinciden. Solución: fija el uid en el provisioning del datasource. Verificación: /api/datasources lo lista.
4. Una variable con el nombre correcto pero el tipo equivocado. Señal: templating.list contiene DS_PROMETHEUS, pero como variable custom. Se resuelve a su propio valor, no al uid de un datasource. Solución: conviértela en type: datasource. Verificación: el guard de CI de más abajo imprime 0 para ese archivo.
Si el mensaje lleva dos puntos, Datasource: ${DS_PROMETHEUS} was not found, lo que falló fue la carga del plugin. Es otro problema.
¿Cómo se corrige para el provisioning y la API?
Sustituye un uid real. Este walk reemplaza cada objeto con placeholder, incluido el de la variable de consulta, y elimina las claves que solo sirven para importar:
jq --arg uid P1809F7CD0C75ACF3 \
'walk(if type == "object" and .uid == "${DS_PROMETHEUS}"
then .uid = $uid else . end)
| del(.__inputs, .__requires)' \
dashboard.json > dashboard.provisioned.jsonPrueba el filtro con tu propio archivo en el jq playground, que ejecuta jq de verdad.
Fija el uid para que coincida en todas partes. Define uid en el provisioning del datasource y el valor sustituido será cierto en todas las instancias:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
uid: prometheus-main
access: proxy
url: http://prometheus:9090El proveedor de dashboards no necesita nada especial, pero ten en cuenta que nunca lee __inputs:
apiVersion: 1
providers:
- name: platform
orgId: 1
folder: Platform
type: file
disableDeletion: true
updateIntervalSeconds: 30
allowUiUpdates: false
options:
path: /var/lib/grafana/dashboardsAmbos formatos están en la documentación de provisioning.
Conserva el placeholder y defínelo. Una variable de tipo datasource con exactamente el nombre del placeholder resuelve ${DS_PROMETHEUS} a un uid real. Su query es el id del plugin:
{
"templating": {
"list": [
{ "type": "datasource", "name": "DS_PROMETHEUS", "query": "prometheus" }
]
}
}Usa la API de importación. POST /api/dashboards/import es el endpoint al que envía el diálogo Import. Requiere dashboards:create y no aparece en la documentación actual de la API HTTP, así que su forma sale del código fuente. Sustituye <folder-uid> por el uid real de la carpeta de destino: el proveedor de arriba crea la carpeta por su título, así que su uid no tiene por qué ser platform.
jq -n --slurpfile dash dashboard.json \
--arg uid prometheus-main --arg folder '<folder-uid>' \
'{dashboard: $dash[0], overwrite: true, folderUid: $folder,
inputs: [{name: "DS_PROMETHEUS", type: "datasource",
pluginId: "prometheus", value: $uid}]}' \
> payload.json
curl -sS -X POST \
-H "Authorization: Bearer $GRAFANA_TOKEN" \
-H "Content-Type: application/json" \
--data @payload.json "$GRAFANA_URL/api/dashboards/import"Si omites un input, falla con HTTP 400 y el mensaje dentro de un cuerpo JSON:
{"message":"dashboard import failed: missing dashboard input variable DS_PROMETHEUS"}¿Borrar __inputs es la solución que no soluciona nada?
Sí. Borrar __inputs hace que el archivo parezca un dashboard normal, pero cada ${DS_PROMETHEUS} sigue ahí. El error pasa de “provisionado mal” a la causa 2, y ahora ni siquiera el diálogo Import puede repararlo, porque el evaluador solo sustituye los nombres declarados en __inputs.
La segunda falsa solución es cambiar el placeholder por el nombre del datasource, "Prometheus". Grafana busca una clave por uid y después por nombre, así que funciona hasta que alguien renombra el datasource u otra instancia lo llama de otra forma. Por eso el validador marca esa forma de cadena simple con la regla datasource-by-name.
Cuidado: renombrar el datasource para que coincida con el placeholder no cambia nada. El panel busca un datasource cuyo uid o nombre sea el literal
${DS_PROMETHEUS}.
¿Cómo evitas que vuelva en CI?
Haz fallar el pipeline cuando un dashboard subido al repositorio todavía tenga un placeholder que ninguna variable de datasource define:
status=0
for f in dashboards/*.json; do
n=$(jq '[.templating.list[]? | select(.type == "datasource") | .name] as $vars
| [.. | strings | select(startswith("${DS_"))
| ltrimstr("${") | rtrimstr("}")
| select(IN($vars[]) | not)] | length' "$f")
if [ "$n" -ne 0 ]; then echo "$f: $n unresolved placeholder(s)"; status=1; fi
done
exit "$status"Con el archivo exportado imprime 3 para ese dashboard. Tras la sustitución con walk, o con la variable de datasource DS_PROMETHEUS en su sitio, imprime 0.
Recorre los archivos uno a uno en lugar de pasar un glob a jq -e, que fija su código de salida solo a partir de la última salida. Ejecuta el guard en el mismo job que copia los dashboards a la ruta de provisioning, para que un archivo que falla nunca llegue a Grafana.
Combínalo con la comprobación de uids, porque un archivo limpio puede seguir nombrando un uid que falta en algún entorno. La misma disciplina se aplica a las consultas que contiene: Cómo leer una consulta de PromQL explica qué preguntan los paneles, y LogQL vs PromQL ayuda cuando hay un panel de Loki al lado.
¿Qué revisar antes de publicar un archivo de dashboard?
- Exporta con la opción de compartir desactivada cuando el archivo sea para tu propio repositorio de provisioning.
- Ejecuta el filtro
paths(...)y revisa cada ubicación que imprima. - Enumera los uids con
GET /api/datasourcesen cada instancia de destino. - Fija esos uids en el provisioning de datasources, para que todos los entornos coincidan.
- Sustituye con
walk, o añade una variable de datasource por cada placeholder. - Elimina
__inputsy__requiressolo después de la sustitución. - Usa
/api/dashboards/importconinputssi necesitas conservar el archivo compartible. - Añade el guard de CI y haz que sea obligatorio antes del despliegue.
Antes de que el próximo dashboard llegue a tu carpeta de provisioning, pégalo en el Grafana Dashboard Validator y corrige lo que marque.
¿Mantienes los dashboards exportados para compartir y los provisionados como archivos separados, o generas uno a partir del otro en CI?
Publicaciones relacionadas
-
11 min de lectura
Prometheus descartó mi target: depurando relabel_configs
Un target o una etiqueta desapareció tras el relabeling. Depura relabel_configs frente a metric_relabel_configs, el anclaje de regex y la lógica de keep/drop.
-
11 min de lectura
Actions de relabeling de Prometheus: keep, labelmap, hashmod
Referencia de las 11 actions de relabeling de Prometheus —replace, keep, drop, labelmap, labeldrop, hashmod, keepequal, lowercase— cada una con un ejemplo.
-
6 min de lectura
Cómo leer una consulta de PromQL
Una consulta de PromQL se lee de dentro hacia afuera, no de izquierda a derecha. Aprende las cuatro capas: selectores, rangos, funciones y agregaciones.