Pular para o conteúdo

Grafana Dashboard Validator · Observability

Ache as armadilhas de importação antes de publicar o dashboard.

22 regras sobre um parse real do seu JSON: a variável de template que nada declara, o placeholder ${DS_PROMETHEUS} que o provisionamento nunca vai resolver, o painel AngularJS que o Grafana 12 removeu, o painel sem type que desenha uma caixa vazia. Cada achado nomeia o caminho JSON onde está — e nada do que você cola sai da aba.

Roda no seu navegador — nada do que você cola sai desta página. Como comprovamos isso

Roda no seu navegador 22 regras grafana-12 / schemaVersion 41 Sem cadastro Atualizado em 31 de jul. de 2026

Playground do Grafana Dashboard Validator

Examples

The uid is the stable identifier you choose and keep in git; the id is a row number belonging to one Grafana database, and it should be null in any file you commit. schemaVersion records which of Grafana's own dashboard-format migrations have already been applied to the JSON.

Rules pinned to grafana-12 / schemaVersion 41 — every version-sensitive finding is phrased as a range, never as one exact release.

dashboard.json input

Results update as you type — press Enter to run now.

Press Esc to release keyboard focus from the editor; /Ctrl + Enter lints and leaves the editor. Dashboards are too large for a shareable link, so use Save snapshot instead — it stays in this browser. Nothing you paste is uploaded.

Findings

Paste a Grafana dashboard JSON above — or tap an example — to see every finding here, with the JSON path it lives at and the fix.

A lacuna

Um dashboard que importa não é um dashboard que funciona.

O Grafana é tolerante da pior forma possível. Uma variável não declarada não é um erro: a query simplesmente roda com $env dentro, então o painel fica vazio, ou casa com muito mais séries do que você queria e fica cheio e errado. Um painel sem type desenha uma caixa em branco. Um repeat sobre uma variável inexistente renderiza exatamente um painel e parece pronto. Nada na interface reclama.

E um dashboard é o único artefato que ninguém consegue revisar. Um diff JSON de quatro mil linhas em que valores de gridPos se deslocaram, ids foram renumerados e a interface reescreveu fieldConfig não é um diff que um humano lê: é um diff que um humano aprova. Os defeitos que sobrevivem à revisão são os que se parecem com qualquer outra linha do arquivo.

Peça um dashboard a um assistente e você herda a mesma classe de bug com mais confiança por cima. Dashboards gerados são fluentes: títulos de painel plausíveis, PromQL plausível, um bloco templating — e queries que referenciam variáveis que esse bloco nunca declara, uma schemaVersion tirada da memória de algum Grafana mais antigo, e tipos de painel removidos há duas versões maiores. Verificar a afirmação leva segundos. Saber o que verificar é a parte difícil, e é essa parte que esta ferramenta faz: cada regra está nesta página, e tudo o que ela se recusa a apontar também.

Revisando as queries também? PromQL Explainer desmonta uma query em português claro, e Alertmanager Route Tester prova onde os alertas que esses painéis vigiam realmente chegam.

O pipeline

Como funciona.

Quatro passos, todos dentro da sua aba, refeitos enquanto você digita.

  1. Parsear, e dizer o que isso custou.

    Primeiro JSON estrito. Depois uma marca de ordem de bytes, `// comentários`, vírgulas finais, um wrapper de API `{ dashboard: … }` ou um dashboard guardado como string escapada — cada caso recuperado e cada caso reportado, porque a API do Grafana não é assim tolerante.

  2. Achatar todos os layouts de painel.

    O `panels` de primeiro nível, os filhos que uma row recolhida guarda, os irmãos que uma row expandida possui, e o `rows[]` de antes da schemaVersion 16 — tudo em uma lista onde cada painel lembra o caminho JSON de onde veio.

  3. Indexar as variáveis.

    Cada string do documento é varrida em busca de `$var`, `${var}`, `${var:formato}` e `[[var]]`, e comparada com o que `templating.list` declara mais os built-ins do Grafana. Usadas, não usadas e não resolvidas saem do mesmo índice.

  4. Rodar 22 regras e reportar um caminho e uma correção.

    Cada regra roda no próprio try/catch, então uma que tropeça custa uma única nota em vez dos outros achados. Cada diagnóstico nomeia o caminho, o motivo e a mudança a fazer — pronto para colar em uma revisão.

