Image of Fixing the PostgreSQL 18 PGDATA Error in Docker Compose

Fixing the PostgreSQL 18 PGDATA Error in Docker Compose

Aron Schüler Published


Intro

If you’re a developer using Docker Compose for your local development environment, you’ve likely appreciated how easy it is to spin up services like a PostgreSQL database. You define your service in a docker-compose.yml file, run docker compose up, and you’re good to go.

But what happens when updating the PostgreSQL image causes things to suddenly break? When upgrading to PostgreSQL 18 or using the latest tag, you might be met with a frustrating startup error that prevents your database container from running correctly. This is due to a significant but subtle breaking change in the official PostgreSQL Docker image regarding the PGDATA environment variable.

This post will walk you through the error, explain the change, and provide the simple fix to get your development environment back up and running.

The PostgreSQL 18 Docker Startup Error

Let’s say you have a docker-compose.yml file that looks something like this, using a pre-18 PostgreSQL version:

services:
  db:
    image: postgres:16
    restart: always
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydatabase
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
 
volumes:
  postgres-data:

This configuration has worked reliably for older PostgreSQL versions. However, if you simply change the image to postgres:18 or have postgres:latest and pulled recently, your container will fail to start, and you’ll see a message in the logs like this:

Important Change: the PGDATA environment variable of the image was changed to be version specific in PostgreSQL 18 and above⁠. For 18 it is /var/lib/postgresql/18/docker. Later versions will replace 18 with their respective major version (e.g., /var/lib/postgresql/19/docker for PostgreSQL 19.x). The defined VOLUME was changed in 18 and above to /var/lib/postgresql. Mounts and volumes should be targeted at the updated location. This will allow users upgrading between PostgreSQL major releases to use the faster --link when running pg_upgrade and mounting /var/lib/postgresql.

This message clearly indicates a change in how data volumes should be handled for PostgreSQL 18 and newer.

Why PostgreSQL 18 Breaks Your Docker Compose Setup

The core of the issue is that the maintainers of the official PostgreSQL Docker image have changed the data directory structure. Starting with version 18, the PGDATA environment variable, which points to the database’s data directory, is now version-specific. This is a key breaking change for anyone mounting volumes the old way.

This change was implemented to make major version upgrades smoother. By having versioned data directories, you can use tools like pg_upgrade more easily when moving from one major PostgreSQL version to another.

How to Fix the Docker Compose Volume Mount

To resolve the startup error, you need to update the volume mount in your docker-compose.yml file to align with the new directory structure.

Here is the old, non-working configuration line:

# ...
volumes:
  - postgres-data:/var/lib/postgresql/data
# ...

You need to change it to the new, correct path:

# ...
volumes:
  - postgres-data:/var/lib/postgresql/docker
# ...

Your final, working docker-compose.yml for PostgreSQL 18 and newer should look like this:

services:
  db:
    image: postgres:18 # This fix applies to version 18 and any newer versions
    restart: always
    environment:
      POSTG-data:

With this simple change to your volume definition, your PostgreSQL container should start up without any issues.

A Note on Upgrades

While the fix above works perfectly for getting a single version of PostgreSQL running, the official documentation points to another way of mounting the volume that is specifically designed to make major version upgrades easier.

If you plan to perform pg_upgrade between major versions (e.g., from 18 to 19) in the future, it is recommended to mount the parent directory:

# ...
volumes:
  - postgres-data:/var/lib/postgresql
# ...

By mounting /var/lib/postgresql, Docker will create version-specific subdirectories (like 18/docker, 19/docker) inside your postgres-data volume. This allows different PostgreSQL versions to coexist and makes the pg_upgrade process more straightforward.

For most local development scenarios where you just need the database to run, the first fix is sufficient. But it’s good to know about the recommended approach for future-proofing your database upgrades.

Backing Up and Restoring Your Data

Important: Applying this fix will create a new database volume. If you have important data in your local PostgreSQL container, you must back it up before making these changes and restore it after.

Here’s a step-by-step guide to safely migrate your data.

Step 1: Back Up Your Database

With your old PostgreSQL container still running, execute the following command in your terminal. This will dump the database contents into a backup.dump file in your current directory.

docker compose exec db pg_dump -U user -d mydatabase -F c > backup.dump

Make sure to replace db, user, and mydatabase with your Docker Compose service name, PostgreSQL user, and database name, respectively.

Step 2: Stop the Old Container

Now you can safely shut down your Docker Compose environment.

docker compose down

Step 3: Apply the Fix

Update your docker-compose.yml with the correct volume mount as described earlier in this post.

Step 4: Start the New PostgreSQL Container

Bring your database service back up with the updated configuration. The --force-recreate flag ensures it starts fresh.

docker compose up -d --force-recreate db

Step 5: Restore Your Data

Finally, restore your data from the backup file into the new container. The -T flag is important as it disables pseudo-tty allocation, which is necessary when piping file content.

cat backup.dump | docker compose exec -T db pg_restore -U user -d mydatabase

Once the command completes, your data will be available in the new PostgreSQL 18+ container. You can connect to your database to verify that everything has been restored correctly.

Conclusion

The breaking change in the PostgreSQL 18 Docker image is a welcome one for those who need to manage major version upgrades, but it can be a small stumbling block for developers just trying to get their local environment running. By making a simple one-line change in your docker-compose.yml, you can adapt to the new PGDATA structure and get back to coding.

I hope this quick fix helps you resolve the issue. If you have any questions or other tips, feel to share them! :)


Related Posts

Find posts on similar topics:


Support me via buymeacoffee

If I helped you solve a problem or you just like my content, consider supporting me by buying me a coffee.

Buy me a coffee

Comments