Skip to content

Migrate Grafana from SQLite to an External Database (PostgreSQL / MySQL)

This guide walks you through migrating Grafana from its default embedded SQLite database to an external PostgreSQL or MySQL database. Custom folders, dashboards, alert rule groups, and contact points that exist only in the old SQLite database will be lost if you do not export them before switching and import them afterward. Follow every step in order.

Note

Privacera-managed dashboards, alerts, and folders are excluded from export and are automatically restored by the Post Install Job.


Migration Workflow

  1. (Optional) Back up Internal SQLite Grafana database.
  2. Enter the Privacera Manager container (Python is pre-installed).
  3. Export custom assets with grafana_export.py — run the export script before the DB switch.
  4. Exit the container (the bundle is saved under output/grafana-migration/ on the jumphost).
  5. Switch Grafana to an external database.
  6. Re-enter the container and import custom assets with grafana_import.py.

Files You Need

File Purpose When to run
grafana_export.py Exports custom assets to a local bundle Before the DB switch
grafana_import.py Restores custom assets from the bundle After the DB switch

(Optional) Back Up Internal SQLite Grafana Database

For a safer recovery path, copy the SQLite database file from the Grafana pod before exporting or switching databases.

Step 1: Enter the PM Container

Bash
cd /privacera/privacera-manager
./privacera-manager.sh shell

Step 2: Copy grafana.db to the Backup Path

Run inside the container:

Bash
1
2
3
4
5
6
NS=<K8s_NAMESPACE>

POD=$(kubectl get pods -n $NS -l app.kubernetes.io/name=grafana -o jsonpath='{.items[0].metadata.name}')

kubectl cp -n $NS "$POD:/var/lib/grafana/grafana.db" \
  output/grafana.db.backup

Step 3: Verify the Backup

Bash
ls -lh output/grafana.db.backup

The backup is saved at /privacera/privacera-manager/output/grafana.db.backup on the jumphost.


Export Custom Assets (Before DB Switch)

The export script reads custom Grafana folders, dashboards, alert rule groups, and contact points over the HTTP API and writes them to a bundle directory. Privacera-managed assets are excluded automatically.

Step 1: Enter the PM Container

Skip this step if you are already inside the container from the optional backup step.

Bash
cd /privacera/privacera-manager
./privacera-manager.sh shell

Step 2: Download the Export Script

Download the export script inside the container:

Bash
curl -O https://trust3ai-artifacts.s3.us-east-1.amazonaws.com/grafana-migration-scripts/grafana_export.py

Step 3: Verify the Download

Bash
ls -lh grafana_export.py

Step 4: Run the Export Script

Run the export script (still inside the container). There are two ways to authenticate with Grafana. Option 1 (username and password) is the default for most environments.

Bundle path

Use --output output/grafana-migration. From inside the container, run export and import from /privacera/privacera-manager so the bundle is written under output/grafana-migration/.

Bash
1
2
3
4
5
python3 grafana_export.py \
  --grafana-url https://grafana-<namespace>.<domain> \
  --userid <grafana-admin-user> \
  --passwd <grafana-admin-password> \
  --output output/grafana-migration

Example:

Bash
1
2
3
4
5
python3 grafana_export.py \
  --grafana-url https://grafana-dev-test.dev.privacera.us \
  --userid admin \
  --passwd dev123 \
  --output output/grafana-migration

Create an Admin service account token:

  1. Log in to Grafana as an administrator.
  2. Go to Administration > Users and access > Service accounts.
  3. Select Add service account, assign the Admin role, and save.
  4. Open the service account and select Add service account token.
  5. Copy the token immediately (starts with glsa_...). It is shown only once.
Bash
1
2
3
4
python3 grafana_export.py \
  --grafana-url https://grafana-<namespace>.<domain> \
  --api-token glsa_xxxxxxxxxxxxxxxx \
  --output output/grafana-migration

Example:

Bash
1
2
3
4
python3 grafana_export.py \
  --grafana-url https://grafana-dev-test.dev.privacera.us \
  --api-token glsa_xxxxxxxxxxxxxxxx \
  --output output/grafana-migration

Optional flags:

  • --verbose — print each exported item
  • --dry-run — preview without writing files

Review the Export Bundle

After export completes, custom data is stored as JSON under output/grafana-migration/.

Example summary:

Text Only
1
2
3
4
5
[SUMMARY] Export complete
  folders:              2
  dashboards:           3
  alert_groups:         2
  contact_points:       1

Bundle Layout

Text Only
1
2
3
4
5
6
output/grafana-migration/
├── manifest.json          # export timestamp, source URL, counts
├── folders.json           # custom folders only
├── contact-points.json    # custom contact points
├── dashboards/            # custom dashboards
└── alert-rules/           # custom alert rule groups

Step 1: Confirm Counts in manifest.json