Referência

Variáveis, versões de schema e as 22 regras.

O conjunto de regras está fixado em grafana-12 / schemaVersion 41, e todo achado sensível a versão é escrito como faixa em vez de um release exato. 7 regras são erros, 11 são avisos e 4 são notas.

As quatro sintaxes de variável

As quatro resolvem hoje, e duas coisas que parecem variáveis não são. O linter lê todas elas em cada string do documento: queries, títulos, formatos de legenda, links de painel, queries de anotação e as queries de outras variáveis.

Forma Desde O que saber
$env Sempre Termina no primeiro caractere que não é letra, dígito ou sublinhado, então “$env-prod” é a variável env seguida do texto literal “-prod”.
${env} Grafana 6 A forma atual. Sem ambiguidade ao lado do texto em volta, e a única que pode carregar um formato.
${env:regex} Grafana 6 Uma interpolação formatada — regex, csv, json, pipe, glob e outras — e é isso que torna segura uma variável de múltiplos valores dentro de uma query.
[[env]] Antes do Grafana 6 Obsoleta. Ainda resolve, não pode carregar formato. Reportada como legacy-var-syntax.
$__rate_interval Grafana 7.2 Um built-in, não uma das suas. Todo nome que começa com dois sublinhados é tratado como built-in e nunca é reportado.
${DS_PROMETHEUS} Não é variável de template nenhuma: é um placeholder de importação de __inputs. Reportado como unresolved-ds-input.

Marcos de schemaVersion

A coluna Grafana indica a faixa de releases em que cada migração chegou, não uma correspondência de um para um: o Grafana também incrementa schemaVersion dentro de releases menores, então trate as linhas como pontos de referência e não como tabela de consulta.

schemaVersion Grafana O que mudou
16 5.x Os painéis saíram de “rows” para um array “panels” de primeiro nível, e gridPos substituiu span. Abaixo disso, tudo sobre o layout de um dashboard é armazenado de outra forma.
36 8.3–9.x A “datasource” de um painel passou a ser uma referência { type, uid } em vez de um nome. Essa migração está por trás da maioria das importações do tipo “funciona na minha instância”.
39 11.x O schema vigente na linha Grafana 11, em que os painéis Angular passaram a vir desativados por padrão.
41 12.x O schema mais novo que este linter conhece. Qualquer valor acima de 41 é reportado como nota, nunca como erro.

O catálogo de regras

Um erro significa que o Grafana faz algo diferente do que o JSON diz; um aviso significa que ele carrega e está errado ou não é portável; uma nota vale saber. Cada achado do playground linka para a regra dele aqui.

no-uid aviso

Dê um uid ao dashboard

Sem uid, cada importação cria um dashboard NOVO em vez de atualizar o que já existe — então o mesmo arquivo importado duas vezes deixa duas cópias, e qualquer link que alguém salvou aponta para o que agora está desatualizado. O Grafana aceita até 40 caracteres entre letras, dígitos, hífens e sublinhados.

Fix "uid": "api-slo"

root-id-set aviso

id precisa ser null em um arquivo commitado

id é um número de linha dentro de um único banco de dados do Grafana. Levado para outra instância, ou a importação falha, ou ela cai em um dashboard que não tem nada a ver com o seu. O Grafana atribui o dele, sempre.

Fix "id": null

empty-title aviso

Dê um título ao dashboard

Um title ausente ou vazio aparece como “New dashboard”: impossível de achar na busca e indistinguível de qualquer outro painel que alguém criou por acidente.

Fix "title": "API SLO"

duplicate-panel-id erro

Os ids de painel precisam ser únicos

O Grafana endereça links de painel, URLs de “View panel” e repeats pelo id do painel. Dois painéis com o mesmo id quebram os três, e a interface não diz nada: o segundo painel simplesmente deixa de ser endereçável.

Fix Renumere um dos dois; ids só precisam ser únicos dentro deste dashboard

duplicate-variable erro

Os nomes de variável precisam ser únicos

