PostgreSQL

Migrating Grafana from SQLite to PostgreSQL with Docker Compose

PostgreSQL

By default, Grafana ships with a lightweight SQLite database to store dashboards, users, and configuration. While SQLite works fine for small setups, it quickly becomes a bottleneck in production environments where reliability, performance, and scaling matter. PostgreSQL is a natural upgrade: it’s robust, battle-tested, and integrates well with Grafana.

In this post, I’ll walk you through migrating Grafana’s default SQLite backend to PostgreSQL using Docker Compose and pgloader.


Why Migrate to PostgreSQL?

  • Scalability: PostgreSQL handles larger datasets and higher query loads better than SQLite.
  • Reliability: Supports advanced features like replication, backups, and tuning.
  • Ecosystem Integration: Unlike SQLite, which is just a local file, PostgreSQL runs as a proper database server.

Migration Steps

We’ll assume you’re already running Grafana with Docker Compose, using the default SQLite backend.

1. Stop the Grafana container

Before doing anything else, stop the Grafana service to prevent new writes during migration:

Bash
docker compose stop grafana

2. Backup the SQLite database

The SQLite database file is usually named grafana.db and lives inside Grafana’s storage directory (/var/lib/grafana/ by default). Copy it somewhere safe:

Bash
cp ./grafana/data/grafana.db ./grafana/data/grafana.db.bak

3. Add PostgreSQL to Docker Compose

Extend your docker-compose.yml to include a PostgreSQL service. Example:

YAML
services:
  postgres:
    image: postgres:15
    restart: unless-stopped
    environment:
      POSTGRES_USER: grafana
      POSTGRES_PASSWORD: grafana
      POSTGRES_DB: grafana
    volumes:
      - ./postgres-data:/var/lib/postgresql/data
    expose:
      - "5432"

4. Configure Grafana to use PostgreSQL

Update your Grafana service configuration so it connects to PostgreSQL. You can either adjust environment variables in docker-compose.yml or mount a grafana.ini file. For environment variables:

YAML
services:
  grafana:
    image: grafana/grafana-oss
    environment:
      - GF_DATABASE_TYPE=postgres
      - GF_DATABASE_HOST=postgres:5432
      - GF_DATABASE_NAME=grafana
      - GF_DATABASE_USER=grafana
      - GF_DATABASE_PASSWORD=grafana
    volumes:
      - "./data:/var/lib/grafana"
    depends_on:
      - postgres

5. Start PostgreSQL and initialize schema

Bring up the PostgreSQL container:

Bash
docker compose up -d postgres

Now start Grafana once so it automatically creates the schema in PostgreSQL:

Bash
docker compose up -d grafana

When Grafana has finished booting, stop it again:

Bash
docker compose stop grafana

6. Add pgloader to Docker Compose

pgloader makes it easy to migrate from SQLite to PostgreSQL. Add it to your docker-compose.yml:

Bash
  pgloader:
    image: ghcr.io/dimitri/pgloader:latest
    container_name: pgloader
    command: pgloader /grafana.load
    restart: "no"
    depends_on:
      - postgres
    volumes:
      - "./data:/data"
      - "./grafana.load:/grafana.load"

Create grafana.load file

grafana.load
load database
    from sqlite:///data/grafana.db
    into postgresql://grafana:grafana@grafana-postgres:5432/grafana

with data only, truncate, reset sequences;

7. Run pgloader

Run pgloader with options to only migrate data, ensuring sequences and constraints are reset properly:

Bash
docker compose up pgloader

This will transfer all existing dashboards, users, and config into the PostgreSQL database.

After pgloader was executed once you should comment it out in your docker compose file to avoid future overwrites of the postgresql database.

8. Start Grafana with PostgreSQL

Now start Grafana again:

Bash
docker compose up -d grafana

Grafana should now run using PostgreSQL as its backend — with all your existing data intact.

Verifying the Migration

Log into Grafana and confirm that:

  • Your dashboards are still there
  • Users are intact

If everything looks good, you can safely remove the old SQLite file backup later.

Full Example: docker-compose.yml

Here’s a working example you can adapt to your setup:

YAML
version: "3.9"

services:
  grafana:
    image: grafana/grafana-oss
    container_name: grafana
    restart: unless-stopped
    environment:
      - GF_DATABASE_TYPE=postgres
      - GF_DATABASE_HOST=grafana-postgres:5432
      - GF_DATABASE_NAME=grafana
      - GF_DATABASE_USER=grafana
      - GF_DATABASE_PASSWORD=grafana
    ports:
      - "3000:3000"
    volumes:
      - "./data:/var/lib/grafana"
    depends_on:
      - postgres

  postgres:
    image: postgres:17-alpine
    container_name: grafana-postgres
    restart: unless-stopped
    environment:
      - POSTGRES_USER=grafana
      - POSTGRES_PASSWORD=grafana
      - POSTGRES_DB=grafana
    volumes:
      - "./postgres-data-4:/var/lib/postgresql/data"
    expose:
      - "5432"

  pgloader:
    image: ghcr.io/dimitri/pgloader:latest
    container_name: pgloader
    command: pgloader /grafana.load
    restart: "no"
    depends_on:
      - postgres
    volumes:
      - "./data:/data"
      - "./grafana.load:/grafana.load"

Conclusion

Migrating Grafana from SQLite to PostgreSQL is straightforward with Docker Compose and pgloader. The key is to let Grafana generate the schema first, then import the data with pgloader’s data only option.

You now have a more robust Grafana backend that’s production-ready and easier to maintain at scale.

The migration steps are of course transferable to Kubernetes setups.

Leave a Comment

en_USEnglish