# FATAL: password authentication failed

> Fix "password authentication failed for user postgres" when connecting to SupaHost Postgres through the SSH tunnel. Covers wrong passwords, pooler user names and a local Postgres on port 5432.

Source: https://supahost.app/kb/password-authentication-failed · Updated 2026-11-11

## What you see

```
psql: error: connection to server at "127.0.0.1", port 5432 failed:
FATAL:  password authentication failed for user "postgres"
```

Node drivers, Prisma and other tools show the same message, sometimes with code `28P01`.

## Why it happens

The server you reached didn't accept that user name and password. Postgres gives the same message whether the password is wrong or the user doesn't exist, so a typo in the user name looks exactly like a wrong password.

On SupaHost there are three common reasons:

- **The password is from somewhere else.** After moving from Supabase Cloud or another host, the old database password is still in your `.env`.
- **The user name has a suffix.** Supabase Cloud pooler strings use `postgres.<project-ref>`. On SupaHost the user is plain `postgres`.
- **You're not talking to SupaHost at all.** If something on your machine already listens on port 5432 (a local Postgres or a Docker container), the tunnel can't bind that port and your client connects to the local server, which doesn't know your SupaHost password.

## How to fix it

1. Copy a fresh connection string from *Dashboard → your project → **Connect** tab → Database → Show connection string* (owner only, Business and Enterprise). It looks like:

   ```
   postgresql://postgres:<password>@127.0.0.1:5432/p_app_one
   ```

   Use it as-is. The user is `postgres`, the host is `127.0.0.1` and the database is `p_` plus your project name with hyphens as underscores.

2. Start the tunnel in its own terminal and read its output:

   ```bash
   ssh -N -L 5432:127.0.0.1:5432 supahost@acme.supahost.space
   ```

   If it prints `bind [127.0.0.1]:5432: Address already in use`, another server owns that port. Forward a different local port instead and change the port in your connection string to match:

   ```bash
   ssh -N -L 55432:127.0.0.1:5432 supahost@acme.supahost.space
   psql "postgresql://postgres:<password>@127.0.0.1:55432/p_app_one" -c "select current_database();"
   ```

3. Check that your app actually loads the value you think it does. Print the host, port, user and database (not the password) at startup. A stale `DATABASE_URL` in a shell profile or CI secret often wins over `.env`.

4. If the password contains characters such as `@`, `:`, `/` or `#`, they must be percent-encoded inside a URL. The string from the dashboard is already safe to paste.

## On SupaHost

- To rule out the network and the tunnel, SSH in and run `sudo supahost-psql app-one`. It opens psql as `postgres` inside the server without a password. If that works, the problem is on the client side.
- The `postgres` role belongs to the instance's Postgres server, so every project on the instance uses the same password. Their connection strings differ only in the database name.
- Don't change the password yourself with `alter role postgres password ...`. Studio signs in as the same role, and the connection string in the dashboard would no longer match. If you think the password has leaked, see [Resetting a project's database password](/kb/reset-database-password).
- On Builder and Pro there's no direct Postgres access, so no password to use. Work through the Data API, Studio's SQL editor or MCP `execute_sql` instead.

## Related

- [SSH access and direct Postgres](/docs/platform/ssh)
- [Connecting directly to Postgres (psql, pg_dump, ORMs)](/kb/direct-postgres-connection)
- [Connection refused or timeout when connecting to Postgres](/kb/connection-refused)
- [no pg_hba.conf entry for host](/kb/no-pg-hba-entry)