Duas entradas em templating.list com o mesmo name: o Grafana mantém a última e descarta a primeira em silêncio, então o tipo, a query e o valor padrão da variável são os que a segunda declaração fixou por acaso.

Fix Renomeie ou apague uma das duas

schema-version-old aviso erro abaixo de 16

Reexporte uma schemaVersion antiga

schemaVersion registra por quais das migrações de formato do próprio Grafana o JSON já passou. Abaixo de 36, a datasource de um painel ainda é um nome; abaixo de 16, os painéis ainda vivem dentro de rows. O Grafana migra ao carregar, mas o arquivo no seu repositório não se migra sozinho — revisar esse arquivo é revisar algo que o Grafana nunca vai renderizar.

Fix Abra no Grafana 9 ou mais novo e exporte de novo

schema-version-unknown nota

Uma schemaVersion que este linter não conhece

Mais nova que a 41 fixada, ausente, ou escrita como string em vez de número. Reportada como nota e nunca como erro: um schema desconhecido é um limite deste linter, não um defeito do seu dashboard.

Fix Nada a mudar — a nota marca os achados abaixo como indicativos

undefined-variable erro aviso dentro de uma regex

Toda $variável precisa estar declarada

Uma referência que nada declara fica na query como texto literal: a consulta roda com “$env” dentro e o painel não devolve nada — ou, pior, devolve algo plausível. Os built-ins do Grafana ($__rate_interval, $__from, $__range e os demais) são conhecidos e nunca reportados, e $1 também não, porque é uma retrorreferência de regex.

Fix Adicione em templating.list, ou corrija a escrita

unused-variable nota

Uma variável que ninguém lê

Declarada, mas nunca referenciada por painel, query, título, link ou anotação. Inofensiva em si — só que uma variável do tipo “query” que ninguém lê ainda roda a consulta dela em cada carregamento do dashboard.

Fix Apague, ou use

legacy-var-syntax aviso

[[var]] é a forma anterior ao Grafana 6

Ela ainda resolve e continua obsoleta. É também a única forma que não pode carregar um formato, então um valor que precisa ser uma alternância de regex ou uma lista CSV tem de ser reescrito antes de poder ser formatado.

Fix "${env}", ou "${env:regex}" quando a query precisa de um padrão

datasource-by-name aviso

Referencie datasources por uid, não por nome

Um nome só resolve se existir na instância de destino uma datasource com exatamente aquele nome. Essa única diferença separa um dashboard que importa de um que mostra “Datasource not found” no Grafana de um colega. A forma { type, uid } é o que o Grafana escreve desde a schemaVersion 36.

Fix "datasource": { "type": "prometheus", "uid": "P1809F7CD0C75ACF3" }

unresolved-ds-input erro

${DS_…} precisa do diálogo de importação

“Export for sharing externally” troca cada datasource por um placeholder de __inputs que só Dashboards → Import preenche. Se você provisionar esse mesmo arquivo, o Grafana reporta “Datasource ${DS_PROMETHEUS} not found” — e uma referência ${DS_…} sem nenhum bloco __inputs falha do mesmo jeito, mesmo pelo diálogo.

Fix Importe pelo diálogo, ou substitua antes o { type, uid } real

empty-targets aviso

Um painel sem query

Nenhum targets, então o painel renderiza vazio. Tipos de painel que nunca consultam — row, text, dashlist, news, alertlist, annolist — não são reportados, e nem os library panels, cujas queries vivem na biblioteca e não neste arquivo.

Fix Adicione um target, ou apague o painel

deprecated-panel-type aviso

graph, singlestat e table-old

O Grafana 9–12 migra esses tipos quando o dashboard carrega, e é exatamente por isso que vale apontá-los: o que você revisa no JSON não é o que ninguém vai ver na tela. Salvar de novo a partir do Grafana 9 escreve a migração no arquivo, e então revisão e renderização finalmente combinam.

Fix "type": "timeseries" / "stat" / "table"

angular-panel erro

Plugins de painel AngularJS

O suporte a Angular foi marcado como obsoleto no Grafana 9 e removido ao longo do Grafana 11–12. Diferente dos tipos do núcleo acima, um plugin não tem migração automática: o painel não degrada, ele não renderiza nada.

