Scout Dashboards as Code with gcx
gcx is Grafana's command-line tool for
managing Grafana resources. Scout exposes a standard Grafana API, so you can
define dashboards and folders in files, keep them in Git, and apply them from a
pipeline.
This guide covers dashboards and folders. Alert rules are not covered: the
alerting resource API that gcx resources needs is not served by the Grafana
build Scout runs, so alert rules cannot be managed as code against Scout today.
Manage them in the Scout UI instead.
Requirements
- Grafana 12.0.0 or later.
gcxrefuses to run against earlier versions. Scout meets this requirement. - A Grafana service account token with the Editor or Admin role. Create one in Scout under Administration > Service accounts.
- Your Scout URL, provided during service setup.
Installing gcx
On macOS:
brew install --formula gcx
On Linux, download a release binary. Pin the version rather than tracking latest, so pipelines stay reproducible:
GCX_VERSION=1.1.0
curl -sL https://github.com/grafana/gcx/releases/download/v${GCX_VERSION}/\
gcx_${GCX_VERSION}_linux_amd64.tar.gz | tar xz gcx
sudo mv gcx /usr/local/bin/
Confirm the install:
gcx --version
Connecting gcx to Scout
gcx uses a configuration model similar to kubectl. A stack holds the
server address and credentials. A context points at a stack and is the
thing commands run against. Configuration is stored in
~/.config/gcx/config.yaml.
Create a stack and a context for your Scout instance:
gcx config set stacks.scout.grafana.server <your_base14_grafana_url>
gcx config set stacks.scout.grafana.token <your_base14_grafana_api_token>
gcx config set stacks.scout.grafana.org-id 1
gcx config set contexts.scout.stack scout
gcx config use-context scout
org-id is required for self-hosted Grafana and identifies the organization
the API calls apply to. The default organization is 1. To find another, open
Administration > Organizations in Scout and read the numeric ID from the
URL.
Verify the connection:
gcx config check
A working context reports valid configuration, online connectivity, and the Grafana version:
Context: scout
==============
✔ Configuration: valid
🛈 Auth method: token (inferred)
🛈 Context type: On-prem
✔ Connectivity: online
✔ Grafana version: 12.4.1
Defining folders and dashboards
gcx reads resource definitions from a directory, ./resources by default.
Each file uses the Grafana resource API format: an apiVersion, a kind,
metadata, and a spec.
Folders organize dashboards:
apiVersion: folder.grafana.app/v1beta1
kind: Folder
metadata:
name: sample
spec:
title: Special Sample Folder
metadata.name is the folder UID. spec.title is the display name.
Dashboards reference their folder through an annotation:
apiVersion: dashboard.grafana.app/v1beta1
kind: Dashboard
metadata:
name: prod-overview
annotations:
grafana.app/folder: sample
spec:
title: Production Overview
tags:
- templated
timezone: browser
schemaVersion: 41
metadata.name is the dashboard UID. Everything under spec is the dashboard
JSON model, so panels, templating, and annotations go there.
To see the resource types your Scout instance serves:
gcx resources list-types
Pushing, validating, and pulling
Validate before applying
validate checks local files against the schemas your instance serves, without
writing anything:
gcx resources validate -p ./resources
✔ No errors found.
--dry-run simulates the write itself:
gcx resources push -p ./resources --dry-run
A dashboard whose folder does not exist yet fails dry-run, because the folder is not created during a simulated push. This is expected on a first run.
Apply
Push everything in the directory:
gcx resources push -p ./resources
✔ 2 resources pushed, 0 errors
Push a single kind, or named resources within a kind:
gcx resources push dashboards -p ./resources
gcx resources push dashboards/prod-overview -p ./resources
Export existing resources
pull writes what is in Scout to disk, which is how you bring dashboards built
in the UI under version control.
Always name the resource kinds you want:
gcx resources pull dashboards folders -p ./resources
✔ 2 resources pulled, 0 errors
A bare gcx resources pull attempts every resource type gcx knows about,
including Grafana Cloud services that Scout does not run. It reports dozens of
errors that are not problems with your setup. Naming the kinds avoids this.
Pulled files are written into one directory per kind, for example
resources/dashboards.v1beta1.dashboard.grafana.app/prod-overview.yaml.
Inspect
List what is currently in Scout:
gcx resources get dashboards
KIND GROUP NAME
Dashboard dashboard.grafana.app prod-overview
Automating with CI/CD
In a pipeline, skip the config file and pass connection details as environment
variables. They apply at runtime and need no gcx config commands:
| Variable | Description |
|---|---|
GRAFANA_SERVER | Scout URL. |
GRAFANA_TOKEN | Service account token. |
GRAFANA_ORG_ID | Organization ID, 1 by default. |
Sample Makefile
export GRAFANA_SERVER := <your_base14_grafana_url>
export GRAFANA_TOKEN := <your_grafana_api_token>
export GRAFANA_ORG_ID := 1
.PHONY: validate
validate:
gcx resources validate -p ./resources
.PHONY: apply
apply:
gcx resources push -p ./resources
.PHONY: pull
pull:
gcx resources pull dashboards folders -p ./resources
Sample GitHub Actions workflow
name: Apply Grafana Configuration
on:
push:
branches: [main]
paths:
- 'resources/**.yaml'
jobs:
apply:
runs-on: ubuntu-latest
env:
GRAFANA_SERVER: ${{ secrets.GRAFANA_SERVER }}
GRAFANA_TOKEN: ${{ secrets.GRAFANA_TOKEN }}
GRAFANA_ORG_ID: '1'
steps:
- uses: actions/checkout@v4
- name: Install gcx
run: |
GCX_VERSION=1.1.0
curl -sL https://github.com/grafana/gcx/releases/download/\
v${GCX_VERSION}/gcx_${GCX_VERSION}_linux_amd64.tar.gz | tar xz gcx
sudo mv gcx /usr/local/bin/
- name: Validate resources
run: gcx resources validate -p ./resources
- name: Push resources
run: gcx resources push -p ./resources
Store GRAFANA_SERVER and GRAFANA_TOKEN as repository secrets. Run the same
validate step on pull requests to catch broken definitions before merge.
A note on Jsonnet
The Jsonnet workflow used with Grizzly does not carry over. gcx has no Jsonnet
integration. Its programmatic path is Go, using the
Grafana Foundation SDK
through gcx dev scaffold and gcx dev import, which generate and compile
builder code rather than templating YAML.
If you generate dashboard JSON with Jsonnet today, you can keep doing so and
wrap the output in the spec of a Dashboard resource before pushing it.
Related Guides
- GitOps for Dashboards and Alerts - The earlier Grizzly-based guide, kept for reference
- User Management - Roles and service accounts
- Create Dashboards - Build dashboards in the Scout UI