password authentication failed for user "postgres"
What it means
This Postgres error (SQLSTATE 28P01) means the server rejected your credentials before opening the connection. On Supabase it is almost always a wrong DB password, the wrong username on the pooler host (it must be postgres.<project-ref>, not postgres), or a password with special characters that was not URL-encoded in the connection URI.
Why it happens
28P01 is raised by Postgres during the authentication handshake, before any query runs. The server received a username/password pair it does not accept. In a Supabase migration there are three concrete causes.
-
Wrong database password. The DB password is not your Supabase account password and not shown after project creation. If you never saved it, you do not have it — you have to reset it.
-
Direct-connection username used on the pooler host. Supabase exposes two endpoints. The direct connection (
db.<project-ref>.supabase.co:5432) uses the usernamepostgres. The connection pooler (...pooler.supabase.com:6543or:5432) requires the tenant-qualified usernamepostgres.<project-ref>. Sendpostgresto the pooler and it rejects the login with 28P01, even though the password is correct. -
A password with special characters, not URL-encoded. When the password is embedded in a URI like
postgresql://postgres:p@ss/w0rd@host:5432/postgres, the raw@and/are parsed as URI delimiters. The client sends a truncated password and the server rejects it.
How to fix it
First, confirm which endpoint you are hitting. The port tells you: 5432 on db.<ref>.supabase.co is direct; 6543 (or 5432) on *.pooler.supabase.com is the pooler.
Use the correct username for the host. On the pooler, the username must include the project ref:
# Direct connection (port 5432, db.<ref>.supabase.co)
postgresql://postgres:[PASSWORD]@db.abcdefghijklmno.supabase.co:5432/postgres
# Pooler (port 6543, aws-0-<region>.pooler.supabase.com)
postgresql://postgres.abcdefghijklmno:[PASSWORD]@aws-0-eu-central-1.pooler.supabase.com:6543/postgres
URL-encode reserved characters in the password. If the password is p@ss:w0rd/x, encode it before putting it in the URI:
| Char | Encoded |
|---|---|
@ | %40 |
: | %3A |
/ | %2F |
? | %3F |
# | %23 |
& | %26 |
So p@ss:w0rd/x becomes p%40ss%3Aw0rd%2Fx:
postgresql://postgres:p%40ss%3Aw0rd%2Fx@db.abcdefghijklmno.supabase.co:5432/postgres
The connection string builder does this encoding for you and picks the right username per host.
Reset the password if you don't have it. In the Supabase dashboard: Settings → Database → Database password → Reset. Choose a password without reserved characters to avoid the encoding step entirely. Then test the new credentials with psql:
psql "postgresql://postgres:[NEW_PASSWORD]@db.abcdefghijklmno.supabase.co:5432/postgres" -c "select current_user, current_database();"
A clean login returns:
current_user | current_database
--------------+------------------
postgres | postgres
If that still fails with 28P01, the password is wrong — reset again and copy it exactly, with no leading or trailing whitespace.
How to prevent it
- Store the password the moment you reset it. Supabase does not display it again. Keep it in a secret manager, not in a URI in your shell history.
- Prefer a password without reserved characters (
@ : / ? # & %). It removes the entire class of URL-encoding bugs. - Match username to host every time. Pooler →
postgres.<project-ref>. Direct →postgres. When in doubt, copy the exact string from Settings → Database → Connection string and pick the tab for the endpoint you want. - Test connectivity with a trivial query first (
select 1;) before running a migration. A 28P01 during batched inserts wastes far more time than catching it up front. If the connection succeeds but a later step fails on IPv6 or connection limits, see ipv6-enetunreach and too-many-connections.
SupaMigrate validates both the source and destination connection strings — username, host, and password encoding — before it starts the migration, so 28P01 surfaces at connect time rather than mid-run.
Frequently asked questions
- Why do I get 28P01 only on the pooler but not on the direct connection?
- The pooler (Supavisor / port 6543) requires the tenant-qualified username postgres.<project-ref>. The direct host (db.<ref>.supabase.co, port 5432) uses plain postgres. Same password, different username. Using postgres on the pooler fails authentication.
- My password is correct but it still fails. What else could it be?
- If the password contains @, :, /, ?, #, or & and you put it inside a connection URI, those characters break parsing. URL-encode them: @ becomes %40, : becomes %3A, / becomes %2F. Or reset the password to one without reserved characters.
- Does resetting the database password log out my app users?
- No. The database password is separate from Supabase Auth. Resetting it in Settings → Database changes the Postgres role password only; your application end users and their sessions are unaffected.
Related errors