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。