controls

Motivation

Truth be told. How often do you update your home setup? I often have the feeling I should upgrade my home server running Nextcloud. But then there comes out of the blue an unexpected roadblock. And all the good intentions are blown away by frustation.

This post was the reason for me to start my blog. Because I already started to upgrade my Nextcloud setup a couple of month ago. This project directly led me to run into a roadblock. It was called Postgres.

If you ever had to get your hands dirty with pg_dump or pg_dumpall you may guess what my struggle was. You may ask yourself why I struggled with this. Well, recently a valued colleague of mine pointed out that doing a major Postgres upgrade does not necessarily need to involve aforementioned tools. He said it should be possible to simply bump the major version and let Postgres modify the data. I felt a bit stupid but than I remembered why I wanted to do my upgrade using a dump of the database.

Because when I do a major version upgrade of my precious database I want to make sure if something goes wrong, for whatever reason, I can restore a backup and ensure everything works as before. So I not just want to have an upgrade I also want a validated backup and restore.

Concept

So with this preconditions I had to decide between pg_dump and pg_dumpall. Which seemed to be easy at first glance but turned out o be tricky. Use pg_dump to create a dump of your database and pg_restore to restore it. Easy peasy, but! if you now think - as I foolishly did - you can do the same with pg_dumpall you are so wrong! What I wanted to have was a convenient dumping of my database:

pg_dump dumps only a single database at a time, and it does not dump information about roles or tablespaces (because those are cluster-wide rather than per-database). To support convenient dumping of the entire contents of a database cluster, the pg_dumpall program is provided. pg_dumpall backs up each database in a given cluster, and also preserves cluster-wide data such as role and tablespace definitions.

source: postgresql docs

So pg_dumpall it is. But how can I restore it now without having the benefits of pg_restore? Especially in my containerized environment! A second Postgres container needs to be deployed. It will have its own volume and the desired Postgres version. In this setup you will have the benefit of never changing your current data. The current data will be dumped and restored into the separate database. Afterwards you point to the database and check if everything works. If not you still have the old database and can switch back anytime.

   db_postgres_14:
    image: postgres:14.6-alpine
    restart: always
    volumes:
      - <NEW_VOL>:/var/lib/postgresql/data
    env_file:
      - db.env

Note that the mountpoint is important

Upgrade Process

I will keep the steps simple and when I use an example it will be Nextcloud. This process does not only apply to Nextcloud and can be adapted to similar scenarios.

Preparation

Before you upgrade any component of your Nextcloud stack do a backup. Especially if you touch the database. I can recommend BorgBackup https://www.borgbackup.org as it is simple, efficient and reliable. Turn on maintenance mode so there will be no writes to the database:

docker-compose exec --user www-data app php occ maintenance:mode --on

Dump Data

Get your data out of the running database container:

docker-compose exec -T YOUR-DB-CONTAINER \
  pg_dumpall --clean -U postgres --quote-all-identifiers \
  > /PATH/TO/DUMP/pgdumpall_2022-11-01.sql

For the pg_dumpall parameters look at postgresql docs

Copy Dump Into New DB

There are multiple options to get the data into the new database but I decided to copy it into the volume that is mounted into the container.

docker cp pgdumpall_2022-11-01.sql NEW-DB_postgres_14:/var/lib/postgresql/data

Restore Data

And insert it into the newly created database:

docker-compose exec NEW-DB_postgres_14 \
  psql -U postgres -f /var/lib/postgresql/data/pgdumpall_2022-11-01.sql

Cleanup

Now we do not need the old dump in the volume of the new database container and can remove it.

docker-compose exec NEW-DB_postgres_14 /bin/sh

rm /var/lib/postgresql/data/pgdumpall_2022-11-01.sql

Change DB Container

This depends on your app and likely have to be looked up in the corresponding documentation. In case you use Nextcloud the config.php needs to be configured.

docker exec -it NEXTCLOUD_CONTAINER /bin/sh

vi config/config.php

You need to modify the dbhost and change the value to NEW-DB_postgres_14.

Also the value of POSTGRES_HOST needs to be adapted with the same value.

To make the changes work the containers need a restart:

docker-compose restart

NOTE: Check if you can log in. Logged in as Administrator you should make sure that the new database is recognized as expected and no errors appear.

Troubleshooting

convert-filecache-bigint

docker exec -it --user www-data <container_ID> php occ db:convert-filecache-bigint

add-missing-indices

docker exec -it --user www-data NEXTCLOUD_CONTAINER php occ db:add-missing-indices

Conclusion

For me upgrading Postgres is still a bigger task and takes its time but has become strait forward and not scary. Still it would be way more convenient to have pg_restoreall 😉