Skip to content
Latchkey

Neon "password authentication failed" (pooled vs direct) in CI

Neon rejected the login because the password in DATABASE_URL does not match the role, or the host is the wrong endpoint variant. Neon issues two connection strings per branch: a direct one and a -pooler one, and each has its own host.

What this error means

A migration or test step fails connecting to Neon with "FATAL: password authentication failed for user 'X'". The same string may work locally but not in CI, or vice versa, because the pooled and direct hosts differ.

psql
psql: error: connection to server at "ep-cool-name-123456.us-east-2.aws.neon.tech"
failed: FATAL:  password authentication failed for user "neondb_owner"

Common causes

CI stored a connection string that does not match the branch role

The DATABASE_URL secret holds a password for a different branch or a rotated role. Neon branch endpoints each carry their own role password, so a copied string from another branch fails.

The pooled and direct hosts were mixed up

Neon gives a direct host and a -pooler host. Using the pooled password against the direct host (or an outdated pooled string) surfaces as an auth failure rather than a host error.

How to fix it

Read the exact connection string from the Neon API for this branch

  1. Fetch the branch connection URI at CI time instead of storing a stale one.
  2. Set it as DATABASE_URL for the migration and test steps.
  3. Use the -pooler URI for app connections and the direct URI for migrations.
Terminal
# get the pooled URI for the branch created for this PR
neonctl connection-string "pr-${{ github.event.number }}" \
  --project-id "$NEON_PROJECT_ID" --pooled >> conn.txt

Store only the project id and API key, derive URLs in CI

Keep NEON_API_KEY and NEON_PROJECT_ID as secrets and generate the connection string per run so the password always matches the current branch role.

.github/workflows/ci.yml
env:
  NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
  NEON_PROJECT_ID: ${{ secrets.NEON_PROJECT_ID }}

How to prevent it

  • Generate the Neon connection string at CI time rather than pinning a static secret.
  • Keep the pooled URI for app pools and the direct URI for migrations.
  • Rotate the branch role and refresh the derived URL together, never by hand.

Frequently asked questions

What causes "Neon "password authentication failed""?
The DATABASE_URL secret holds a password for a different branch or a rotated role. Neon branch endpoints each carry their own role password, so a copied string from another branch fails.
How do I fix Neon "password authentication failed"?
Read the exact connection string from the Neon API for this branch

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card