Supabase配置与反向代理

自托管 Supabase 平台的完整部署与运维,涵盖 Docker 容器管理、Caddy 反向代理配置、安全认证修复和 REST API 接入。

概述

该项目是在云服务器上自托管 Supabase 全套服务的部署与运维实践。涉及13个 Docker 容器的镜像管理、Caddy 反向代理配置(包括 /pg 路径转发到 Kong 网关的关键配置)、Studio Dashboard 安全认证修复,以及 REST API 的 CRUD 接入指南。

项目还包括将所有 Supabase 容器镜像推送到 Azure Container Registry (externalacr.azurecr.io) 的镜像同步工作,生成了替换为 ACR 地址的 docker-compose.yml,并建立了自动化同步脚本 scripts/mirror_supabase_images.sh

在安全层面,发现并修复了 Studio Dashboard 无认证访问的严重问题——根因是 Caddy 直连 Studio 绕过了 Kong 网关,而 DASHBOARD_USERNAME/PASSWORD 实际上是给 Kong 用的。最终通过在 Caddy 层添加 basic auth 解决。

关键点

  • 容器版本管理:13个容器全部保持最新版 (2026.02.16),包括 Studio、Auth v2.186.0、PostgREST v14.5 等
  • Caddy 反向代理关键配置/pg 路径必须转发给 Kong (8000端口),不能用 handle_path(会 strip 掉 /pg 前缀导致 Kong 404)
  • 安全修复:Studio 自身无认证功能,必须在 Caddy 层加 basic auth 保护
  • RLS 全量开启:37张表全部开启 Row Level Security
  • Logto 集成评估:评估了 Logto OIDC 集成方案,结论是当前场景下意义不大,记为待办
  • REST API 双通道:外网 API 通过 /pg/rest/v1/ 访问(只需 apikey),Dashboard 通过 basic auth 保护

技术细节

Caddy 反向代理正确配置

db.dora.restry.cn {
    # 1. basic auth 保护 Dashboard
    basicauth /pg* {
        # API 路径不需要 basic auth,通过 apikey 认证
    }
 
    # 2. /pg 路径优先匹配,转发给 Kong(保留 /pg 前缀!)
    reverse_proxy /pg* supabase-kong:8000
 
    # 3. Dashboard 受 basic auth 保护
    basicauth * {
        supabase $2a$14$hash...
    }
    reverse_proxy * supabase-studio:3000
}

