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: bridgeI 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_dbOnce 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.sqlFor Listmonk:
docker exec listmonk-container pg_dump -U listmonk -d listmonk > listmonk_backup.sqlImport 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.sqlI 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: bridgeImportant: 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: bridgeTesting 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_dataReal 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#
- 1TB NVMe SSD — Improves database server performance
- Intel N100 Mini PC — Silent and efficient home server for running Docker and PostgreSQL 24/7
- 600VA UPS/Power Backup — Protects the server and database from power outages
Affiliate links. No extra cost to you.