Bash
cat output/grafana-migration/manifest.json

Step 2: Exit the Container

Bash
exit

The bundle is now available at /privacera/privacera-manager/output/grafana-migration/ on the jumphost.


Switch Grafana to the External Database

Follow all steps in the Grafana High Availability (HA) guide to configure Grafana with the external database and apply the configuration.

Once Grafana has restarted, verify it is healthy and connected to the new database before proceeding:

Bash
curl -sk https://grafana-<namespace>.<domain>/api/health

Expected response includes "database":"ok". Do not proceed to the import step until you see this.


Import Custom Assets

The import script restores custom assets from the bundle. Import order: folderscontact pointsdashboardsalert rules.

Step 1: Re-enter the PM Container

Bash
cd /privacera/privacera-manager
./privacera-manager.sh shell

Step 2: Download the Import Script

Download the import script inside the container:

Bash
curl -O https://trust3ai-artifacts.s3.us-east-1.amazonaws.com/grafana-migration-scripts/grafana_import.py

Step 3: Verify the Download

Bash
ls -lh grafana_import.py

Step 4: Run the Import Script

Run the import script (still inside the container). There are two ways to authenticate with Grafana. Option 1 (username and password) is the default for most environments.

Bash
1
2
3
4
5
python3 grafana_import.py \
  --grafana-url https://grafana-<namespace>.<domain> \
  --userid <grafana-admin-user> \
  --passwd <grafana-admin-password> \
  --input output/grafana-migration

Example:

Bash
1
2
3
4
5
python3 grafana_import.py \
  --grafana-url https://grafana-dev-test.dev.privacera.us \
  --userid admin \
  --passwd dev123 \
  --input output/grafana-migration

Create a new Admin service account token after the DB switch (old tokens are invalidated):

  1. Log in to Grafana as an administrator.
  2. Go to Administration > Users and access > Service accounts.
  3. Select Add service account, assign the Admin role, and save.
  4. Open the service account and select Add service account token.
  5. Copy the token immediately (starts with glsa_...). It is shown only once.
Bash
1
2
3
4
python3 grafana_import.py \
  --grafana-url https://grafana-<namespace>.<domain> \
  --api-token glsa_xxxxxxxxxxxxxxxx \
  --input output/grafana-migration

Example:

Bash
1
2
3
4
python3 grafana_import.py \
  --grafana-url https://grafana-dev-test.dev.privacera.us \
  --api-token glsa_xxxxxxxxxxxxxxxxxxxxxxxxxxxx \
  --input output/grafana-migration

Optional flags:

  • --verbose — print each imported item

Review the Import

After the import script completes, review the output in the terminal. A successful run reports errors: 0 in the summary.

Example output:

Text Only
1
2
3
4
5
6
7
[SUMMARY] Import complete
  folders_created:         3
  folders_reused:          0
  contact_points_imported: 1
  dashboards_imported:     4
  alert_groups_imported:   2
  errors:                  0

To list each imported item as it is processed, add --verbose to the import command:

Bash
1
2
3
4
5
6
python3 grafana_import.py \
  --grafana-url https://grafana-<namespace>.<domain> \
  --userid <grafana-admin-user> \
  --passwd <grafana-admin-password> \
  --input output/grafana-migration \
  --verbose

Step 1: Confirm the Imported Assets

In your external PostgreSQL or MySQL database, confirm that Grafana tables contain the imported records.

Step 2: Exit the Container

Bash
exit

Troubleshooting

Issue Solution
ModuleNotFoundError: No module named 'requests' Python requests library is pre-installed in the PM container. If you see this error, you are running the script outside the container. Enter the container with ./privacera-manager.sh shell and try again.
401 Unauthorized API token or password is invalid or expired. Create a new Admin service account token (see authentication steps above).
403 Forbidden Service account lacks Admin role. Create a new token with Admin role.
SyntaxError when running script Ensure you are using Python 3. The PM container has python3 pre-installed.
Dashboard/Alert already exists The import script uses overwrite: true for dashboards and upsert semantics for alerts and contact points. Existing assets are updated, not duplicated.
Bundle not found after exiting container The bundle must be written under output/grafana-migration/ inside /privacera/privacera-manager. If you used a different path, it may not persist on the jumphost. Re-run export with --output output/grafana-migration.

Summary

This guide walked you through:

  1. (Optional) Backing up Internal SQLite Grafana database before switching databases.
  2. Exporting custom Grafana assets (folders, dashboards, alerts, contact points) before switching to an external database.
  3. Switching Grafana from the internal SQLite database to an external database.
  4. Importing the custom assets after the database switch.

Privacera-managed assets are automatically excluded from export and restored by the Post Install Job. The migration scripts run entirely inside the Privacera Manager container, where Python and all dependencies are pre-installed.