Pages

How to upgrade old version of postgres to latest in 2027?

Hi Noonari.

I was working on everything-personal project. I ran into a hiccup when I ran the `uv run manage.py migrate`. I was on `django 6.1.2`. And it turns out that from Django version 6.1 onwards, django will not be supporting postgres 14.XX or below along with any mysql below mysql 8.4. 

I had two options. One take a step back to django version 5.2LTS or stay on the current version and take on the challenge of upgrading the postgres version. Ofcourse, I lack foresight, I chose later. I added another challenge to this. I will not use AI to do this. Claude could have helped me do all this without a hiccup and I would have been done and dusted in like less than 30 minutes. But it is me. You know me. How I was. So Here is it.

I started from postgres official documentation. Why you ask? I did that because, every tom dick and harry of a search engine website has added AI explanation/Summary. I learnt about pg_upgrade. Here is what the docs taught me. pg_upgrade upgrades a cluster in place. It needs the old Postgres and the new Postgres installed side by side, and it moves your data files from one to the other. Powerful, but a bit raw. You have to point it at both binary folders, both data directories and both configs, and one wrong path is a bad afternoon.

Then I found out Ubuntu ships a friendlier wrapper called pg_upgradecluster. It figures out all the paths for you. By default it does a dump and restore into the new cluster, which is slower than pg_upgrade on huge databases but perfectly fine for my 39 MB of personal projects. If you want the real pg_upgrade behind it, there is a --method=upgrade flag. Good to know for later.

Now the actual work.

  1. Look around first.
   pg_lsclusters

A cluster is one running Postgres server with its own port and data folder. I had 14 on port 5432. Remember this, you will need it.

  1. Backup. Before anything else. The docs repeat this like a broken record, so I listened.
   cd /tmp && sudo -u postgres pg_dumpall > ~/pg14_backup.sql

The cd /tmp is there because the postgres user cannot enter my home folder and complains about it. The complaint is harmless.

  1. Check the backup is real.
   ls -lh ~/pg14_backup.sql
   grep -c "^CREATE DATABASE" ~/pg14_backup.sql
   tail -n 3 ~/pg14_backup.sql

39 MB, 12 databases, and the last line said PostgreSQL database cluster dump complete. Good.

  1. Add the official PostgreSQL repo and install 18. Ubuntu’s own repo lags behind.
   sudo apt install -y postgresql-common
   sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
   sudo apt install -y postgresql-18
   pg_lsclusters

Surprise. The installer quietly created an empty 18 main cluster on port 5433 because 14 was already on 5432. I also found a forgotten empty 16 main cluster hiding on 5434. Always run pg_lsclusters before you trust your memory.

  1. Get rid of the empty 18 cluster so the upgrade can use the name main.
   sudo pg_dropcluster 18 main --stop
  1. The upgrade itself. One command.
   sudo pg_upgradecluster -v 18 14 main

It builds a fresh 18 cluster, copies every database and user over, gives it port 5432, and parks the old 14 on another port, switched off. My Django settings did not need a single change.

  1. Verify. Do not skip this.
   pg_lsclusters
   sudo -u postgres psql -c "SELECT version();"
   sudo -u postgres psql -l
   uv run manage.py migrate

PostgreSQL 18.6, all my databases listed, and migrate ran with no version error. That was the moment.

  1. Clean up, but only after step 7 passes.
   sudo pg_dropcluster 14 main
   sudo apt remove -y postgresql-14

I kept the backup file anyway. It costs nothing.

Two things that scared me for no reason:

  • collation version mismatch warnings showed up on the old clusters. Ubuntu updated its system libraries after those clusters were created. The upgrade builds the new cluster against the current libraries, so it does not carry over.
  • apt suggested apt autoremove, and the list had ffmpeg and ImageMagick libraries in it. Nothing to do with Postgres. I removed only the old postgresql-client-xx packages by hand.

What I took away from it: the “scary” upgrade was really three ideas. A cluster is just a server with a port. A backup you have checked is the reason you can stay calm. And the old cluster stays on disk until you decide to delete it, so there is a way back until the very last step.

With this today’s lecture ends. Remember Noonari, the docs are slower than a chatbot, but they stick. Stay sharp and stay hungry.