Fix piechart para grafana-piechart-panel, geomap para grafana-worldmap-panel

repeat-undefined erro

Um repeat sobre nada

Um repeat que nomeia uma variável inexistente produz um único painel e nenhuma mensagem de erro. Uma row que deveria se abrir para cada cluster mostra um em silêncio, e o dashboard parece pronto.

Fix Declare a variável, ou remova “repeat”

time-range-absurd aviso

Um intervalo padrão que ninguém quer

Intervalos maiores que um ano, intervalos ao contrário, intervalos de tamanho zero e expressões que o Grafana não consegue interpretar. Todo painel executa o intervalo padrão no momento em que o dashboard abre, o que faz disso o erro de desempenho mais barato de corrigir no arquivo.

Fix "time": { "from": "now-6h", "to": "now" }

refresh-aggressive aviso

Um refresh abaixo de dez segundos

Abaixo de dez segundos as queries enfileiram mais rápido do que terminam, em cada aba com o dashboard aberto, e a datasource paga por todas. O próprio min_refresh_interval do Grafana pode sobrepor você de qualquer forma, e então o JSON diz uma coisa e a instância faz outra.

Fix "refresh": "1m"

override-suspect nota

Um override que não aplica nada

Um matcher byName sem valor não casa com nenhum campo; um override com o array properties vazio não define nada. O Grafana mantém os dois no JSON e não aplica nenhum, então eles se leem como configuração que está fazendo algo.

Fix Dê algo para casar e algo para definir, ou apague

empty-row nota

Uma row sem painéis

Uma row recolhida com o array panels vazio é invisível até alguém expandi-la e não achar nada dentro. Uma row EXPANDIDA não é reportada quando os painéis dela vêm em seguida como irmãos: é assim que o Grafana realmente armazena, e reportar isso dispararia em todo dashboard moderno.

Fix Apague a row, ou mova painéis para dentro

panel-no-type erro

Um painel sem type

Nada diz ao Grafana o que renderizar, então ele desenha uma caixa vazia onde o painel deveria estar. Um painel sem type normalmente foi editado à mão, mergeado errado, ou gerado por um script. Painéis de biblioteca são a única exceção legítima: o Grafana os salva como {id, title, gridPos, libraryPanel}, e esta regra os ignora.

Fix "type": "timeseries"

panel-zero-size aviso

Um painel que não ocupa espaço

Um gridPos com largura ou altura zero é invisível. Um gridPos ausente faz o Grafana cair em uma posição padrão, onde painéis podem acabar um sobre o outro. A grade tem 24 colunas de largura.

Fix "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }

A cerca

O que ela não aponta de propósito.

Cada um desses pontos foi considerado e descartado, e a mesma lista está em um comentário no topo do motor. Um silêncio que você pode ler vale mais que uma regra que você aprende a ignorar.

Validação completa de schema

Isto é um lint estrutural, não uma checagem de schema. O schema de dashboard do Grafana é grande, versionado e ainda em movimento; uma checagem parcial apresentada como checagem de schema seria uma mentira.

PromQL, LogQL e SQL dentro de targets

Uma linguagem de consulta merece a própria ferramenta. O PromQL Explainer faz esse trabalho direito, em vez de esta ferramenta parsear expressões pela metade.

Tudo que depende do seu Grafana

Se um uid está ocupado, se um plugin está instalado, se uma pasta existe. Aqui não há rede, então adivinhar seria inventar.

Erros de digitação em nomes $__

Tudo que começa com dois sublinhados é tratado como built-in do Grafana, porque o Grafana não para de adicioná-los. Chamar o built-in do ano que vem de “não definido” seria pior do que deixar passar um typo.

Painéis sem título

Painéis de texto e cartões de um único número são legitimamente sem título. Só o título do DASHBOARD é obrigatório.

Sobreposição de gridPos e geometria de layout

O Grafana reempacota a grade ao carregar o dashboard, então uma sobreposição no JSON não é uma sobreposição na tela.

Tamanho de painéis em rows[] legadas

Layouts anteriores à schemaVersion 16 usavam span, não gridPos. Reportar ali um gridPos ausente dispararia em cada painel de um dashboard cujo problema real já é um erro.

