monotux.tech

Upgrading to a new Postgresql major version on NixOS

NixOS PostgreSQL

I’ve seen a few different guides on how to upgrade to a new major PostgreSQL version but couldn’t make it work as I’m also using extensions for some of my databases. Luckily this was relatively easy due to NixOS declarative nature!

My method is just an modified version of Kevin Cox’s guide. A few other sources of inspiration was the NixOS wiki entry on upgrading (which uses flakes which I still don’t understand) and reading the pg_upgrade manual.

Table of Contents

Preflight check

First up, run a check before doing anything more, this is done with the old PostgreSQL version running:

sudo -su postgres

cd /var/lib/postgresql/

old=15
new=17

pg_old=$(nix-build --no-out-link -E "with import <nixpkgs> {}; \
         postgresql_${old:?}.withPackages (p: [ p.pgvector p.vectorchord ])")
pg_new=$(nix-build --no-out-link -E "with import <nixpkgs> {}; \
         postgresql_${new:?}.withPackages (p: [ p.pgvector p.vectorchord ])")

# Update shared_preload_libraries to your needs!
$pg_new/bin/pg_upgrade \
        --socketdir=/var/run/postgresql \
        --old-bindir=$pg_old/bin \
        --new-bindir=$pg_new/bin \
        --old-datadir=/var/lib/postgresql/${old:?} \
        --new-datadir=/var/lib/postgresql/${new:?} \
        --old-options "-c shared_preload_libraries='vchord.so'" \
        --new-options "-c shared_preload_libraries='vchord.so'" \
        --check

$pg_old and $pg_new needs to be representative for your current and your future PostgreSQL installations. In my case I’m also including pgvector and vectorchord in my installation1, but if you try to upgrade to a new version without the necessary extensions this check will fail.

If the check went ok then we can prepare for upgrading.

Preparing for the upgrade

In this example I’m using postgresql_15 and want to upgrade to the latest packaged version (which currently is 17). Below is a very short slice of my configuration:

{ pkgs, ... }:
{
  services.postgresql.enable = true;
  services.postgresql.package = pkgs.postgresql;
  # services.postgresql.package = pkgs.postgresql_15;
  # ...
}

Change services.postgresql.package to the version you want to upgrade to, and then build (don’t switch yet!)

nixos-rebuild build

Now, stop the database, prepare the socket directory under /var/run:

systemctl stop postgresql.service
mkdir /var/run/postgresql
chown postgres:postgres /var/run/postgresql

Upgrade the database

Run initdb and migrate your database:

sudo -su postgres

cd /var/lib/postgresql/

old=15
new=17

pg_old=$(nix-build --no-out-link -E "with import <nixpkgs> {}; \
         postgresql_${old:?}.withPackages (p: [ p.pgvector p.vectorchord ])")
pg_new=$(nix-build --no-out-link -E "with import <nixpkgs> {}; \
         postgresql_${new:?}.withPackages (p: [ p.pgvector p.vectorchord ])")

$pg_new/bin/initdb -D /var/lib/postgresql/17 || exit 1

# Remember to update the shared_preload_libraries if needed!
$pg_new/bin/pg_upgrade \
        --socketdir=/var/run/postgresql \
        --old-bindir=$pg_old/bin \
        --new-bindir=$pg_new/bin \
        --old-datadir=/var/lib/postgresql/${old:?} \
        --new-datadir=/var/lib/postgresql/${new:?} \
        --old-options "-c shared_preload_libraries='vchord.so'" \
        --new-options "-c shared_preload_libraries='vchord.so'" \

If the migration was successful, switch over to your new system configuration:

nixos-rebuild switch

Check if PostgreSQL if running:

systemctl status postgresql.service

Update extensions

After upgrading, I noticed that I had two new files in /var/lib/postgresql, one called update_extensions.sql and one called delete_old_cluster.sh.

I was interested in update_extensions.sql, which contained:

\c immich
ALTER EXTENSION "earthdistance" UPDATE;

That makes sense, as my Immich databases makes use of several extensions!

To apply:

sudo -u postgres psql -f /var/lib/postgresql/update_extensions.sql

Conclusion

Now I’m just waiting for NixOS 25.11 which should include PostgreSQL 18, so I can start experimenting with it’s new OIDC support!


  1. Immich requires them to function, no other ai here, I promise! ↩︎