mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
docs(ops): describe the move to a newer Postgres major
The compose file stays on 16. Changing only the image line stops the stack: the 18 image refuses a data directory from an older major and expects its volume one level up. The runbook walks the dump and restore into a fresh volume, keeps the old one as the way back, and is linked from the update section and the compose pin.
This commit is contained in:
1 parent
84064ea045
commit
ffd06ce289
3 files changed
+96
-1
No files matched your search
+2
-1
@@ -577,7 +577,8 @@ services:
|
||||
# change (at pin time `16-alpine` resolved to this same digest, so
|
||||
# existing deployments see no image change). Minor releases share
|
||||
# the on-disk format — bumping this pin is safe without a dump /
|
||||
# restore; a major-version bump (17+) is not.
|
||||
# restore; a major-version bump (17+) is not, see
|
||||
# docs/ops/postgres-major-upgrade.md.
|
||||
#
|
||||
# The pin only works as long as something watches it: PostgreSQL
|
||||
# publishes minor releases with security and data-corruption fixes and
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# Moving the database to a newer Postgres major
|
||||
|
||||
The compose file ships Postgres 16 and stays there for now, because a major
|
||||
version change is the one database update that cannot happen by itself on
|
||||
`docker compose pull`. HealthLog runs on Postgres 18 as well: the full
|
||||
integration suite runs against 18 (`INTEGRATION_PG_IMAGE=postgres:18-alpine
|
||||
pnpm test:integration`), and the TrueNAS app starts new installs on 18.
|
||||
|
||||
## Why changing the image line is not enough
|
||||
|
||||
Minor releases (16.14 to 16.15) share the on-disk format. Majors do not, and
|
||||
the official image refuses to start a data directory written by an older
|
||||
major. From 18 on, the image also keeps its data one level deeper, under
|
||||
`/var/lib/postgresql/18/docker`, and expects the volume mounted at
|
||||
`/var/lib/postgresql` instead of `/var/lib/postgresql/data`. With the old
|
||||
volume and only the image line changed, the `db` container exits at once with
|
||||
"there appears to be PostgreSQL data in: /var/lib/postgresql/data". Nothing is
|
||||
lost, but the stack stays down until the image line goes back to 16.
|
||||
|
||||
So the move is a dump from 16 and a restore into a fresh 18 volume.
|
||||
|
||||
## Steps
|
||||
|
||||
Take the encryption key's copy out of its safe place first: the dump is
|
||||
unreadable without it, and this is the moment you would find out.
|
||||
|
||||
```bash
|
||||
# 1. Stop the app so nothing writes while the dump runs.
|
||||
docker compose stop app
|
||||
|
||||
# 2. Dump the database in Postgres' custom format.
|
||||
docker compose exec -T db pg_dump -U healthlog -Fc healthlog \
|
||||
> healthlog-pg16-$(date +%F).dump
|
||||
|
||||
# 3. Stop the old database. Its volume stays untouched.
|
||||
docker compose stop db
|
||||
```
|
||||
|
||||
4. In `docker-compose.yml`, change the `db` service:
|
||||
|
||||
```yaml
|
||||
image: postgres:18.6-alpine # or the current 18 minor
|
||||
volumes:
|
||||
- pgdata18:/var/lib/postgresql
|
||||
```
|
||||
|
||||
and add the new volume next to `pgdata` in the top-level `volumes:` block:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
pgdata:
|
||||
pgdata18:
|
||||
```
|
||||
|
||||
```bash
|
||||
# 5. Start the new database and wait until it reports healthy.
|
||||
docker compose up -d db
|
||||
docker compose ps db
|
||||
|
||||
# 6. Restore the dump into it.
|
||||
docker compose exec -T db pg_restore -U healthlog -d healthlog --no-owner \
|
||||
< healthlog-pg16-<date>.dump
|
||||
|
||||
# 7. Start the app and confirm it.
|
||||
docker compose up -d app
|
||||
curl -s http://localhost:3000/api/version
|
||||
```
|
||||
|
||||
The start log should say there are no pending migrations: the dump carried the
|
||||
migration history with it. Sign in and open a few pages before you go on.
|
||||
|
||||
## Afterwards
|
||||
|
||||
Keep the old `pgdata` volume and the dump until the new database has run for a
|
||||
few days. Going back means restoring the old `image:` and `volumes:` lines;
|
||||
the old volume still holds the database as it was before step 2. When you are
|
||||
sure, remove it (`docker volume ls` shows its full name, prefixed with the
|
||||
compose project):
|
||||
|
||||
```bash
|
||||
docker volume rm <project>_pgdata
|
||||
```
|
||||
|
||||
If you had run `CREATE EXTENSION pg_stat_statements;` for
|
||||
[query statistics](query-statistics.md), the dump carries it and the compose
|
||||
`command:` still loads the library, so nothing needs doing there.
|
||||
|
||||
`pg_upgrade --link` is faster on a very large database, but it needs both
|
||||
majors' binaries in one container; for a personal health record the dump is
|
||||
seconds to minutes and has nothing to set up.
|
||||
@@ -393,6 +393,10 @@ curl -s http://localhost:3000/api/version
|
||||
Keep the dump until the new version has run for a few days, and keep your
|
||||
encryption key with it: a dump without the key cannot be read.
|
||||
|
||||
The database image is a separate matter: a new Postgres minor is a change of
|
||||
the `image:` line, a new major (16 to 18) is a dump and restore, described in
|
||||
[`docs/ops/postgres-major-upgrade.md`](../ops/postgres-major-upgrade.md).
|
||||
|
||||
The start log says what is about to happen before the migrations run:
|
||||
|
||||
```text
|
||||
|
||||
Reference in new issue
Block a user