docs/sources/as-code/infrastructure-as-code/grafana-operator/manage-dashboards-argocd.md
This guide shows you how to set up a continuous deployment pipeline using ArgoCD to synchronize your Grafana dashboards with a Git repository. You'll use the Grafana Dashboard Custom Resource provided by the Grafana Operator to manage dashboard configurations declaratively.
Before you begin, make sure you have the following:
Create a directory structure in your repository to organize your Grafana and dashboard configurations. For this tutorial, create a folder named grafana.
The Grafana Operator allows you to authenticate with the Grafana instance using the Grafana Custom Resource (CR).
Store the Grafana API Token in a secret. Create a file named grafana-token.yml in the grafana folder in your Git repository:
apiVersion: v1
kind: Secret
metadata:
name: grafana-cloud-credentials
namespace: '<GRAFANA_OPERATOR_NAMESPACE>'
stringData:
GRAFANA_CLOUD_INSTANCE_TOKEN: '<GRAFANA_API_KEY>'
type: Opaque
Replace the placeholders with your values:
<GRAFANA_API_KEY>: API key from your Grafana instance. To create an API key, refer to Grafana API Key Documentation<GRAFANA_OPERATOR_NAMESPACE>: Namespace where the grafana-operator is deployed in your Kubernetes clusterSet up the connection to your Grafana Cloud instance. Create a file named grafana-cloud.yml in the grafana folder in your Git repository:
apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
name: '<GRAFANA_CLOUD_STACK_NAME>'
namespace: '<GRAFANA_OPERATOR_NAMESPACE>'
labels:
dashboards: '<GRAFANA_CLOUD_STACK_NAME>'
spec:
external:
url: https://<GRAFANA_CLOUD_STACK_NAME>.grafana.net/
apiKey:
name: grafana-cloud-credentials
key: GRAFANA_CLOUD_INSTANCE_TOKEN
Replace the placeholders with your values:
<GRAFANA_CLOUD_STACK_NAME>: Name of your Grafana Cloud Stack<GRAFANA_OPERATOR_NAMESPACE>: Namespace where the grafana-operator is deployed in your Kubernetes clusterIn your grafana directory, create a sub-folder called dashboards.
This guide shows you how to create three separate dashboards. For all dashboard configurations, replace the placeholders with your values:
<GRAFANA_CLOUD_STACK_NAME>: Name of your Grafana Cloud Stack<GRAFANA_OPERATOR_NAMESPACE>: Namespace where the grafana-operator is deployed in your Kubernetes clusterUnder the dashboards folder, create a file named simple-dashboard.yaml:
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafanadashboard-sample
namespace: '<GRAFANA_OPERATOR_NAMESPACE>'
spec:
resyncPeriod: 30s
instanceSelector:
matchLabels:
dashboards: '<GRAFANA_CLOUD_STACK_NAME>'
json: >
{
"id": null,
"title": "Simple Dashboard",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"graphTooltip": 1,
"panels": [],
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [],
"refresh_intervals": []
},
"templating": {
"list": []
},
"annotations": {
"list": []
},
"refresh": "5s",
"schemaVersion": 17,
"version": 0,
"links": []
}
Under the dashboards folder, create a file named dashboard-from-cm.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: dashboard-definition
namespace: <GRAFANA_OPERATOR_NAMESPACE>
data:
json: >
{
"id": null,
"title": "Simple Dashboard from ConfigMap",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"graphTooltip": 1,
"panels": [],
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [],
"refresh_intervals": []
},
"templating": {
"list": []
},
"annotations": {
"list": []
},
"refresh": "5s",
"schemaVersion": 17,
"version": 0,
"links": []
}
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafanadashboard-from-configmap
namespace: '<GRAFANA_OPERATOR_NAMESPACE>'
spec:
instanceSelector:
matchLabels:
dashboards: '<GRAFANA_CLOUD_STACK_NAME>'
configMapRef:
name: dashboard-definition
key: json
Under the dashboards folder, create a file named dashboard-from-id.yaml:
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: node-exporter-latest
namespace: '<GRAFANA_OPERATOR_NAMESPACE>'
spec:
instanceSelector:
matchLabels:
dashboards: '<GRAFANA_CLOUD_STACK_NAME>'
grafanaCom:
id: 1860
After you commit all changes to Git, log in to the ArgoCD user interface or use the CLI.
Using the UI:
grafana folderUsing the CLI:
Prepare an application manifest named argo-application.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: Grafana
namespace: '<ARGOCD_NAMESPACE>'
spec:
destination:
name: ''
namespace: ''
server: 'https://kubernetes.default.svc'
source:
path: '<PATH_TO_GRAFANA_FOLDER>'
repoURL: '<GIT_REPO_URL>'
targetRevision: HEAD
directory:
recurse: true
sources: []
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 2
backoff:
duration: 5s
maxDuration: 3m0s
factor: 2
Replace the placeholders with your values:
<GIT_REPO_URL>: URL of your Git repository<PATH_TO_GRAFANA_FOLDER>: Path to the grafana folder in your repository<ARGOCD_NAMESPACE>: Namespace where ArgoCD is deployed in your Kubernetes clusterCreate the application in ArgoCD:
kubectl apply -f argo-application.yaml
To update an existing dashboard:
Log in to your Grafana dashboard and confirm that the changes are applied. You should see the dashboard updates reflected in the Grafana UI.
You've successfully set up a GitOps workflow to manage Grafana dashboards using ArgoCD and the Grafana Operator. Your dashboards are now version-controlled and can be consistently deployed across environments. This approach provides a reliable and auditable way to manage observability dashboards and scale your operations.
To learn more about managing Grafana using Grafana Operator: