首页
/ Supabase Storage 深度解析:S3 兼容对象存储、图片变换与 RLS 权限体系的实现与自托管实战

Supabase Storage 深度解析:S3 兼容对象存储、图片变换与 RLS 权限体系的实现与自托管实战

2026-09-06 16:41:09作者:晏闻田Solitary

Supabase Storage 是一个与 Supabase Auth、Postgres 深度集成的开源对象存储服务,提供 S3 兼容 API、URL 参数式图片变换和基于 Postgres 行级安全策略(RLS)的细粒度访问控制。本文以 Supabase 主仓库(supa/supabase)中的产品文档为骨架,结合 docker/ 目录下的 Compose 编排、配置参考与端到端测试脚本,讲清 Storage 的五项核心能力、三种桶类型,以及在自托管环境中如何配置 file / S3 两种存储后端、接入 imgproxy 图片变换,并用 AWS CLI 完成 S3 协议验证。

一、Supabase Storage 是什么

官方文档(apps/www/content/md/storage.md)对 Supabase Storage 的定位是:一个开源对象存储,原生集成 Supabase Auth 与 Postgres,支持三种面向不同工作负载的桶类型——files(通用资产)、analytics(Apache Iceberg 表格式)、vector(向量嵌入)

与其他“数据库自带文件字段”的方案不同,Supabase Storage 的权限体系直接复用 Postgres:每个对象都会落入 storage schema 的元数据表中,访问控制通过写在 storage.objects 表上的 RLS 策略实现。这意味着授权逻辑可以用 SQL 表达,并且与 Auth 签发的 JWT(anon / authenticated / service_role 等角色)天然打通。

二、五大核心能力与仓库证据

文档列出了五项 Key Features。下面逐条展开,并给出当前仓库中可以查证的实现依据。

1. S3 兼容:标准 S3 API

Storage 暴露了一个 S3 协议端点,自托管场景下挂载在 /storage/v1/s3 路径。docker/tests/test-s3.sh 中可以看到端点的确切拼法:

S3_ENDPOINT="$BASE_URL/storage/v1/s3"

该测试脚本用 aws cli v2 完整走了一遍 S3 协议栈,验证了 13 个环节:

  1. ListBuckets —— 列出所有桶;
  2. CreateBucket —— 建桶后回查 ListBuckets 确认存在;
  3. PutObject —— 上传文件并校验 upload: 输出;
  4. ListObjectsV2 —— 确认对象出现在对象列表中;
  5. HeadObject —— 用 ContentLength 精确比对上传字节数;
  6. GetObject —— 下载后逐字节校验内容一致;
  7. CopyObject —— 服务端复制,再下载副本验证内容;
  8. DeleteObject —— 删除后确认对象不再出现;
  9. 分片上传 —— 构造 7 MB 随机文件触发 multipart 路径(>5 MB 即分片),并双向校验远端与下载尺寸;
  10. Range 请求 —— --range "bytes=0-4" 只取前 5 字节,验证部分 GET;
  11. 预签名 URL —— aws s3 presign 生成临时 URL 并用 curl 拉取内容比对;
  12. 鉴权失败路径 —— 故意使用无效凭证,确认返回 403/401 类错误;
  13. 清理 —— 递归删对象、删桶,并确认桶已消失。

运行方式(脚本头部注释即为准):

# 前置:已按 S3 配置启动自托管实例
docker compose -f docker/docker-compose.yml -f docker/docker-compose.s3.yml up -d
# .env 中需配置 S3_PROTOCOL_ACCESS_KEY_ID / S3_PROTOCOL_ACCESS_KEY_SECRET / REGION

sh docker/tests/test-s3.sh              # 默认 http://localhost:8000
sh docker/tests/test-s3.sh <base_url>   # 自定义地址

前置依赖是 aws CLI v2 和 jq,脚本会自动检查。凭证从 .env 读取,其中 S3_PROTOCOL_ACCESS_KEY_IDS3_PROTOCOL_ACCESS_KEY_SECRET 是静态 SigV4 密钥,供外部 S3 客户端(aws cli、rclone 等)签名请求使用。

配置参考(docker/CONFIG.md “Storage / S3 backend” 小节)中的关键变量:

变量 类型 说明
S3_PROTOCOL_ENABLED boolean 启用 S3 兼容 API,默认 true
S3_PROTOCOL_ACCESS_KEY_ID string 静态 SigV4 Access Key(单租户)
S3_PROTOCOL_ACCESS_KEY_SECRET string 静态 SigV4 Secret
S3_PROTOCOL_PREFIX string S3 路由的 URL 前缀,默认空
S3_PROTOCOL_ENFORCE_REGION boolean 是否拒绝 region 与 STORAGE_S3_REGION 不符的 SigV4 请求,默认 false
S3_ALLOW_FORWARDED_HEADER boolean 重建 SigV4 规范化 URL 时是否信任 Forwarded 头,默认 false

2. 全球 CDN:托管平台的分发能力

文档称托管版资产“从全球 285+ 城市低延迟分发”。需要说明边界:CDN 属于托管平台能力,自托管 Compose 编排中并未包含 CDN 层;自托管下对象由本地 file 后端或外接 S3 后端直出,可通过 RESPONSE_S_MAXAGE(默认 0 秒)为公开响应追加 CDN s-maxage 缓存寿命,为前置反向代理/CDN 预留缓存策略。

3. 图片变换:imgproxy 实时处理

Storage 的图片变换由独立的 imgproxy 服务承担。docker/docker-compose.yml 中可以看到完整的服务定义:

imgproxy:
  container_name: supabase-imgproxy
  image: darthsim/imgproxy:v3.30.1
  volumes:
    - ./volumes/storage:/var/lib/storage:z
  environment:
    IMGPROXY_BIND: ":5001"
    IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
    IMGPROXY_USE_ETAG: "true"
    IMGPROXY_AUTO_WEBP: ${IMGPROXY_AUTO_WEBP}
    IMGPROXY_MAX_SRC_RESOLUTION: 16.8

三个关键设计点:

  • imgproxy 与 storage 共享同一个数据卷(./volumes/storage:/var/lib/storage),因此可以直接按本地路径读取源图;
  • IMGPROXY_LOCAL_FILESYSTEM_ROOT: / 允许其按绝对路径寻址卷内文件;
  • storage 服务声明了对 imgproxydepends_ondocker-compose.yml),并启用变换:ENABLE_IMAGE_TRANSFORMATION: "true"IMGPROXY_URL: http://imgproxy:5001

相关配置变量(来自 docker/CONFIG.md “Image transformation” 小节):

变量 默认值 说明
IMAGE_TRANSFORMATION_ENABLED false 总开关(旧名 ENABLE_IMAGE_TRANSFORMATION
IMGPROXY_URL imgproxy 基址,启用变换时必填
IMAGE_TRANSFORMATION_LIMIT_MAX_SIZE 2000 允许请求的最大变换尺寸(px)
IMAGE_TRANSFORMATION_LIMIT_MIN_SIZE 1 允许请求的最小变换尺寸(px)
IMGPROXY_REQUEST_TIMEOUT 15 imgproxy 调用超时(秒)
IMGPROXY_HTTP_MAX_SOCKETS 5000 imgproxy HTTP agent 最大并发连接

对应文档中 “resize, crop, format conversion (WebP, AVIF) via URL parameters” 的能力:变换通过对象 URL 上的参数触发,IMGPROXY_AUTO_WEBP 控制是否自动转 WebP;变换路径还有配套的限流变量 RATE_LIMITER_*memory / redis 两种驱动,渲染路径默认上限 5 req/s),防止变换端点被滥用。

4. 行级安全:Postgres RLS 即访问策略

