How to Upgrade Postgres in Docker
Overcoming Challenges During a Postgres Database Upgrade
Postgres is one of my go-to, go-to databases, hands down. But it's also important to keep this database up to date from time to time. I recently wanted to upgrade the Postgres database of my self-hosted Plausible instance from version 14 to version 16. Since this is a step between major versions and not as straightforward as minor versions, I thought I'd document how simple it can be if you have everything set up in Docker with docker compose.
Upgrade Minor/Patch Versions
Upgrading from a minor or patch version is as simple as swapping out the current image for the new version and starting the container again. The other steps will be taken for you automatically, so there's no need for you to take any manual action.
For example just replace you current version:
image: postgres:14.12-alpine
With the new version:
image: postgres:14.13-alpine
Upgrade Major Versions
Upgrading between major versions requires a bit more attention because it could introduce breaking changes. Since it sometimes changes how data is written to the filesystem, it's better to make the migration by exporting the database and importing the data into the new database. This can be done in just a few simple steps.
1. Add a Container for the new Database version
The first thing we'll do is add a new container for the upgrade version to our docker-compose.yml
and start the stack.
services:
plausible_db:
image: postgres:14-alpine
container_name: plausible_db_14
restart: unless-stopped
volumes:
- ./postgres-14:/var/lib/postgresql/data
env_file:
- stack.env
# new database version
plausible_db_15:
image: postgres:15-alpine
container_name: plausible_db_15
restart: unless-stopped
volumes:
- ./postgres-15:/var/lib/postgresql/data
env_file:
- stack.env
# other services
plausible:
image: ghcr.io/plausible/community-edition:v2.1.4
container_name: plausible
# [...]
As you can see in the configuration, we generally use the same setup, but we've added a new volume for the data.
3. Stop the Workload
The goal here is to make sure that no new data is written to our database. In my case, I need to stop the plausible container.
docker stop plausible
4. Create Postgres Backup
We can use pg_dumpall to back up our database.
docker exec -it plausible_db_14 pg_dumpall -U postgres > plausible_upgrade_backup_14.sql
Double-check you're using the right PostgreSQL credentials and take a look at the file to confirm it's in SQL format. If there are any issues with the backup, try to resolve them before moving forward.
5. Import Postgres Backup
Now we can move the backup to the new database.
cat plausible_upgrade_backup_14.sql | docker exec -i plausible_db_15 psql -U postgres
6. Switch Database for your workload
As a final step, we can move our workload to a new database and start the container again.
services:
# rename so that its no longer in use
plausible_db_14:
image: postgres:14-alpine
container_name: plausible_db_14
restart: unless-stopped
volumes:
- ./postgres-14:/var/lib/postgresql/data
env_file:
- stack.env
# replacement for old database service
plausible_db:
image: postgres:15-alpine
container_name: plausible_db_15
restart: unless-stopped
volumes:
- ./postgres-15:/var/lib/postgresql/data
env_file:
- stack.env
# other services
plausible:
image: ghcr.io/plausible/community-edition:v2.1.4
container_name: plausible
# [...]
Once we get the workload back up and running, we should be good to go with our service as usual.
docker start plausible
If everything's running fine with our service, we can go and get rid of the old database service plausible_db_14
from our docker-compose.yml
and then the database upgrade will be done. But I'd say it's a good idea to check the service first before deleting the old database, because we can always switch back to the old setup if something isn't working right.
Conclusion
As you can see, with a Docker Compose setup, it's pretty simple to upgrade a major Postgres version, but there is a risk of losing all your data. The good news is that we can always switch back to the old version if anything goes wrong.