Skip to content

PostgreSQL major upgrade

When the postgres image changes major version, an ordinary update is not enough. PostgreSQL refuses to start on a data directory written by an earlier major, so the data has to be dumped and reloaded. Since 18 the image also owns /var/lib/postgresql instead of data inside it, and it refuses to start on a volume laid out the old way rather than ignoring it, so a stack that has not been moved across fails visibly and changes nothing.

Terminal window
export PGPASSWORD=$(grep -m1 '^POSTGRES_PASSWORD=' .env | cut -d= -f2-)
docker compose stop app
docker run --rm --network badlen_default -e PGPASSWORD postgres:18-alpine \
pg_dump -h db -U badlen -d badlen > badlen-dump.sql
grep -c 'PostgreSQL database dump complete' badlen-dump.sql # must print 1
docker compose down
docker volume rm badlen_pgdata
docker compose up -d --wait db
docker run --rm -i --network badlen_default -e PGPASSWORD postgres:18-alpine \
psql -h db -U badlen -d badlen -v ON_ERROR_STOP=1 < badlen-dump.sql
docker compose up -d app

badlen is the POSTGRES_USER from .env, and the app is stopped first so nothing writes during the dump.

Both client containers run the new major on purpose: pg_dump should be at least as new as the server the file is reloaded into, and the file it writes uses psql meta-commands an older psql does not know.

--wait on the up is not a nicety. The volume was just deleted, so this start is the one where the database creates its cluster from nothing, and for some ten seconds the container is up and the server is not listening. Without it, up -d hands back as soon as the container exists and psql lands on Connection refused at the one moment when the dump file is your only copy.

Privacy