常见错误

  • ❌ 使用 handle_path /pg/* → 会自动去除 /pg 前缀,Kong 匹配不到路由
  • ❌ 通用 reverse_proxy 写在特殊路径前面 → Caddy 可能优先匹配通用规则
  • ❌ 依赖 Studio 的 DASHBOARD_USERNAME/PASSWORD → 这些是给 Kong 用的,Caddy 直连 Studio 时不生效

REST API CRUD

# 查询
curl 'http://localhost:8000/rest/v1/表名?select=*' \
  -H "apikey: YOUR_KEY" -H "Authorization: Bearer TOKEN"
 
# 插入
curl -X POST 'http://localhost:8000/rest/v1/表名' \
  -H "Content-Type: application/json" -d '{"name":"test"}'
 
# 更新
curl -X PATCH 'http://localhost:8000/rest/v1/表名?id=eq.1' \
  -d '{"value": 456}'
 
# 删除
curl -X DELETE 'http://localhost:8000/rest/v1/表名?id=eq.1'

RLS + 第三方 Auth (Logto) 方案

  • 方式 A:共享 JWT_SECRET(简单但耦合)
  • 方式 B:JWKS 验证(推荐,标准做法)
  • RLS policy 通过 current_setting('request.jwt.claims') 读取 JWT 中的 claims

时间线

  • 2026-03-06: 检查 Supabase Docker 更新状态,确认已是最新版 (2026.02.16)
  • 2026-03-06: 将13个容器镜像推送到 Azure Container Registry
  • 2026-03-06: 生成 ACR 版本的 docker-compose.yml
  • 2026-03-06: 发现并修复 /pg 路径反向代理配置问题(handle_path 错误)
  • 2026-03-16: 发现 Studio Dashboard 无认证访问安全漏洞
  • 2026-03-16: 修复认证问题:在 Caddy 层添加 basic auth
  • 2026-03-16: 37张表全部开启 RLS
  • 2026-03-16: 评估 Logto 集成方案,决定暂缓

相关页面

Clawline Gateway 数据库接入

以下信息来源于 gatewaybot 的 Mattermost DM 聊天记录(2026-03-18)。

接入方式

Clawline Gateway 是第一个接入自托管 Supabase 的业务应用。由于 PostgREST schema cache 未自动刷新(新建表后 /rest/v1/ 返回 404),采用 /pg/query SQL 直连端点作为替代方案。

数据表设计

采用 cl_ 前缀(用户指定 cl- 但建议用下划线避免 SQL 引号问题):

-- cl_channels
channel_id text PRIMARY KEY,
label text,
secret text,
token_param text NULL,
created_at timestamptz,
updated_at timestamptz
 
-- cl_channel_users
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
channel_id text REFERENCES cl_channels,
sender_id text,
chat_id text,
token text,
enabled boolean,
created_at timestamptz,
updated_at timestamptz

API 调用方式

# 通过 /pg/query 端点执行任意 SQL
curl -s -X POST "https://db.dora.restry.cn/pg/query" \
  -H "apikey: <service_role_key>" \
  -H "Authorization: Bearer <service_role_key>" \
  -H "Content-Type: application/json" \
  -d '{"query": "SELECT * FROM cl_channels"}'

经验教训

  • PostgREST 新建表后需要重启容器或执行 NOTIFY pgrst, 'reload schema' 才能刷新 schema cache
  • /pg/query 端点是自建实例额外暴露的,可执行任意 SQL,适合 DDL 和调试
  • 后续成功切换到 /pg/rest/v1 端点(PostgREST 重启后生效)

/pg/query → PostgREST 迁移事件(2026-03-18)

Supabase 重启后 /pg/query endpoint 认证失效(返回 401),但标准 PostgREST /pg/rest/v1/ 用同一个 service_role key 正常工作。这一事件导致 7 层连锁问题修复:

  1. Gateway backendpg/query → PostgREST REST API (/pg/rest/v1/)
  2. Portal server.js — 39 条 raw SQL → 创建 run_sql PL/pgSQL RPC 函数桥接
  3. run_sql 函数 — 用 CTE 包裹 DML (INSERT/UPDATE/DELETE RETURNING)
  4. Sync scripts — 6 个 sync.mjs 端口从 18820 → 3019
  5. Slug 检测 — sync.mjs slug 检测 bug 修复
  6. Dashboard collector — 也依赖 /pg/query,同步迁移
  7. 数据表消失 — Supabase 容器重启导致表消失,需确认 PostgreSQL volume 持久化

核心教训:从一开始就不该用 /pg/query(Studio 非标准 API),应该只用 PostgREST 标准接口。

表持久化问题

Supabase 容器重启后表会消失,根因是 Docker volume 未正确持久化 PostgreSQL data。需确认 docker-compose.yml/var/lib/postgresql/data 挂载到持久化 volume。

2026-04-16 更新:PostgreSQL 远程连接配置(最终方案:Docker 端口映射)

来源:giraffe(早期尝试)→ quokka(最终方案)频道 Clawline 聊天记录(2026-04-15~16)

背景

用户需要从外部远程连接 supabase-china VM 上的 PostgreSQL 数据库。VM 上 Supabase 的 PG 监听在 5432 和 6543(PgBouncer),但 Azure NSG 未放行这些端口。

演进过程

初始方案(giraffe,04-15~16 上半天)— iptables 转发:

  • 问题:5432 未映射到宿主机,容器内 PG(172.18.0.15:5432)不直接可达
  • 加 iptables DNAT 18543 → 172.18.0.15:5432 + DOCKER-USER ACCEPT + POSTROUTING MASQUERADE
  • 一度可连,但依赖手工 iptables 规则,脆弱且不干净

最终方案(quokka,04-16 13:12)— Docker 原生端口映射:

  • docker-compose.ymldb 服务下加 ports: - "18543:5432"
  • PG 容器内依旧监听 5432(不改 POSTGRES_PORT,避免 pg_isready 健康检查和 include 的 conf 出问题)
  • docker compose up -d db 重启
  • 删除所有之前加的 iptables DNAT / FORWARD / POSTROUTING / DOCKER-USER 规则
  • netfilter-persistent save 清理持久化

Azure NSG 规则(tiger-hostNSG)

规则端口协议方向备注
Allow-SSH-188228080TCPInbound⚠️ 名字与端口不对应(历史遗留)
AllowHTTP80TCPInbound
AllowHTTPS443TCPInbound
Allow-PG-1854318543TCPInboundPG 对外端口 ✅
all1882218822*InboundSSH ✅

子网 NSG (basicNsgvnet-chinanorth3-4-nic01) 无额外限制。

连接信息

Host:     tiger-host.chinanorth3.cloudapp.chinacloudapi.cn
Port:     18543
Database: postgres
User:     postgres

连接字符串:

postgresql://postgres:***@tiger-host.chinanorth3.cloudapp.chinacloudapi.cn:18543/postgres

数据库状态(04-16 13:03 quokka 汇总)

  • PostgreSQL 15.8(x86_64, GCC 13.2.0)
  • 启动时间 2026-03-17 10:32 UTC(稳定运行 ~30 天)
  • 活跃连接 40/100(idle 30,active 2)
  • 主库 postgres (16 MB) + _supabase
  • 缓存命中率极高(blks_hit >> blks_read),负载很轻
  • 最大表:cron.job_run_details 2.1 MB、public.registrations 160 KB、auth.users 152 KB
  • 有一个 Supabase Realtime 的 cainophile logical replication slot 持续运行(正常)
  • listen_addresses = *wal_level = logicalmax_connections = 100shared_buffers = 128 MB

经验教训

  • 不要改 PG 容器内部监听端口pg_isreadypostgresql.conf include 文件都预期 5432,改内部端口会导致 health check 长时间 unhealthy
  • 优先用 Docker 原生 port mapping,不用 iptables:更干净、可审计、与 compose 生命周期绑定
  • azchinaaz 的别名(切 AzureChinaCloud,配置目录 ~/.azure-china),连接 Azure China 订阅专用——易忘记
  • 本机 DNS 代理劫持(fake-ip)会导致 psql 测试连接假象