Supabase 資料庫連線踩坑:Docker 容器內 DNS 無法解析直連字串,改用 Connection Pooler 解決

在 Docker 容器內連接 Supabase 資料庫時,直連字串的 DNS 解析失敗。本文說明原因並介紹如何改用 Supavisor Connection Pooler 解決。

問題現象

在 Docker 容器內運行 FastAPI 應用程式,嘗試連接 Supabase PostgreSQL 資料庫時,出現以下錯誤:

socket.gaierror: [Errno -2] Name or service not known
Code language: CSS (css)

使用的連線字串是 Supabase 提供的「直連格式」:

postgresql://postgres:[PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres
Code language: JavaScript (javascript)

奇怪的是,同一個連線字串在本機開發環境可以正常連接,但在 Docker 容器內就失敗了。

問題原因

經過排查,發現問題出在 IPv6 與 DNS 解析

Supabase 直連預設使用 IPv6

根據 Supabase 官方文檔:

Direct connections use IPv6 by default. If your environment doesn’t support IPv6, use Supavisor session mode or get the IPv4 add-on.

直連字串 db.[PROJECT-REF].supabase.co 解析出來是 IPv6 地址

Docker 容器預設不支援 IPv6

大多數 Docker 環境預設沒有啟用 IPv6 網路,導致:

  1. DNS 查詢 db.[PROJECT-REF].supabase.co 時只查詢 A 記錄(IPv4)
  2. 但 Supabase 直連主機只有 AAAA 記錄(IPv6)
  3. 結果就是 Name or service not known

可以用以下指令驗證:

# 在容器內執行
nslookup db.[PROJECT-REF].supabase.co
# 回應:NXDOMAIN(找不到)

# 但 API endpoint 可以正常解析
nslookup [PROJECT-REF].supabase.co
# 回應:正常的 IPv4 地址
Code language: CSS (css)

解決方案:使用 Supavisor Connection Pooler

Supabase 提供的 Supavisor 是一個連線池(Connection Pooler),有以下優點:

  1. 支援 IPv4:Supavisor 端點同時支援 IPv4 和 IPv6
  2. 連線池管理:自動管理資料庫連線,減少連線開銷
  3. 更好的擴展性:適合 Serverless 和容器化環境

Supavisor 兩種模式

模式Port適用場景
Session Mode5432持久連線,行為類似直連
Transaction Mode6543Serverless,每次查詢後釋放連線

取得 Supavisor 連線字串

  1. 開啟 Supabase Dashboard
  2. 進入 Project SettingsDatabase
  3. 找到 Connection string 區塊
  4. 選擇 Session modeTransaction mode
  5. 複製 URI 格式

連線字串格式會像這樣:

# Session Mode (port 5432)
postgresql://postgres.[PROJECT-REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:5432/postgres

# Transaction Mode (port 6543)
postgresql://postgres.[PROJECT-REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres
Code language: PHP (php)

直連 vs Pooler 連線字串比較

# 直連(IPv6,Docker 內可能失敗)
postgresql://postgres:[PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres

# Pooler Session Mode(IPv4,推薦)
postgresql://postgres.[PROJECT-REF]:[PASSWORD]@aws-0-ap-southeast-1.pooler.supabase.com:5432/postgres
Code language: PHP (php)

注意兩個關鍵差異:

  1. 主機名db.[REF].supabase.coaws-0-[REGION].pooler.supabase.com
  2. 用戶名postgrespostgres.[PROJECT-REF]

實際修改

原本的 .env(會失敗)

DATABASE_URL=postgresql://postgres:mypassword@db.[PROJECT-REF].supabase.co:5432/postgres
Code language: JavaScript (javascript)

修改後的 .env(成功)

DATABASE_URL=postgresql://postgres.[PROJECT-REF]:mypassword@aws-1-ap-southeast-1.pooler.supabase.com:5432/postgres
Code language: JavaScript (javascript)

修改後重啟應用程式,資料庫連線立即成功。

其他解決方案

如果你確實需要使用直連,還有以下選項:

方案 A:啟用 Docker IPv6

在 Docker 的 daemon.json 中啟用 IPv6:

{
  "ipv6": true,
  "fixed-cidr-v6": "2001:db8:1::/64"
}
Code language: JSON / JSON with Comments (json)

但這需要你的網路環境支援 IPv6,且設定較為複雜。

方案 B:購買 IPv4 Add-on

Supabase Pro 方案以上可以購買 IPv4 Add-on,讓直連也支援 IPv4。

但對於開發環境來說,使用免費的 Supavisor 是更實際的選擇。

總結

連線方式IPv4 支援適用環境
直連僅 IPv6(預設)支援 IPv6 的 VM、本機開發
Supavisor SessionIPv4 + IPv6Docker、K8s、Serverless
Supavisor TransactionIPv4 + IPv6Serverless、Edge Functions

建議:在容器化環境(Docker、Kubernetes)中,一律使用 Supavisor Connection Pooler 的連線字串,避免 IPv6 相關的連線問題。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *