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.
The sequence
Section titled “The sequence”export PGPASSWORD=$(grep -m1 '^POSTGRES_PASSWORD=' .env | cut -d= -f2-)docker compose stop appdocker run --rm --network badlen_default -e PGPASSWORD postgres:18-alpine \ pg_dump -h db -U badlen -d badlen > badlen-dump.sqlgrep -c 'PostgreSQL database dump complete' badlen-dump.sql # must print 1docker compose downdocker volume rm badlen_pgdatadocker compose up -d --wait dbdocker 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.sqldocker compose up -d appbadlen is the POSTGRES_USER from .env, and the app is stopped first so nothing writes during
the dump.
Why both clients run the new major
Section titled “Why both clients run the new major”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.
Why the start waits for the database
Section titled “Why the start waits for the database”--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.