DEV Community

Varun Krishnan
Varun Krishnan

Posted on Originally published at dbdiagramr.space

Supabase Connection String: IPv6, ENOTFOUND, and the Transaction Pooler Fix

The short version

Supabase uses IPv6 by default. Most local dev environments and many hosting providers don't support IPv6, so DNS resolution fails with ENOTFOUND or hangs with ETIMEDOUT. The fix: use the transaction pooler connection string (port 6543) instead of the direct connection (port 5432). It routes through Supabase's IPv4-compatible proxy.

The error

You're running your app locally and you see:

Error: getaddrinfo ENOTFOUND db.xxxxxxxxx.supabase.co
    at GetAddrInfoReq.onlookup [as oncomplete] (node:dns:1107:26)
Enter fullscreen mode Exit fullscreen mode

Or:

Error: connect ETIMEDOUT 2600:1ff:f000:e000::1:5432
Enter fullscreen mode Exit fullscreen mode

The first one means DNS can't resolve the hostname. The second means DNS resolved it to an IPv6 address, but the connection timed out because your network doesn't route IPv6.

Why it happens

Supabase assigns each project a hostname like db.xxxxxxxxx.supabase.co. This hostname resolves to an IPv6 address by default. If your machine or hosting provider doesn't support IPv6, the connection fails.

Environment IPv6 support
macOS (most setups) Yes
Linux (most setups) Yes
Windows (most setups) No
Vercel Yes
Railway Yes
Render Yes
Cloudflare Workers No
Local Docker Depends on config

The three connection strings

Supabase gives you three connection strings in the dashboard (Settings → Database):

1. Direct connection (port 5432)

postgresql://postgres:[YOUR-PASSWORD]@db.xxxxxxxxx.supabase.co:5432/postgres
Enter fullscreen mode Exit fullscreen mode
  • Full Postgres protocol support
  • Supports prepared statements, SET commands, LISTEN/NOTIFY
  • Uses IPv6 -- may fail locally or on some hosts

2. Transaction pooler (port 6543)

postgresql://postgres.[YOUR-PROJECT-REF]:[YOUR-PASSWORD]@aws-0-[region].pooler.supabase.com:6543/postgres
Enter fullscreen mode Exit fullscreen mode
  • Routes through Supabase's connection pooler (PgBouncer)
  • IPv4-compatible -- works everywhere
  • Does NOT support prepared statements or SET commands
  • Best for most web apps

3. Session pooler (port 6543, mode=session)

postgresql://postgres.[YOUR-PROJECT-REF]:[YOUR-PASSWORD]@aws-0-[region].pooler.supabase.com:6543/postgres?pgbouncer=true&session_mode=true
Enter fullscreen mode Exit fullscreen mode
  • Same as transaction pooler but preserves session state
  • Supports SET commands but not prepared statements
  • Use this if you need SET search_path or similar

Which one to use

Use case Connection string
Local dev (Node.js, Python, etc.) Transaction pooler (6543)
Vercel / Railway / Render Transaction pooler (6543)
Cloudflare Workers Transaction pooler (6543)
Prisma ORM Transaction pooler (6543)
Drizzle ORM Transaction pooler (6543)
Django Session pooler (6543 + session_mode)
Laravel Transaction pooler (6543)
pg_dump / pg_restore Direct connection (5432)
Migrations (Prisma, Knex) Direct connection (5432)

The Prisma gotcha

Prisma uses prepared statements by default. The transaction pooler doesn't support prepared statements, so you'll get:

Error: prepared statement "stmt_1" does not exist
Enter fullscreen mode Exit fullscreen mode

Fix: Add ?pgbouncer=true to your connection string, or use the direct connection for migrations and the pooler for runtime queries.

# .env
DATABASE_URL="postgresql://postgres.xxx:password@aws-0-us-east-1.pooler.supabase.com:6543/postgres?pgbouncer=true"
Enter fullscreen mode Exit fullscreen mode

Quick diagnosis

Test if IPv6 works

# If this returns an address, IPv6 works
dig AAAA db.xxxxxxxxx.supabase.co

# If this returns an address, IPv4 works
dig A db.xxxxxxxxx.supabase.co
Enter fullscreen mode Exit fullscreen mode

Test the connection

# Direct connection (may fail with IPv6)
psql "postgresql://postgres:password@db.xxxxxxxxx.supabase.co:5432/postgres"

# Transaction pooler (should always work)
psql "postgresql://postgres.xxx:password@aws-0-us-east-1.pooler.supabase.com:6543/postgres"
Enter fullscreen mode Exit fullscreen mode

FAQ

Why does my app work on Vercel but not locally?

Vercel supports IPv6. Your local machine might not. Use the transaction pooler (6543) locally and you'll get the same behavior as production.

Can I force IPv4 on the direct connection?

Yes. Add ?sslmode=require&ip=4 to the direct connection string, but this isn't officially supported and may break. Use the pooler instead.

What's the difference between transaction and session pooler?

Transaction pooler resets the connection after each transaction (faster, more scalable). Session pooler preserves session state (needed for SET commands, LISTEN/NOTIFY). Most apps should use transaction pooler.

Does the pooler affect performance?

Minimal. The pooler adds ~1-2ms of latency per query. For most web apps, this is negligible compared to network latency and query execution time.

Can I use both pooler and direct connections?

Yes. Use the pooler for runtime queries (better scalability) and the direct connection for migrations and admin tasks (full protocol support).

Top comments (0)