FATAL: password authentication failed for user "postgres" — and the password you typed is the one in your docker run line or compose file. The postgres image reads POSTGRES_PASSWORD only when it creates a new database. Your volume still holds the database made with the old password. Set the new one inside PostgreSQL, or throw the volume away.

The one-line fix is docker exec into the container and ALTER USER postgres WITH PASSWORD '...'. The psql inside the container connects through the local socket, which needs no password, so it works even while every connection from outside fails.

The error

$ docker run -d --name fix3-dpg-pw -e POSTGRES_PASSWORD=new-dev-password -p 127.0.0.1:15432:5432 -v fix3-dpg-pwdata:/var/lib/postgresql postgres:18
$ docker run --rm --name fix3-dpg-cli -e PGPASSWORD=new-dev-password postgres:18 psql -h host.docker.internal -p 15432 -U postgres -c "SELECT current_user;"
psql: error: connection to server at "host.docker.internal" (192.168.65.254), port 15432 failed: FATAL:  password authentication failed for user "postgres"

The same connection from a .NET app with Npgsql, first line of the exception:

Unhandled exception. Npgsql.PostgresException (0x80004005): 28P01: password authentication failed for user "postgres"

And the server's side, in docker logs:

2026-10-02 01:30:24.227 UTC [36] FATAL:  password authentication failed for user "postgres"
2026-10-02 01:30:24.227 UTC [36] DETAIL:  Connection matched file "/var/lib/postgresql/18/docker/pg_hba.conf" line 128: "host all all all scram-sha-256"

Why it happens

The image's entrypoint script runs before the server. On an empty data directory it runs initdb and sets the postgres password from POSTGRES_PASSWORD. On a directory that already holds a database it prints PostgreSQL Database directory appears to contain a database; Skipping initialization and starts the server; the variable is never read again. So the container's environment says new-dev-password, while the role inside the volume still has the first one. The old password kept working the whole time.

You only notice from outside. The pg_hba.conf the image writes trusts the local socket and 127.0.0.1 inside the container, and requires scram-sha-256 for everything else (line 128 above). That is why docker exec ... psql logs in fine while your app, or any client that comes in through the published port, is refused.

The fix

docker exec fix3-dpg-pw psql -U postgres -c "ALTER USER postgres WITH PASSWORD 'new-dev-password';"

It printed ALTER ROLE. The .NET app then printed Connected as postgres, psql over TCP connected with the new password, and the old password was refused. Nothing else in the database changes, and no restart is needed.

If the database holds nothing you need, recreating the volume works too, because the entrypoint initialises again:

docker stop fix3-dpg-pw
docker rm fix3-dpg-pw
docker volume rm fix3-dpg-pwdata
docker run -d --name fix3-dpg-pw -e POSTGRES_PASSWORD=new-dev-password -p 127.0.0.1:15432:5432 -v fix3-dpg-pwdata:/var/lib/postgresql postgres:18

The log showed PostgreSQL init process complete; ready for start up. and the new password connected. Removing the volume deletes the database, so use this only on a throwaway development container.

How it was reproduced

Docker Desktop 4.91.0 (engine 29.8.0) on Windows 11, image postgres:18 (18.4), port 15432 published on 127.0.0.1. The first container ran with POSTGRES_PASSWORD=first-dev-password on a named volume; it was stopped, removed and started again on the same volume with new-dev-password. Clients: psql 18.4 in a second container connecting through host.docker.internal, and a .NET 10 console app (SDK 10.0.401, Npgsql 10.0.3) on the host. Both failed with the new password and connected with the old one until the ALTER USER. Names beginning fix3-dpg- are this test's own container and volume; use yours.

Frequently asked

Why does postgres in Docker say password authentication failed when the password is right?
Because POSTGRES_PASSWORD is applied only when the image creates a new database. If the container reuses a volume made earlier, the postgres role still has the password from that first start, whatever the variable now says.
How do I change the postgres password in a Docker container?
Run docker exec on the container with psql -U postgres -c and an ALTER USER postgres WITH PASSWORD statement. psql inside the container uses the local socket, which the image trusts, so it does not need the old password.
Why does docker exec psql work but my app gets 28P01?
The image's pg_hba.conf trusts connections from inside the container and requires a scram-sha-256 password for every other address. Your app connects through the published port, so its password is checked; docker exec psql is not.

More decoded errors in the Fixes category. The same entrypoint check explains another first-start failure: Database is uninitialized and superuser password is not specified.