Fixing Supabase IPv6 connection failures (ENETUNREACH)
Fixing Supabase IPv6 connection failures (ENETUNREACH)
You try to connect to a Supabase database over db.<project-ref>.supabase.co and the connection hangs or dies with:
connect ENETUNREACH 2600:1f18:...:5432
The address in the error is IPv6. That is the whole problem: the direct database host resolves to an IPv6 address, and your network cannot route IPv6.
Why this happens
Supabase moved direct database connections to IPv6. The direct host db.<ref>.supabase.co returns an AAAA (IPv6) record. If your client, your ISP, your CI runner, or your container network is IPv4-only, the OS cannot open a socket to that address and returns ENETUNREACH — "network is unreachable."
This is not an authentication problem and not a Supabase outage. The route does not exist. See ENETUNREACH for the decoded error.
Confirm it is IPv6
Resolve the host and look at what you get:
dig AAAA db.your-project-ref.supabase.co +short
# or
nslookup db.your-project-ref.supabase.co
If you get an IPv6 address (colons, e.g. 2600:1f18:...) and no reachable IPv4, that is the failure. Confirm you have no IPv6 route:
curl -6 https://ifconfig.co # fails or times out on IPv4-only networks
curl -4 https://ifconfig.co # returns your IPv4 address
Fix 1 — Use the connection pooler (recommended)
Supabase's pooler (Supavisor) is dual-stack and answers on IPv4. Switch the host from the direct database host to the pooler host:
# Direct (IPv6-only):
postgresql://postgres:PW@db.abcdefgh.supabase.co:5432/postgres
# Pooler (IPv4 reachable):
postgresql://postgres.abcdefgh:PW@aws-0-<region>.pooler.supabase.com:6543/postgres
Three things change with the pooler, and each one bites people:
- The username becomes
postgres.<project-ref>, not plainpostgres. Miss this and you get password authentication failed even with the right password. - The port is
6543for the transaction pooler (or5432on the session pooler host). - Transaction mode does not support session-level features like prepared statements or
LISTEN/NOTIFY. For a migration that is usually fine; for some workloads use the session pooler.
The connection string builder assembles the pooler URL from your project ref and region so you do not fat-finger the username. Background on why the pooler exists: connection pooler.
Fix 2 — Buy an IPv4 add-on
Supabase sells a dedicated IPv4 address for the direct host as a paid add-on. Enable it and db.<ref>.supabase.co gets an A record. This keeps the direct connection with session features, but costs money and is unnecessary if the pooler works for you.
Fix 3 — Give the client IPv6
If you control the network, enabling real IPv6 connectivity fixes it at the source. On many home and cloud networks you cannot, which is why the pooler is the usual answer. Supabase documents both paths under connecting to your database.
Which fix for which situation
- Local dev on an IPv4-only ISP → pooler.
- CI runner (GitHub Actions historically IPv4-only) → pooler.
- You need session-level Postgres features and control billing → IPv4 add-on.
- You run your own IPv6-capable infrastructure → fix the route.
A note on migrations
A migration opens connections to two projects: the source and the destination. Either host can be IPv6-only, so set both to pooler URLs if either network leg is IPv4-only. Too many parallel connections through the pooler can also raise too many connections — keep the migration's connection count modest. The connect source and connect destination docs show where each string goes.
FAQ
Why did my connection work last year and break now?
Supabase moved direct database connections to IPv6 by default. If your code used db.<ref>.supabase.co and your network is IPv4-only, the string that resolved to a reachable address before now resolves to an unreachable IPv6 address. Switch to the pooler host or enable the IPv4 add-on.
Is the pooler slower than a direct connection?
For request/response query workloads the difference is negligible, and the pooler often helps by reusing connections. The trade-off is not speed — it is that transaction-mode pooling drops session-level features like prepared statements and LISTEN/NOTIFY. Use the session pooler if you need those.
Does ENETUNREACH mean my password or project is wrong?
No. ENETUNREACH is a network-layer error raised before any authentication happens — your machine has no route to the IPv6 address. A wrong password produces password authentication failed instead, which only appears after the socket connects.
Founder, SupaMigrate
Builder of SupaMigrate. Works on Postgres, Supabase, and the unglamorous parts of database migration — schema replay, auth hash preservation, and connection pooling.
Related reading
Recreate a Postgres schema without pg_dump
No shell, no Postgres binary, no pg_dump. You can still rebuild a schema by reading the system catalogs and emitting CREATE statements in dependency order.
Migrate Supabase auth users without forcing a password reset
Supabase's admin API cannot set a bcrypt hash, so recreating users there forces a password reset. Insert into auth.users directly and keep every login working.
Migrate a Lovable Cloud project to your own Supabase
Lovable Cloud is a real Supabase Postgres backend. Here is what moves to a project you own, in what order, and the exact errors you hit if you get the order wrong.