The error, verbatim
Jump to the fix ↓psql: error: connection to server at "localhost" (::1), port 5432 failed: FATAL: password authentication failed for user "postgres"
Tested on
- Postgres
- 18.6 (official Docker image)
- Clients
- psql 16.15, node-postgres 8.23.1, Prisma 7.10.1
- Docker
- Docker Compose v2
- OS
- Ubuntu 24.04 (GitHub Actions runner)
✓ Last re-run on GitHub Actions · see the run · all repros
Contents
The most common version of this has nothing to do with typing: you changed POSTGRES_PASSWORD in docker-compose.yml, restarted, and the new password fails while the old one still works. POSTGRES_PASSWORD is only read the first time the container starts with an empty data folder, and the volume keeps the old password. Postgres gives the same message for that, for a user that doesn’t exist and for a different server on the same port, on purpose, so an attacker can’t tell which part was right.
I caused it five different ways with the official postgres:18 image. Every time, the client said exactly the same thing:
| What was actually wrong | What the client said |
|---|---|
The password in docker-compose.yml was changed after the first start |
password authentication failed for user "postgres" |
POSTGRES_USER was added after the first start |
password authentication failed for user "admin" |
The env file had quotes around the password, used with docker run --env-file |
password authentication failed for user "postgres" |
| Another Postgres was already running on port 5432 | password authentication failed for user "postgres" |
| Plain wrong password | password authentication failed for user "postgres" |
Step 1: read the server’s side
The client only gets the short version. The server writes the reason to its own log, one line under the error:
docker logs <container> 2>&1 | grep -E "FATAL|DETAIL"With Compose, docker compose ps -q db prints the container ID. What the DETAIL line said in my tests:
Role "admin" does not exist.The user isn’t there at all. Jump to the user doesn’t exist.Connection matched file ".../pg_hba.conf" line 128: "host all all all scram-sha-256"The user exists and the password is wrong. If you’re sure you typed it right, check the changed Docker password and env file quotes.- Nothing new in the log at all. Your connection never reached this container. See another Postgres on the same port.
You changed the password in docker-compose.yml
This is the most common one. POSTGRES_PASSWORD only does anything the first time the container starts with an empty data folder. After that, the password lives in the data, which sits in a volume that outlives the container.
I started Compose with password first, ran docker compose down, changed it to second and ran docker compose up again. The new password failed and the old one still worked. The container log said why, on startup:
PostgreSQL Database directory appears to contain a database; Skipping initialization
To keep your data, change the password inside the running container. The container’s own psql connects over a local socket that doesn’t ask for a password:
docker exec <container> psql -U postgres -c "ALTER USER postgres PASSWORD 'second'"If the data doesn’t matter (a fresh dev database), delete the volume so the next start initializes again with the new password:
docker compose down -v
docker compose up -dThe user doesn’t exist
Same trap, different variable. I added POSTGRES_USER: admin to a Compose file whose volume already existed. Postgres skipped initialization, so admin was never created, and the client said password authentication failed for user "admin". Only the server log said Role "admin" does not exist.
Create the user yourself, inside the container:
docker exec <container> psql -U postgres -c "CREATE USER admin WITH PASSWORD 'second' SUPERUSER"Or, for a throwaway database, delete the volume as in the section above so the next start creates admin.
Quotes in an env file
My .env file had POSTGRES_PASSWORD="secret". The same file gave two different passwords:
| Started with | Password that worked |
|---|---|
docker compose with env_file: .env |
secret |
docker run --env-file .env |
"secret", quotes included |
Compose removes the quotes. docker run --env-file keeps them as part of the password.
Write the line without quotes when you use docker run --env-file:
POSTGRES_PASSWORD=secretIf the database already started with the quoted value, the quotes are now part of its password. Connect with them once and change it:
PGPASSWORD='"secret"' psql -h localhost -U postgres -c "ALTER USER postgres PASSWORD 'secret'"Another Postgres on the same port
The runner had its own Postgres 16 installed as a system service. With that running, docker run -p 5432:5432 postgres:18 didn’t start at all:
docker: Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint n1 (...): failed to bind host port for 0.0.0.0:5432
If you miss that line, everything after it goes to the other server. The password I gave the container failed, and the container log stayed empty because the connection never reached it.
Check that your container is actually running:
docker psMine wasn’t in the list. docker ps -a showed it with the status Created: it was made, but never started. Then either stop the other server, or publish the container on a different port and use that port in your connection string:
docker run -d -e POSTGRES_PASSWORD=dockerpw -p 5433:5432 postgres:18I tested this on Linux only. The setup that usually leads here on Windows (the PostgreSQL installer’s service on 5432 plus Docker Desktop) wasn’t part of this test.
Special characters in the connection URL
A password with @ or # in a URL doesn’t usually give this error. It breaks the URL first, so the error looks unrelated. With the password p@ss#w0rd written as-is:
| Client | Error |
|---|---|
| psql | could not translate host name "ss#w0rd@localhost" to address |
| node-postgres 8.23.1 | getaddrinfo EAI_AGAIN ss |
| Prisma 7.10.1 | P1001: Can't reach database server at `ss:5432` |
Everything after the @ in the password was read as the host.
Percent-encode those characters in the URL: @ is %40, # is %23.
postgresql://postgres:p%40ss%23w0rd@localhost:5432/postgresHow the error looks in Node and Prisma
The same failure, wrong password and all, comes out differently depending on the client:
- node-postgres:
password authentication failed for user "postgres", witherr.codeset to28P01. Use that code to catch it. - Prisma 7 CLI:
Error: P1000 Authentication failed against database server, the provided database credentials for `postgres` are not valid.
A missing password is a different error. When the password was undefined, for example from an environment variable that wasn’t loaded, node-postgres threw SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string before the server could check anything.
How this was tested
Ubuntu 24.04 on a GitHub Actions runner, October 18, 2026. The postgres:18 image (18.6) ran through Docker Compose v2 and docker run, and the runner’s preinstalled Postgres 16 played the second server. Each case was checked with psql 16.15, node-postgres 8.23.1 and Prisma 7.10.1. Every message above is the real output, from the test script.
— N.K., end of entry No.078