Skip to main content

Migrate Grafana and Listmonk to centralized PostgreSQL in Docker

Rogelio Guerra Riverón
Author
Rogelio Guerra Riverón
Building my own web infrastructure from scratch. Here I document each step: servers, networks, containers and everything that comes along.

The initial problem
#

I had Grafana and Listmonk running in Docker containers, each with its own embedded PostgreSQL instance. This worked, but it was inefficient: two database engines consuming resources and no centralized way to do backups. I decided to consolidate everything into a single shared PostgreSQL instance.

Preparation: setting up central PostgreSQL
#

The first step was to create the PostgreSQL server that would be the central point. I did this with a dedicated docker-compose:

version: '3.8'
services:
  postgres:
    image: postgres:15-alpine
    container_name: postgres-central
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: tu_contraseña_segura
      POSTGRES_DB: default_db
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - central-net

volumes:
  postgres_data:

networks:
  central-net:
    driver: bridge

I ran docker-compose up -d and verified it worked with docker exec postgres-central psql -U admin -d default_db -c "\l".

Create databases for each service
#

I accessed the PostgreSQL container and created the databases I would need:

docker exec -it postgres-central psql -U admin -d default_db

Once inside:

CREATE DATABASE grafana;
CREATE DATABASE listmonk;
CREATE USER grafana_user WITH PASSWORD 'grafana_pass';
CREATE USER listmonk_user WITH PASSWORD 'listmonk_pass';
GRANT ALL PRIVILEGES ON DATABASE grafana TO grafana_user;
GRANT ALL PRIVILEGES ON DATABASE listmonk TO listmonk_user;

Export data from the old databases
#

Before moving anything, I dumped the existing databases. For Grafana:

docker exec grafana-container pg_dump -U grafana -d grafana > grafana_backup.sql

For Listmonk:

docker exec listmonk-container pg_dump -U listmonk -d listmonk > listmonk_backup.sql

Import data into central PostgreSQL
#

I imported the backups into the new databases:

docker exec -i postgres-central psql -U grafana_user -d grafana < grafana_backup.sql
docker exec -i postgres-central psql -U listmonk_user -d listmonk < listmonk_backup.sql

I verified that the tables were present with \dt in each database.

Update Grafana
#

I modified Grafana’s docker-compose to point to central PostgreSQL:

services:
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    environment:
      GF_DATABASE_TYPE: postgres
      GF_DATABASE_HOST: postgres-central:5432
      GF_DATABASE_NAME: grafana
      GF_DATABASE_USER: grafana_user
      GF_DATABASE_PASSWORD: grafana_pass
    networks:
      - central-net
      - grafana-net

networks:
  central-net:
    external: true
  grafana-net:
    driver: bridge

Important: the central-net network must be external: true because it already exists in the PostgreSQL docker-compose.

Update Listmonk
#

The same for Listmonk. Its config in docker-compose:

services:
  listmonk:
    image: listmonk/listmonk:latest
    container_name: listmonk
    environment:
      LISTMONK_db__host: postgres-central
      LISTMONK_db__port: "5432"
      LISTMONK_db__user: listmonk_user
      LISTMONK_db__password: listmonk_pass
      LISTMONK_db__database: listmonk
    networks:
      - central-net
      - listmonk-net

networks:
  central-net:
    external: true
  listmonk-net:
    driver: bridge

Testing and cleanup
#

I brought up both containers: docker-compose up -d. I verified that Grafana and Listmonk started correctly and that their data was intact.

Once everything was confirmed working, I deleted the volumes from the old databases:

docker volume rm grafana_postgres_data listmonk_postgres_data

Real benefits
#

Now I have a single backup point, less RAM consumption, and can scale more easily. One thing to watch: make sure central PostgreSQL is on the correct network or that services can communicate via external host.

The migration took me an hour. No stress.

Recommended equipment#

Affiliate links. No extra cost to you.