Um refresh que este linter não consegue interpretar

Adivinhar quanto vale “1m30s” é adivinhar, e o achado falaria do palpite e não do dashboard.

Campos obsoletos dentro das opções de painel

Eles variam por plugin, mudam a cada release e o Grafana os migra ao carregar. Uma regra sobre eles ficaria defasada em uma versão menor.

Limites, ditos em vez de escondidos: o linter lê até 5.000.000 caracteres, guarda no máximo 50 achados por regra e 400 no total, e informa o número real sempre que um teto se aplica.

Próximo passo

Cole o relatório na revisão.

“Copy report” entrega a execução inteira em texto puro: uma linha por achado, com o caminho JSON, o id da regra e a correção, e a versão fixada das regras no topo para que ninguém precise adivinhar o que verificou. Depois siga: leia as queries que esses painéis executam e prove que os alertas que eles vigiam chegam a alguém.

report.txt
Grafana Dashboard Validator — 7 errors, 12 warnings, 3 notes
  — variables: 2 defined, 2 unresolved, schemaVersion 27
Rules: grafana-12 / schemaVersion 41

ERRORS (7)
  error duplicate-panel-id (panels[1].id): Panel id 1 is
    already used by "Requests" (panels[0]).
  error undefined-variable (panels[2].targets[0].expr):
    "$cluster" is used here, but no template variable named
    "cluster" is defined and it is not a Grafana built-in.

FAQ

Suas perguntas, respondidas.

Toque em uma pergunta para expandir a resposta.

22 regras sobre um parse real do dashboard: 7 erros, 11 avisos e 4 notas. Os erros são os que mudam o que o Grafana faz — uma variável de template que nada declara, um placeholder ${DS_…} sem bloco __inputs, um painel AngularJS que não renderiza nada no Grafana 11–12, dois painéis com o mesmo id, um repeat sobre uma variável inexistente, um painel sem type, e uma schemaVersion tão antiga que o JSON e a renderização são dois dashboards diferentes. Os avisos são os problemas de portabilidade e de revisão: um uid ausente, um id de banco deixado em um arquivo commitado, uma datasource referenciada por nome, um painel graph ou singlestat obsoleto, um painel invisível de largura zero, um intervalo de tempo padrão de mais de um ano, um refresh abaixo de dez segundos. Cada regra tem a própria subseção nesta página, e os chips de regra do playground linkam direto para ela.

Não. O parser e todas as regras são JavaScript rodando na sua aba — não há servidor, chamada de API nem log, então 0 bytes são enviados. Isso importa mais aqui do que na maioria das ferramentas: o JSON de um dashboard é um mapa do seu parque interno. Nomes de métricas, uids de datasources, hostnames em formatos de legenda, nomes de serviço em queries de variável, às vezes uma URL interna em um link de painel. É exatamente o arquivo que você não deveria colar em um formatador online qualquer.

Não, e a ferramenta diz isso em vez de insinuar o contrário. O schema de dashboard do Grafana é grande, versionado e continua em movimento, então uma checagem parcial apresentada como checagem de schema seria pior do que nenhuma. O que ela faz é um lint estrutural: lê as formas que quebram importações e revisões — variáveis, referências de datasource, tipos de painel, ids, layout, tempo e refresh — e nomeia o caminho JSON de cada achado. Não parseia PromQL, não conhece seus plugins e não fala com nenhum Grafana. A lista completa do que ela cala de propósito está acima, em “O que ela não aponta de propósito”.

Porque o arquivo veio de “Export for sharing externally”. Essa exportação troca cada referência de datasource por um placeholder — ${DS_PROMETHEUS} — e adiciona um bloco “__inputs” descrevendo o que cada placeholder precisa. Só o diálogo Dashboards → Import lê esse bloco e pede que você escolha uma datasource real. Provisionar o mesmo arquivo, dar POST na API ou deixá-lo em uma pasta sincronizada por Git pula o diálogo por completo: o placeholder sobrevive no dashboard salvo e todos os painéis falham em resolvê-lo. Ou você importa pelo diálogo, ou substitui o { type, uid } real antes de provisionar. Este linter reporta o placeholder como erro nos dois casos, porque o arquivo não é provisionável como está.

