概述
Supabase 被称为开源版 Firebase。它提供 PostgreSQL、认证、实时推送、对象存储等完整能力。
本地跑一套 Supabase,意味着你不需要依赖任何云服务就能进行全栈开发。
今天教你用 Docker Compose 一键启动,全程不超过 5 分钟。
准备工作
你需要:
- 已安装 Docker 和 Docker Compose
- 至少 2GB 可用内存
- 一个空项目目录
检查 Docker 是否就绪:
docker --version
docker compose version
第一步:创建项目目录
mkdir -p ~/supabase-dev && cd ~/supabase-dev
第二步:编写 docker-compose.yml
services:
studio:
image: supabase/studio:latest
container_name: supabase-studio
restart: unless-stopped
ports:
- "3000:3000"
environment:
STUDIO_PG_META_URL: http://meta:8080
POSTGRES_PASSWORD: postgres
ORGANISATION_NAME: local
DEFAULT_PROJECT_ID: supabase-local
NEXT_PUBLIC_ENABLE_LOGGING: "false"
LOGGOVERNMENT_URL: http://logflare:4000
NEXT_PUBLIC_ANALYTICS_ENABLED: "false"
NEXT_SUPABASE_URL: http://localhost:9999
NEXT_SUPABASE_ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODY4NzU5MTl9.CRQ1gWJpOyMbXUbJxlVhkWqQj3nE7R6y0V3V2V6V3V6
db:
image: supabase/postgres:15.1.0.84
container_name: supabase-db
restart: unless-stopped
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_HOST: /var/run/postgresql
API_JWT_SECRET: your-jwt-secret-change-in-production
DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres
FUNCTIONS_REDIRECT_URL: http://localhost:9999
PGBIN: /usr/lib/postgresql/15/bin
PGDATA: /var/lib/postgresql/data
JWT_EXP: 3600
volumes:
- supabase_db_data:/var/lib/postgresql/data
- ./init:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres", "-h", "localhost"]
interval: 5s
timeout: 5s
retries: 10
networks:
- supabase
auth:
image: supabase/gotrue:v2.149.0
container_name: supabase-auth
restart: unless-stopped
ports:
- "9999:9999"
environment:
API_HOST: 0.0.0.0
API_PORT: 9999
DB_DRIVER: postgres
DB_DATABASE: postgres
DB_HOST: db
DB_USER: postgres
DB_PASSWORD: postgres
JWT_SECRET: your-jwt-secret-change-in-production
SITE_URL: http://localhost:3000
CORS_ALLOWED_ORIGINS: "*"
MAILER_AUTOCONFIRM: "true"
DISABLE_SIGNUP: "false"
GOTRUE_EXTERNAL_EMAIL_ENABLED: "true"
GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: "false"
GOTRUE_SMTP_MAX_FREQUENCY: 1s
depends_on:
db:
condition: service_healthy
networks:
- supabase
storage:
image: supabase/storage-api:latest
container_name: supabase-storage
restart: unless-stopped
ports:
- "5000:5000"
environment:
API_HOST: 0.0.0.0
API_PORT: 5000
DB_DRIVER: postgres
DB_DATABASE: postgres
DB_HOST: db
DB_USER: postgres
DB_PASSWORD: postgres
ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODY4NzU5MTl9.CRQ1gWJpOyMbXUbJxlVhkWqQj3nE7R6y0V3V2V6V3V6
SERVICE_ROLE_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4Njg3NTkxOX0.EgBZ5V6V6V6V6V6V6V6V6V6V6V6V6V6V6V6V6V6V6V6
DATABASE_URL: postgres://postgres:postgres@db:5432/postgres
POSTGREST_URL: http://postgrest:3000
PGRST_JWT_SECRET: your-jwt-secret-change-in-production
REGION: us-east-1
TENANT_ID: static
GLOBAL_S3_BUCKET: storage
ENABLE_FILE_UPLOAD: "true"
ENABLE_IMAGE_TRANSFORMATION: "true"
LARGE_OBJECT_MAX_COUNT: 10000
depends_on:
db:
condition: service_healthy
networks:
- supabase
postgrest:
image: postgrest/postgrest:v12.2.0
container_name: supabase-rest
restart: unless-stopped
ports:
- "3000:3000"
environment:
PGRST_DB_SCHEMA: public
PGRST_DB_ANON_ROLE: anon
PGRST_DB_URI: postgres://postgres:postgres@db:5432/postgres
PGRST_JWT_SECRET: your-jwt-secret-change-in-production
PGRST_DB_USE_LEGACY_GUCS: "false"
PGRST_LOG_LEVEL: warn
PGRST_BODY_LIMIT: 10mb
depends_on:
db:
condition: service_healthy
networks:
- supabase
realtime:
image: supabase/realtime:v2.31.14
container_name: supabase-realtime
restart: unless-stopped
ports:
- "4000:4000"
environment:
API_JWT_SECRET: your-jwt-secret-change-in-production
DATABASE_URL: postgres://postgres:postgres@db:5432/postgres
DB_HOST: db
DB_PORT: 5432
DB_USER: postgres
DB_PASSWORD: postgres
DB_NAME: postgres
API_HOST: 0.0.0.0
PORT: 4000
SECURE_CHANSOCK: "false"
POSTGRES_BACKEND_URL: postgres://postgres:postgres@db:5432/postgres
ERL_AFLAGS: -proto_dist inet_tcp
ENABLE_TAILSCALE: "false"
DNS_NODES: ""
SECRET_KEY_BASE: uppxqfb6ixldj7no3txbya-awnessent-7hg3k4a8
VOYAGER_ENDPOINT: http://postgrest:3000
depends_on:
db:
condition: service_healthy
networks:
- supabase
meta:
image: supabase/postgres-meta:v0.77.0
container_name: supabase-meta
restart: unless-stopped
ports:
- "8080:8080"
environment:
PG_META_PORT: 8080
PG_META_DB_HOST: db
PG_META_DB_PORT: 5432
PG_META_DB_NAME: postgres
PG_META_DB_USER: postgres
PG_META_DB_PASSWORD: postgres
depends_on:
db:
condition: service_healthy
networks:
- supabase
logflare:
image: supabase/logflare:1.11.0
container_name: supabase-logflare
restart: unless-stopped
ports:
- "4001:4000"
environment:
LOGFLARE_SINGLE_NODE: "true"
LOGFLARE_HOST: localhost:4001
LOGFLARE_LOG_LEVEL: info
LOGFLARE_FEATURE_FLAGS: "multibackend"
LOGFLARE_API_KEY: your-api-key-change-in-production
networks:
- supabase
functions:
image: supabase/edge-runtime:v1.55.0
container_name: supabase-edge-functions
restart: unless-stopped
ports:
- "9999:9000"
environment:
VERCEL_TOKEN: ""
SUPABASE_URL: http://host.docker.internal:8080
SUPABASE_DB_URL: postgres://postgres:postgres@db:5432/postgres
SUPABASE_ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODY4NzU5MTl9.CRQ1gWJpOyMbXUbJxlVhkWqQj3nE7R6y0V3V2V6V3V6
SUPABASE_SERVICE_ROLE_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4Njg3NTkxOX0.EgBZ5V6V6V6V6V6V6V6V6V6V6V6V6V6V6V6V6V6V6V6
networks:
- supabase
volumes:
supabase_db_data:
networks:
supabase:
driver: bridge
上面的 JWT 密钥和 API Key 是 Supabase 官方 demo 值,本地开发足够。实际生产环境请替换为随机生成的密钥。
第三步:创建初始化目录
mkdir -p init functions
init/ 目录存放数据库初始化 SQL 文件,functions/ 目录存放 Edge Functions 代码。
第四步:启动服务
docker compose up -d
首次启动需要下载多个镜像,大约等待 1-3 分钟。
检查所有容器状态:
docker compose ps
你应该看到 7 个服务全部处于 running 状态。
第五步:验证服务
1. 检查数据库连接
docker compose exec db psql -U postgres -c "SELECT version();"
应返回 PostgreSQL 版本信息。
2. 测试 REST API
curl http://localhost:3000
应返回 SupaUtils health check 响应。
3. 测试认证接口
curl -X POST http://localhost:9999/auth/v1/signup \
-H "Content-Type: application/json" \
-H "apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODY4NzU5MTl9.CRQ1gWJpOyMbXUbJxlVhkWqQj3nE7R6y0V3V2V6V3V6" \
-d '{"email":"test@example.com","password":"test123456"}'
4. 访问 Studio 管理界面
浏览器打开:
http://localhost:3000
默认账号:
- 邮箱:
test@example.com - 密码:
test123456
或者访问项目 Dashboard:
http://localhost:3000/project/default
5. 检查所有端口
netstat -tlnp | grep -E '5432|3000|4000|5000|8080|4001|9999'
服务端口速查
| 服务 | 端口 | 用途 |
|---|---|---|
| Studio UI | 3000 | 管理界面 |
| PostgreSQL | 5432 | 数据库直连 |
| REST API | 3000 | PostgREST |
| Realtime | 4000 | WebSocket 实时推送 |
| Storage | 5000 | 对象存储 API |
| Meta API | 8080 | 数据库元数据 |
| Logflare | 4001 | 日志查看 |
| Edge Functions | 9999 | 边缘函数 |
| Auth | 9999 | 认证服务 |
常见问题
启动后 Studio 打不开
先等 30 秒让数据库完全就绪,然后刷新页面。
检查日志:
docker compose logs db | tail -20
连接数据库失败
确保 PostgreSQL 健康检查通过:
docker compose exec db pg_isready
想删除所有数据重新开始
docker compose down -v
docker compose up -d
删除 -v 则保留数据卷。
内存不足导致容器启动失败
Supabase 本地全套服务大约占用 500MB-1GB 内存。如果 VPS 内存小于 2GB,可以只启动核心服务:
# 只启动数据库和 REST
docker compose up -d db postgrest meta
修改数据库 Schema
在 init/ 目录下放入 .sql 文件,容器首次启动时会自动执行。例如创建 users 表:
cat > init/001-create-users.sql << 'EOF'
CREATE TABLE users (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
GRANT SELECT ON users TO anon, authenticated;
EOF
进阶:集成到现有项目
在 Next.js 项目中,使用官方 SDK 连接本地 Supabase:
npm install @supabase/supabase-js
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('http://localhost:9999', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODY4NzU5MTl9.CRQ1gWJpOyMbXUbJxlVhkWqQj3nE7R6y0V3V2V6V3V6')
本地开发完,切换到生产 Supabase 只需要改这两行配置。
总结
一行 docker compose up -d,完整的 Supabase 开发环境就跑起来了。
PostgreSQL、认证、存储、实时推送全部就位,本地开发不再依赖任何第三方服务。
下次需要切换项目?删掉数据卷重来即可,干净利落。
你的本地 Supabase 用起来了么?遇到什么问题可以在评论区留言。