文档明确:授权机制是写在 storage.objects 表上的 Postgres RLS 策略,并与 Supabase Auth 集成。这一点在自托管编排里有直接对应:

  • storage 服务以专用角色 supabase_storage_admin 连接数据库:DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@...docker-compose.yml),该角色在 docker/volumes/db/roles.sql 中被创建并赋密;
  • 配置参考中定义了 Storage 识别的三个 Postgres 角色:DB_ANON_ROLE(默认 anon)、DB_AUTHENTICATED_ROLE(默认 authenticated)、DB_SERVICE_ROLE(默认 service_role)。请求携带的 JWT 决定 Storage 以哪个角色执行 SQL,RLS 策略据此放行或拒绝——这就是“Access policies written in SQL”的落地链路;
  • SERVICE_KEY(service_role JWT)可以绕过 RLS,因此必须按机密管理。

5. Dashboard:可视化文件管理

文档提到控制台支持拖拽上传、文件浏览、多选操作与文件夹管理。自托管下该能力由 Studio 提供:docker/docker-compose.ymlstudio 服务注入 SUPABASE_ANON_KEY / SUPABASE_SERVICE_KEY 后,即可通过网关(http://localhost:8000)以标准 Supabase 客户端方式操作存储对象,权限模型与托管版一致(同样受 RLS 约束)。

三、三种桶类型(Bucket Types)

文档将桶划分为三类,对应不同工作负载:

Files 桶:日常资产与用户内容

图片、视频、文档、PDF、归档包等通用资产,走全球 CDN(托管)或本地后端(自托管)分发,访问控制细化到 RLS 策略级别。这是绝大多数场景使用的默认桶类型。

Analytics 桶:开放表格式上的分析负载

面向 Apache Iceberg 等开放表格式的大规模分析负载,适合历史数据、时间序列、日志与 ETL 产出物,可选地经 Postgres 查询。

Vector 桶:AI/ML 工作负载

为 AI/ML 场景存储并索引向量嵌入,支持多种距离度量、元数据过滤与快速相似性查询,服务于 RAG 系统与 AI 搜索。

仓库中可以看到 vector 桶在 Storage 服务侧的配置面(docker/CONFIG.md “Tenant features (Vector)” 小节):

变量 默认值 说明
VECTOR_ENABLED false 是否启用 vector 桶支持
VECTOR_MAX_BUCKETS 10 每租户最多 vector 桶数
VECTOR_MAX_INDEXES 20 每个 vector 桶最多索引数
VECTOR_S3_BUCKETS 承载向量索引的 S3 桶列表(逗号分隔)
VECTOR_BUCKET_REGION vector 桶所在 AWS 区域

从这些变量可以推断:vector 索引的物理数据落在独立的 S3 桶上(VECTOR_S3_BUCKETS),与 files 桶的数据面解耦。

四、技术细节:协议、限制与上限汇总

综合文档 “Technical Details” 一节与仓库配置,关键参数如下:

维度 仓库证据
协议 S3 兼容 API test-s3.shS3_PROTOCOL_* 变量
CDN 托管版 285+ 边缘节点 文档声明;自托管由反向代理/CDN 自理
单文件上限(默认自托管) 50 MB(FILE_SIZE_LIMIT: 52428800 docker-compose.yml
单文件上限(托管付费计划) 500 GB,经 multipart 上传 文档声明
S3 分片大小 默认 16 MiB,低于 5 MiB 时自动向上钳制 STORAGE_S3_UPLOAD_PART_SIZE
TUS 断点续传 分片默认 50 MB,URL 有效期 1 h,路径 /upload/resumable TUS_PART_SIZE / TUS_URL_EXPIRY_MS / TUS_URL_PATH
图片变换 resize / crop / WebP / AVIF 格式转换 imgproxy 服务 + IMAGE_TRANSFORMATION_*
授权 storage.objects 表上的 Postgres RLS DB_*_ROLE 变量、roles.sql

上传限额相关的完整变量组(docker/CONFIG.md “Upload limits” 小节):

变量 默认值 说明
FILE_SIZE_LIMIT / UPLOAD_FILE_SIZE_LIMIT 必填 上传大小上限(字节);Compose 中设为 52428800(50 MB)
UPLOAD_FILE_SIZE_LIMIT_STANDARD 0(不限制) 非断点续传上传的单独上限
TUS_PART_SIZE 50(MB) TUS 分片大小
TUS_MAX_CONCURRENT_UPLOADS 500 最大并发 TUS 会话数
TUS_URL_EXPIRY_MS 3600000(1 小时) TUS 上传 URL 有效期
UPLOAD_SIGNED_URL_EXPIRATION_TIME 60(秒) 签名上传 URL 默认有效期
TUS_LOCK_TYPE postgres TUS 上传锁的存储后端(postgres / s3

另外,STORAGE_EMPTY_BUCKET_MAX(默认 200000)限制单次“清空桶”调用可删除的对象数;REQUEST_URL_LENGTH_LIMIT(默认 7500)约束对象 Key 的 URL 长度——两者都是大规模对象场景下容易踩到的隐性上限。

五、自托管实战:两种存储后端的完整配置

场景 A:默认 file 后端(本地文件系统)

docker/docker-compose.ymlstorage 服务(镜像 supabase/storage-api:v1.60.4)的默认编排即 file 后端:

storage:
  container_name: supabase-storage
  image: supabase/storage-api:v1.60.4
  depends_on:
    db:  { condition: service_healthy }
    rest: { condition: service_started }
    imgproxy: { condition: service_started }
  environment:
    ANON_KEY: ${ANON_KEY}
    SERVICE_KEY: ${SERVICE_ROLE_KEY}
    POSTGREST_URL: http://rest:3000
    AUTH_JWT_SECRET: ${JWT_SECRET}
    DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
    STORAGE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
    REQUEST_ALLOW_X_FORWARDED_PATH: "true"
    FILE_SIZE_LIMIT: 52428800
    STORAGE_BACKEND: file
    GLOBAL_S3_BUCKET: ${GLOBAL_S3_BUCKET}          # file 后端下为目录名
    FILE_STORAGE_BACKEND_PATH: /var/lib/storage
    TENANT_ID: ${STORAGE_TENANT_ID}
    REGION: ${REGION}
    ENABLE_IMAGE_TRANSFORMATION: "true"
    IMGPROXY_URL: http://imgproxy:5001
    S3_PROTOCOL_ACCESS_KEY_ID: ${S3_PROTOCOL_ACCESS_KEY_ID}
    S3_PROTOCOL_ACCESS_KEY_SECRET: ${S3_PROTOCOL_ACCESS_KEY_SECRET}
  volumes:
    - ./volumes/storage:/var/lib/storage:z

要点:

  • STORAGE_BACKEND: file 时,GLOBAL_S3_BUCKET 的语义变为目录名(注释原话:“S3 bucket when using S3 backend, directory name when using 'file'”),docker/.env.example 中示例值为 stub
  • 数据落在宿主机 docker/volumes/storage 下,随卷持久化;file 后端的 ETag 算法可用 STORAGE_FILE_ETAG_ALGORITHMmd5(默认)与 mtime 间切换;
  • REQUEST_ALLOW_X_FORWARDED_PATH: "true" 让 Storage 在计算对外公开 URL 时信任 X-Forwarded-Path,适配网关重写路径的部署;
  • 健康检查探测 http://storage:5000/status,服务默认端口 5000(admin 端口 5001)。

数据库侧配置(docker/CONFIG.md “Database” 小节)值得注意的默认值:DATABASE_MAX_CONNECTIONS 每租户连接池 20、DATABASE_STATEMENT_TIMEOUT 30000 ms、DATABASE_CONNECTION_TIMEOUT 3000 ms;若前置 Supavisor/PgBouncer,可用 DATABASE_POOL_URL 接管(此时 DATABASE_MAX_CONNECTIONS 被忽略)。

场景 B:S3 后端(MinIO / RustFS)

官方提供了两份可叠加的 Compose 覆盖文件,启动命令形如:

docker compose -f docker/docker-compose.yml -f docker/docker-compose.s3.yml up -d

docker/docker-compose.s3.yml 引入 MinIO 作为对象后端,编排链为:minio(健康检查 mc ready local)→ minio-createbucket(一次性容器:mc alias set supa-minio http://minio:9000 ... 后执行 mc mb --ignore-existing supa-minio/${GLOBAL_S3_BUCKET})→ storage(依赖建桶容器成功退出)。storage 服务的叠加环境变量:

STORAGE_BACKEND: s3
GLOBAL_S3_ENDPOINT: http://minio:9000
GLOBAL_S3_PROTOCOL: http
GLOBAL_S3_FORCE_PATH_STYLE: true
AWS_ACCESS_KEY_ID: ${MINIO_ROOT_USER}
AWS_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD}

几个必须理解的细节:

  • GLOBAL_S3_* 系列是 STORAGE_S3_* 的遗留别名,两者等价(见 docker/CONFIG.md);新写配置建议用 STORAGE_S3_*
  • MinIO 内部地址用路径式寻址FORCE_PATH_STYLE: true),这也是自托管 S3 兼容服务的常见要求;
  • 桶名由 GLOBAL_S3_BUCKET 统一,创建容器用 --ignore-existing 保证幂等,可重复 docker compose up
  • docker/docker-compose.rustfs.yml 是同一套模式的 RustFS(Rust 实现的 S3 兼容服务)变体,结构与 MinIO 版完全对称(rustfsrustfs-createbucketstorage),可视为 MinIO 的替换件,二者择一。

验证与回归

后端切换完成后,用 docker/tests/test-s3.sh 跑一遍 S3 协议冒烟测试是最直接的验收手段——它覆盖建桶、上传/下载/复制/删除、7 MB 分片上传、Range 请求、预签名 URL 与错误凭证拒绝共 13 项检查,全部通过才退出码 0。脚本末尾输出 === Results: N passed, 0 failed === 即为通过标准。

六、小结

  • Supabase Storage 的本质是**“Postgres 元数据 + RLS 权限 + 可插拔对象后端(file / S3)+ imgproxy 变换”** 的组合:权限面完全复用 Auth/Postgres,数据面可落在本地文件系统或任意 S3 兼容服务上;
  • 三种桶类型(files / analytics / vector)分别对应通用资产、Iceberg 分析表与向量嵌入负载,vector 能力由 VECTOR_* 变量族控制,默认关闭;
  • 自托管落地时,docker/docker-compose.yml 提供 file 后端开箱配置(50 MB 上限、imgproxy 变换、S3 协议端点密钥),docker/docker-compose.s3.yml / docker/docker-compose.rustfs.yml 提供 MinIO/RustFS 后端的叠加编排,docker/CONFIG.md 是逐变量的权威参考,docker/tests/test-s3.sh 则给出可复制的验收脚本;
  • 托管与自托管的能力边界要分清:全球 CDN、500 GB 分片上限等属于托管平台规格;自托管实例的实际上限由 FILE_SIZE_LIMITSTORAGE_S3_UPLOAD_PART_SIZE 等环境变量自行决定。

完整变量清单(Server / Database / JWT / S3 backend / File backend / Image transformation / Upload limits / Rate limiting / Webhook / Vector 各小节)请查阅 docker/CONFIG.md 的 “Storage” 章节;组件清单与生产安全注意事项(默认配置不可直接用于生产、需更换全部默认密钥)见 docker/README.md

登录后查看全文
热门项目推荐
相关项目推荐