O uid é o identificador que você escolhe. Ele faz parte da URL do dashboard, é o que provisionamento e API endereçam, e deve viver no controle de versão junto do JSON. O id é um número de linha no banco de uma instância específica do Grafana — fora dela não significa nada. Um arquivo com uid atualiza o mesmo dashboard a cada importação; um sem uid cria uma cópia nova toda vez. Um arquivo com id desatualizado falha na importação ou acerta um dashboard que não tem nada a ver com o seu, e é por isso que uma exportação deveria trazer “id”: null.

Os painéis de núcleo graph, singlestat e table-old são migrados automaticamente quando o dashboard carrega, então continuam funcionando — mas o JSON no seu repositório não é migrado, o que significa que o arquivo que você revisa e o painel que o Grafana renderiza são duas coisas diferentes. Salve o dashboard de novo a partir do Grafana 9 ou mais novo para escrever a migração no arquivo. PLUGINS de painel AngularJS são outra história: grafana-piechart-panel, grafana-worldmap-panel e os demais não têm migração automática, e o suporte a Angular foi marcado como obsoleto no Grafana 9 e removido ao longo do Grafana 11–12, então esses painéis não renderizam nada e precisam ser trocados à mão.

O conjunto de regras está fixado em grafana-12 / schemaVersion 41, impresso nesta página e em todo relatório copiado, para que um relatório nunca seja ambíguo sobre o que o verificou. As regras sensíveis a versão são escritas como faixas — “Grafana 9–12”, “removido ao longo do Grafana 11–12” — em vez de um release exato, porque a sua instância está perto deste ponto e não exatamente nele. Se a schemaVersion do seu dashboard for maior que 41, você recebe uma NOTA dizendo isso e os achados específicos de schema ficam marcados como indicativos. Um schema desconhecido é um limite deste linter, não um defeito do seu dashboard, então ele nunca é reportado como erro.

Porque o Grafana não falha: ele não interpola nada e roda a query com o texto literal dentro. Um seletor PromQL {env="$env"} não casa com nenhuma série, então o painel fica vazio; um seletor com casamento por regex pode casar com MUITO mais séries do que você queria, então o painel fica cheio e errado. Nenhum dos dois casos produz mensagem em lugar algum da interface. A única exceção que este linter abre é uma referência dentro de uma string que parece uma expressão regular, onde “$” também é uma âncora de fim de linha: ali o achado cai para aviso, porque aquele “$env” que você escreveu pode realmente ter sido uma âncora seguida de texto.

Até 5.000.000 caracteres, o que cobre um dashboard gerado com vários milhares de painéis; um de 500 painéis é analisado em poucos milissegundos. Acima desse limite ele recusa com uma mensagem em vez de congelar sua aba, porque uma entrada desse tamanho é um log ou um arquivo compactado, não um dashboard. Os achados também têm teto: 50 por regra e 400 no total, e o painel informa o teto e o número real sempre que um deles se aplica — uma lista truncada que não dissesse que está truncada seria exatamente o tipo de erro silencioso que esta ferramenta existe para pegar.

More free, private DevOps tools.

O Grafana Dashboard Validator é uma das ferramentas do OpsCanopy — uma copa crescente de validadores, conversores e testadores que rodam no navegador e nunca tocam um servidor.

39 free tools, every one offline-capable — opscanopy.com works with no signup and nothing uploaded.

Relacionados: PromQL Explainer para as queries dentro dos painéis, Alertmanager Route Tester para onde os alertas ao lado chegam, o Prometheus Relabel Tester para as labels pelas quais suas variáveis filtram, e o Conversor JSON ↔ YAML quando o arquivo de provisionamento em volta do dashboard precisa ser remodelado — ou navegue pelo diretório completo de ferramentas.

Sem afiliação com a Grafana Labs, sem endosso e sem patrocínio dela. “Grafana” é marca da Raintank, Inc. dba Grafana Labs, usada aqui apenas para descrever o que esta ferramenta lê. Fornecido como está; confirme sempre um dashboard no Grafana que vai renderizá-lo. OpsCanopy é gratuito e aberto.