How to Safely Backup and Restore Grafana Dashboards in Kubernetes with Scripts and Docker
This guide walks through recovering lost Grafana data in a Kubernetes cluster by using low‑level shell scripts, the grafana‑backup‑tool, Docker images, GitHub Actions, and Podman deployments, covering export/import commands, component selection, and handling provisioned dashboards.
Preface
Our k8s cluster uses Grafana with Ceph storage; deleting and recreating the Grafana Deployment caused all data loss because the new PV points to a fresh location.
1. Low‑level solution
We searched for Grafana backup scripts that call the Grafana API via shell; the gist at https://gist.github.com/crisidev/bd52bdcc7f029be2f295 contains several useful scripts.
Export script
#!/bin/bash
# Usage: export_grafana_dashboards.sh https://admin:[email protected]
create_slug () {
echo "$1" | iconv -t ascii//TRANSLIT | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+|-+$//g | tr A-Z a-z
}
full_url=$1
username=$(echo "$full_url" | cut -d/ -f3 | cut -d: -f1)
base_url=$(echo "$full_url" | cut -d@ -f2)
folder=$(create_slug "$username-$base_url")
mkdir "$folder"
for db_uid in $(curl -s "$full_url/api/search" | jq -r .[].uid); do
db_json=$(curl -s "$full_url/api/dashboards/uid/$db_uid")
db_slug=$(echo "$db_json" | jq -r .meta.slug)
db_title=$(echo "$db_json" | jq -r .dashboard.title)
filename="$folder/${db_slug}.json"
echo "Exporting \"$db_title\" to \"$filename\"..."
echo "$db_json" | jq -r . > "$filename"
done
echo "Done"This script exports all dashboards as JSON files but places them under the General folder, which is not ideal.
Import script
Use grafana-dashboard-importer.sh with a running Grafana instance and an admin API key.
# Usage: ./grafana-dashboard-importer.sh -t http://<grafana_ip>:<grafana_port> -k <api_key> -p <backup_folder>The -p flag points to the directory containing the exported JSON files.
The low‑level approach cannot back up datasources, users, or folders.
2. Advanced solution
The grafana-backup-tool supports backing up folders, dashboards, datasources, alert channels, organizations and users.
Dockerfile
FROM alpine:latest
LABEL maintainer="grafana-backup-tool Docker Maintainers https://fuckcloudnative.io"
ENV ARCHIVE_FILE ""
RUN echo "@edge http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \
apk --no-cache add python3 py3-pip py3-cffi py3-cryptography ca-certificates bash git && \
git clone https://github.com/ysde/grafana-backup-tool /opt/grafana-backup-tool && \
cd /opt/grafana-backup-tool && \
pip3 --no-cache-dir install . && \
chown -R 1337:1337 /opt/grafana-backup-tool
WORKDIR /opt/grafana-backup-tool
USER 1337A GitHub Actions workflow builds multi‑arch images and pushes them to Docker Hub and GHCR.
# GitHub Actions workflow (simplified)
name: Build and push grafana-backup-tool Docker image
on:
push:
branches: [master]
paths:
- 'grafana-backup-tool/Dockerfile'
- '.github/workflows/grafana-backup-tool.yml'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v2
with:
file: 'grafana-backup-tool/Dockerfile'
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x
context: grafana-backup-tool
push: true
tags: |
yangchuansheng/grafana-backup-tool:latest
ghcr.io/yangchuansheng/grafana-backup-tool:latestAfter building the image, you can run the backup tool in a container, a Kubernetes Deployment, or a Podman pod.
Podman deployment example
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana-backup
labels:
app: grafana-backup
spec:
replicas: 1
selector:
matchLabels:
app: grafana-backup
template:
metadata:
labels:
app: grafana-backup
spec:
containers:
- name: grafana-backup
image: yangchuansheng/grafana-backup-tool:latest
command: ["/bin/bash"]
tty: true
stdin: true
env:
- name: GRAFANA_TOKEN
value: "<redacted>"
- name: GRAFANA_URL
value: "http://<grafana_ip>:<grafana_port>"
- name: GRAFANA_ADMIN_ACCOUNT
value: "admin"
- name: GRAFANA_ADMIN_PASSWORD
value: "admin"
- name: VERIFY_SSL
value: "False"
volumeMounts:
- name: data
mountPath: /opt/grafana-backup-tool
volumes:
- name: data
hostPath:
path: /mnt/manifest/grafana/backupRun the pod with podman play kube grafana-backup-deployment.yaml, then execute grafana-backup save inside the container. The tool backs up all components by default; you can limit components with --components=folders,dashboards and restore with grafana-backup restore.
Handling Provisioned Dashboards
Grafana dashboards provisioned by the Prometheus Operator cannot be deleted via the UI, and grafana-backup-tool will fail if they already exist. Work‑arounds are to remove the provisioning configuration from the Grafana Deployment or to deploy Grafana without provisioning.
For a complete list of supported components and usage examples, refer to the tool's README.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
