# PostgreSQL 15 update

**URL:** https://meta.discourse.org/t/postgresql-15-update/349515
**Category:** Announcements
**Created:** [January 29, 2025, 7:20am UTC](https://meta.discourse.org/t/postgresql-15-update/349515 "2025-01-29T07:20:00Z")
**Posts on this page:** 1
**Showing post:** 1

<div class="post-metadata">

### Author: ![mwaniki](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/mwaniki/32/313097_2.png) [@mwaniki](https://meta.discourse.org/u/mwaniki)
#### Post date: [January 29, 2025, 7:20am UTC](https://meta.discourse.org/t/postgresql-15-update/349515/1 "2025-01-29T07:20:00Z")

</div>

> ⚠ **WARNING!** If your database is very large, you will need a lot of extra disk space (2x database size) and should be very careful with this upgrade!

We’ve just landed changes to upgrade our Docker image to PostgreSQL 15. Any site admins rebuilding Discourse from the command line will be upgraded to PostgreSQL 15 from the previous PostgreSQL 13. Note that if you held back from upgrading when the PostgreSQL 13 update [happened back in 2020](https://meta.discourse.org/t/postgresql-13-update/172563), you can skip that upgrade and go straight to PostgreSQL 15.

If you had held back the upgrade previously, change the PostgreSQL template in `app.yml` from `templates/postgres.12.template.yml` to `templates/postgres.template.yml`.

As with any upgrade, it is **strongly advised** to take a backup before doing anything.

## Updating

### [Official Install Guide](https://meta.discourse.org/t/142537?silent=true) (single container)

On your next rebuild, you will see this message at the end:

```plaintext
-------------------------------------------------------------------------------------
UPGRADE OF POSTGRES COMPLETE

Old 13 database is stored at /shared/postgres_data_old

To complete the upgrade, rebuild again using:

./launcher rebuild app
-------------------------------------------------------------------------------------

```

That means everything went well in the upgrade! You just need to issue a new rebuild to get your site back and running.

### Data Container Install

If you are running a setup with a dedicated data container based in the sample supplied in our discourse\_docker repository, you want to be sure you are shutting down PostgreSQL in a safe and clean way.

Nowadays, we have background jobs running queries spanning several minutes, so shutting down the web container will help the data container be shutdown safely.

```plaintext
./launcher stop web_only
./launcher stop data
./launcher rebuild data
./launcher rebuild data
./launcher rebuild web_only

```

Before issuing the first rebuild to the data container, you can tail the PostgreSQL log to see if it was shutdown properly.

Running a `tail -f shared/standalone/log/var-log/postgres/current` should give you the following log if it was clean:

```plaintext
2025-01-24 09:19:06.437 UTC [37] LOG: received smart shutdown request
2025-01-24 09:19:06.444 UTC [37] LOG: background worker "logical replication launcher" (PID 54) exited with exit code 1
2025-01-24 09:19:06.446 UTC [49] LOG: shutting down
2025-01-24 09:19:06.468 UTC [37] LOG: database system is shut down

```

### Doing a manual update / space constrained environments

> ⚠⚠⚠  
> YOU MUST BACKUP THE POSTGRES\_DATA BEFORE TRYING THIS  
> ⚠⚠⚠

If you are in a constrained space environment without any way to get more space you can try the following:

```bash
./launcher stop app #(or both web_only and data if that is your case)
mkdir -p /var/discourse/shared/standalone/postgres_data_new
docker run --rm \
	--entrypoint=/bin/bash \
	-v /var/discourse/shared/standalone/postgres_data:/var/lib/postgresql/13/data \
	-v /var/discourse/shared/standalone/postgres_data_new:/var/lib/postgresql/15/data \
	tianon/postgres-upgrade:13-to-15 \
	-c "apt-get update && apt-get install -y postgresql-13-pgvector postgresql-15-pgvector &&
	docker-upgrade"
mv /var/discourse/shared/standalone/postgres_data /var/discourse/shared/standalone/postgres_data_old
mv /var/discourse/shared/standalone/postgres_data_new /var/discourse/shared/standalone/postgres_data
docker run --rm -v /var/discourse/shared/standalone:/shared local_discourse/app \
	chown -R postgres:postgres /shared/postgres_data #(or local_discourse/data)
./launcher rebuild app #(or first data and then web_only if that is your case)

```

On my tests this procedure requires less than 1x your current database size in free space.

If you are using a non-default locale, you can try replacing the first `docker` command with this:

```bash
# change 'en_US.UTF-8' to your locale
docker run --rm \
	--entrypoint=/bin/bash \
	-e LANG='en_US.UTF-8' \
	-v /var/discourse/shared/standalone/postgres_data:/var/lib/postgresql/13/data \
	-v /var/discourse/shared/standalone/postgres_data_new:/var/lib/postgresql/15/data \
	tianon/postgres-upgrade:13-to-15 \
	-c 'sed -i "s/^# $LANG/$LANG/" /etc/locale.gen && locale-gen &&
	apt-get update && apt-get install -y postgresql-13-pgvector postgresql-15-pgvector &&
	docker-upgrade'

```

## Postponing the update

If you need to postpone the update during your next rebuild, you can swap the PostgreSQL template on your app.yml file by changing `"templates/postgres.template.yml"` to `"templates/postgres.13.template.yml"`.

This is not recommended, as some site admins will forget to revert the change afterwards.

## Post update optional tasks

### Optimizing PostgreSQL statistics

After the update, the new PostgreSQL won’t have table statistics on hand. You can generate those using:

```plaintext
docker exec -u postgres app \
	/usr/lib/postgresql/15/bin/vacuumdb -d discourse --analyze-in-stages

```

### Cleaning up old data

For a [standard install](https://meta.discourse.org/t/142537?silent=true), you can delete the old data in PG13 format with the following command:

```plaintext
cd /var/discourse
./launcher cleanup

```

If you have a separate data container, you’ll need to remove the backup copy like this:

```plaintext
rm -fr /var/discourse/shared/data/postgres_data_old/

```

## FAQ

### The source cluster was not shut down cleanly

If you get a upgrade failed with the above message, you can try a simpler approach to get it back into a better state.

Restart the old container with `./launcher start app`. Wait a few minutes until it is back up.

Now shut it down again with `./launcher stop app`. After that tail the logs to see if it was a clean one:

```plaintext
tail -f shared/standalone/log/var-log/postgres/current
2025-01-24 09:19:06.437 UTC [37] LOG: received smart shutdown request
2025-01-24 09:19:06.444 UTC [37] LOG: background worker "logical replication launcher" (PID 54) exited with exit code 1
2025-01-24 09:19:06.446 UTC [49] LOG: shutting down
2025-01-24 09:19:06.468 UTC [37] LOG: database system is shut down

```

If the logs do not indicate that the database is shut down, you can start the old container again, enter with `./launcher enter app`, run these commands and tail the logs again once done.

```plaintext
export SVWAIT=300
sv stop nginx
sv stop unicorn
sv stop postgres
exit

```

If the logs look like above, you can now try to upgrade again using `./launcher rebuild app`.

### lc\_collate values for database “postgres” do not match

This errors happens if you are using non-default locales for your database. It was reported that you need 3 variables for it to succeed. Ensure that the `env: ` section of you app.yml file has the 3 lines:

```yaml
  LC_ALL: en_US.UTF-8
  LANG: en_US.UTF-8
  LANGUAGE: en_US.UTF-8

```

Changing `en_US.UTF-8` to your locale.

### Every rebuild does the upgrade again aka upgrade loop

When this happens your upgrade logs will contain

```plaintext
mv: cannot move '/shared/postgres_data' to '/shared/postgres_data_old/postgres_data': Directory not empty
mv: cannot move '/shared/postgres_data_new' to '/shared/postgres_data/postgres_data_new': Directory not empty

```

This means that there are still files from the last upgrade lingering around. Move those elsewhere before continuing.

### Upgrade Complete suggestion scripts - do I need to do anything?

Once the upgrade completes, you’ll see output from the `pg_upgrade` message saying:

```plaintext
Upgrade Complete
----------------
Optimizer statistics are not transferred by pg_upgrade.
Once you start the new server, consider running:
    /usr/lib/postgresql/15/bin/vacuumdb --all --analyze-in-stages

Running this script will delete the old cluster's data files:
    ./delete_old_cluster.sh

```

You can safely ignore this message.

### I did skip the PostgreSQL 13 update, what to do now?

You can follow the standard instructions at the top of this guide and they will upgrade from your version to 15 without issues.

If you are following the space constrained instructions, adapt the version numbers accordingly.

Rebuilding indexes can give you significant disk space savings. Follow the steps from our [PostgreSQL 13 update](https://meta.discourse.org/t/postgresql-13-update/172563#re-creating-the-indexes-8) after the upgrade.

---

_[View the full topic](https://meta.discourse.org/t/postgresql-15-update/349515)._
