BunshipBunship
运维

环境变量与配置

Bunship Web + API 运行所需的环境变量清单。

本页是当前仓库 必需环境变量 的权威清单。

运行时 public config 现在优先使用 PUBLIC_* 命名。旧的 NEXT_PUBLIC_* 仍作为 fallback 兼容;PUBLIC_AUTH_PROVIDER 是构建期变量,用来选择 auth provider 构建分支。

代码参考:

  • web schema:apps/ship/src/env.ts
  • auth runtime:packages/auth/src/server.ts, packages/auth-clerk/src/server.ts
  • api runtime:apps/ship-api/src/server.tsapps/ship-api/src/index.ts
  • Trigger runtime sync:apps/ship-api/trigger.config.ts
  • Docker build:Dockerfile, .github/workflows/publish-ghcr.yml

环境归属矩阵

先按“谁读取这个变量”归类,不要把 build、容器 runtime、Trigger runtime 混在一起。

场景变量归属是否存业务 secret说明
Docker build / GHCR publishGIT_COMMIT_SHA, VERCEL_ENV, PUBLIC_AUTH_PROVIDER,以及可选 BuildKit secret file infisical_env只决定镜像版本和 auth provider 构建分支。Dockerfile 已设置 SKIP_ENV_VALIDATION=1,默认不需要业务 runtime env。
GHCR Docker build 的 Infisical 控制变量INFISICAL_IDENTITY_ID, INFISICAL_PROJECT_SLUG, INFISICAL_ENV_SLUG, INFISICAL_DOMAIN, INFISICAL_SECRET_PATH, INFISICAL_RECURSIVE, INFISICAL_INCLUDE_IMPORTS只用于 publish-ghcr.yml 通过 OIDC 找到 Infisical 项目/环境/folder,把结果写成 .infisical.docker.env 并作为 Docker BuildKit secret 传入。
Trigger deploy CITRIGGER_ACCESS_TOKEN, TRIGGER_PROJECT_IDTRIGGER_ACCESS_TOKEN 是 secret只用于 deploy-trigger.yml 部署任务;不会作为 Trigger 任务运行时变量同步。
Web/API 容器 runtimeSITE_URL, DATABASE_URL, auth、email、payment、storage、OAuth、public runtime config 等容器启动后由部署平台注入。OAuth client ID/secret 只属于这里。
Web/API Trigger dispatchTRIGGER_SECRET_KEY, 可选 TRIGGER_PROJECT_ID让 Web/API 选择 Trigger adapter 并向 Trigger.dev 投递任务。不是 Docker build 变量。
Trigger 云端 task runtimeDATABASE_URL, S3_*, PUBLIC_S3_URL_BASE, provider API keys, AI tuning vars任务跑在 Trigger.dev 云端,需要单独配置/同步;不需要 OAuth client ID。
BullMQ runtimeREDIS_URL自建持久 worker 队列使用;和 Trigger runtime 二选一。

最小必需(Web + API)

缺失这些变量会导致启动失败,或影响认证/支付/存储等核心流程。

变量是否必需使用方说明
PUBLIC_AUTH_PROVIDER是(默认 better-authweb, apibetter-authclerk
DATABASE_URLweb, api, authPostgres 连接串
SITE_URLweb, api, auth站点主域名,用于 SEO、邮件、认证链接和绝对地址
TRUSTED_ORIGINS建议api, authBetter Auth 与 CORS 使用的额外可信来源(逗号分隔)
PUBLIC_SERVER_URL可选web浏览器请求的运行时 API origin 覆盖项;仅当公开 API origin 与 SITE_URL 不一致时设置;有 VERCEL_ENV 时目标必须暴露 /api/v1,否则必须暴露 /v1。旧变量 NEXT_PUBLIC_SERVER_URL 仍兼容。
ADMIN_EMAIL_LISTweb, auth管理员邮箱列表(逗号分隔)
EMAIL_FROMauth邮件发件人(验证码/重置密码/OTP)
RESEND_API_KEYweb/auth邮件服务密钥
S3_ENDPOINTweb, api存储服务地址
S3_REGIONweb, api存储区域
S3_ACCESS_KEYweb, api存储访问密钥
S3_SECRET_KEYweb, api存储访问密钥密文
S3_BUCKETweb, apiBucket 名称
PUBLIC_S3_URL_BASEweb, api公网对象地址前缀。旧变量 NEXT_PUBLIC_S3_URL_BASE 仍兼容。
STRIPE_SECRET_KEYweb, apiStripe API Key
STRIPE_WEBHOOK_SECRETweb, apiStripe Webhook 签名密钥
S_GITHUB_PERSONAL_ACCESS_TOKENweb, api管理端/功能页用的 GitHub API Token
CLOUDFLARE_ACCOUNT_ID是(web schema)web当前环境校验要求

上传 Provider 选择

上面的 S3_* 是基础存储配置。Better Upload 服务端路由还可以从 settings 动态选择 provider,再回退到环境变量。这是服务端配置机制,不是用户自助切换 provider 的界面。

Provider 解析顺序:

  1. settings 表里的 UPLOAD_PROVIDER_OVERRIDES[userId]
  2. settings 表里的 UPLOAD_PROVIDER_DEFAULT
  3. BETTER_UPLOAD_PROVIDER
  4. cloudflare
变量是否必需使用方说明
BETTER_UPLOAD_PROVIDER否(默认 cloudflareapi没有 settings 覆盖时的 env 兜底 provider。支持值:cloudflareawsbackblazetigrisdigitaloceanminiowasabicustom。无效值会被忽略并回退到 cloudflare
BETTER_UPLOAD_<PROVIDER>_BUCKETapiprovider 专属 bucket 兜底,例如 BETTER_UPLOAD_AWS_BUCKETBETTER_UPLOAD_CUSTOM_BUCKET
BETTER_UPLOAD_BUCKETapi通用 bucket 兜底,优先级高于 S3_BUCKET

凭证和客户端字段优先从 settings 的 client 对象读取,再回退到 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEYS3_ACCESS_KEY / S3_SECRET_KEY 等环境变量别名。Path-style 或 provider 专属 client 行为请写在 settings 的 client 对象里,不再作为全局环境变量配置。

按用户切 provider 的 settings 示例和 bucket 解析细节,请看S3 协议存储

Better Auth 专用(当 PUBLIC_AUTH_PROVIDER=better-auth

变量是否必需使用方说明
BETTER_AUTH_SECRETauth/apiBetter Auth 签名密钥
BETTER_AUTH_URL否(默认 SITE_URLauth/api当 auth/API 对外域名与站点域名不同时,用于覆盖公开地址
AUTH_SECRETweb/authWeb 环境校验必需
OAUTH_GITHUB_CLIENT_ID是(开启 GitHub OAuth 时)web/authGitHub OAuth
OAUTH_GITHUB_CLIENT_SECRET是(开启 GitHub OAuth 时)web/authGitHub OAuth
OAUTH_GOOGLE_CLIENT_ID建议authGoogle OAuth 服务端 Client ID
OAUTH_GOOGLE_CLIENT_SECRET建议authGoogle OAuth 服务端 Secret
PUBLIC_OAUTH_GOOGLE_CLIENT_ID是(开启 Google OAuth 时)webGoogle One Tap 与 Google 登录按钮需要。旧变量 NEXT_PUBLIC_OAUTH_GOOGLE_CLIENT_ID 仍兼容。

支付提供商扩展

变量是否必需使用方说明
STRIPE_SECRET_KEY是(使用 Stripe 时)web, apiStripe 密钥
STRIPE_WEBHOOK_SECRET是(使用 Stripe 时)web, apiStripe Webhook 签名密钥
CREEM_API_KEY条件web, apiCreem API Key(使用 Creem 时必填)
CREEM_WEBHOOK_SECRET条件web, apiCreem Webhook 签名密钥
PAYMENT_PROVIDER_DEFAULT否(默认 stripeapi无激活配置时的默认支付提供商

Clerk 专用(当 PUBLIC_AUTH_PROVIDER=clerk

变量是否必需使用方说明
PUBLIC_CLERK_PUBLISHABLE_KEYwebClerk 公钥。旧变量 NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY 仍兼容。
CLERK_SECRET_KEYapi/authClerk 服务端密钥
CLERK_WEBHOOK_SECRETapi/api/v1/webhook/clerk 使用

API 运行与部署变量

API 前缀需要区分挂载入口:

  • Next.js ship 路由入口:/api/v1/*
  • 独立 ship-api 服务:/v1/*

如果不配置 PUBLIC_SERVER_URL,Web 会走 SITE_URL 下的同源 /api/v1。只有浏览器要访问的 API origin 和 SITE_URL 不一致时才设置它。有 VERCEL_ENV 时,浏览器请求会走 ${PUBLIC_SERVER_URL}/api/v1;没有 VERCEL_ENV 时,会走 ${PUBLIC_SERVER_URL}/v1,用于独立 ship-api 或 Worker 风格 API origin。

变量是否必需说明
PORT否(默认 9001API 监听端口
APP_ENV建议development / production 等环境标识
NODE_ENV建议Node 运行环境
JWT_SECRET建议API JWT 签名密钥(虽有 fallback,不建议用于生产)
JWT_EXPIRATION否(默认 7dAPI JWT 有效期
GIT_COMMIT_SHA日志构建信息
BUILD_TIME日志构建信息
CROSS_SUB_DOMAIN可选跨子域 Cookie 域名

生产地址变量

更新 Infisical、容器运行时 env 或主机环境变量时,以这张表为准。

变量线上处理是否必需说明
SITE_URL保留/新增站点 canonical origin,例如 https://app.example.com
TRUSTED_ORIGINS保留/新增建议Better Auth 与 CORS 使用的可信来源,多个值逗号分隔
BETTER_AUTH_URL按需保留默认回退到 SITE_URL;只有 auth/API 对外 origin 不同时才设置
VERCEL_ENV保留/默认Docker build arg 默认 production;Cloudflare/Vercel runtime 建议显式保持 production
APP_ENV保留建议业务/运行环境标识
PUBLIC_SERVER_URL按需设置仅当浏览器访问的 API origin 与 SITE_URL 不一致时设置;有 VERCEL_ENV 时目标暴露 /api/v1,否则暴露 /v1;旧变量 NEXT_PUBLIC_SERVER_URL 仍兼容
NEXT_PUBLIC_SITE_URL删除已替换为 SITE_URL
NEXT_PUBLIC_API_PREFIX删除已替换为 shared 常量
CORS_ORIGIN删除已替换为 TRUSTED_ORIGINS
API_PREFIX删除前缀不再由 env 控制

GitHub Variables for Infisical OIDC(GHCR Docker build)

这些值写在 GitHub Repository Variables,不写在代码里。入口: GitHub repo > Settings > Secrets and variables > Actions > Variables > New repository variable

这些变量用于 publish-ghcr.ymlLoad Infisical secrets。它们只是让 GitHub Actions 通过 OIDC 找到 Infisical project/env/folder,不是业务运行时 secret。Trigger deploy 不需要这组 Infisical OIDC 变量。

publish-ghcr.yml 已经声明 permissions.id-token: write,所以 OIDC auth method not found for identity 通常不是 GitHub Actions 没权限发 OIDC token,而是 Infisical 里这个 identity 还没有配置 OIDC Auth。默认创建的 machine identity 可能带的是 Universal Auth;用于 GitHub Actions 时,需要打开 INFISICAL_IDENTITY_ID 对应的 identity,把 Universal Auth 移除/替换为 OIDC Auth。

GitHub Variable从 Infisical 哪里拿说明
INFISICAL_IDENTITY_IDInfisical > Project > Access Control > Machine Identities,打开给 GitHub Actions 用的 identity;或从 Organization Settings > Access Control > Identities 找到同一个 identity不是 secret,是 OIDC machine identity 的公开 ID。Authentication 里必须有 OIDC Auth;OIDC Discovery URL 和 Issuer 都用 https://token.actions.githubusercontent.com。本 workflow 使用 GitHub Environment,main 的 Subject 建议锁到 repo:<owner>/<repo>:environment:Productiondev 锁到 repo:<owner>/<repo>:environment:staging;只有不使用 environment 的 workflow 才用 repo:<owner>/<repo>:ref:refs/heads/main
INFISICAL_PROJECT_SLUGInfisical > Bunship project > Project Settings > General > Project Slug必须和 workflow 访问的 Infisical project slug 完全一致。
INFISICAL_ENV_SLUGInfisical > Bunship project > Project Settings > Environments,复制生产环境 slug例如 prod / production,区分大小写,必须和 Infisical 环境 slug 完全一致。
INFISICAL_DOMAIN当前 Infisical 实例入口域名Infisical Cloud 默认是 https://app.infisical.com,可以不填;自建实例才需要填自建域名。
INFISICAL_SECRET_PATHInfisical project 里的 secret folder path可选,默认 /。如果 folder 是根目录下的 env,填 /env。这不是 INFISICAL_ENV_SLUG
INFISICAL_RECURSIVEGitHub Repository Variables可选,默认 false。只有 Docker build 输入放在 INFISICAL_SECRET_PATH 下面的子目录里才填 true

排障顺序:

  1. OIDC auth method not found for identity:到 Project > Access Control > Machine Identities 打开 INFISICAL_IDENTITY_ID 对应 identity,确认 Authentication 里有 OIDC Auth;如果还是 Universal Auth,删除/替换为 OIDC Auth。
  2. Subject 不匹配:因为 workflow 使用 GitHub Environment,生产分支默认 subject 是 repo:<owner>/<repo>:environment:Production,不是 repo:<owner>/<repo>:ref:refs/heads/main
  3. Folder with path '/' ... was not foundINFISICAL_ENV_SLUGINFISICAL_SECRET_PATH 不匹配。folder 在根目录下叫 env 时,INFISICAL_SECRET_PATH 要填 /env

workflow 入口:

文件用途
.github/workflows/publish-ghcr.ymlGHCR Docker image build/push;执行 Load Infisical secrets,把结果作为 BuildKit secret file 传给 Dockerfile。
.github/workflows/deploy-trigger.ymlTrigger.dev deploy;不走 Infisical OIDC,使用 GitHub Secrets/Variables 中的 Trigger deploy 与同步变量。

GHCR 构建输入

私有 GHCR 镜像由 .github/workflows/publish-ghcr.yml 使用 Dockerfile 构建。工作流会把 Better Auth 镜像发布到默认 tag(latest、分支 tag、<branch>-<sha>),把 Clerk 镜像发布到 clerk-latest / clerk-<sha>。运行时 env 和运行时 public config 都由容器启动后的环境变量注入,因此同一个 provider 专用镜像可以复用到不同环境。

Build args:

变量是否必需说明
GIT_COMMIT_SHAworkflow 使用 GitHub SHA;默认 local
VERCEL_ENV默认 production
PUBLIC_AUTH_PROVIDER由 workflow matrix 提供默认 tag 使用 better-authclerk-* tag 使用 clerk

BuildKit secret file:workflow 会把 Infisical 读取结果写入 .infisical.docker.env,并以 infisical_env 传给 Docker build。Dockerfile 只在构建时读取这份文件,不会把文件复制进镜像层。这里可以放 VERCEL_ENVPUBLIC_* 这类构建期/公开配置;不应该要求 SITE_URLDATABASE_URLS3_*、auth secrets、Stripe keys、email keys 这些运行时变量。Docker build 会设置 SKIP_ENV_VALIDATION=1,这些业务变量属于容器 runtime env。

OAuth 变量不属于 Docker build。OAUTH_GITHUB_CLIENT_ID / OAUTH_GOOGLE_CLIENT_ID 只在启用 GitHub/Google 登录时,作为 Web/API auth runtime 配置使用,并且要和对应的 *_CLIENT_SECRET 一起在容器运行时注入。Trigger/AI runtime 不需要这两个变量。

Trigger dispatch 变量也不属于 Docker build。TRIGGER_SECRET_KEY 只应该放在 Web/API 容器 runtime,用于选择 Trigger adapter 并向 Trigger.dev 投递任务;不要放进 GHCR build secrets。

容器运行时 public config:

变量是否必需旧变量 fallback说明
PUBLIC_S3_URL_BASENEXT_PUBLIC_S3_URL_BASE公网 CDN/对象地址前缀
PUBLIC_OAUTH_GOOGLE_CLIENT_ID启用 Google OAuth 时必需NEXT_PUBLIC_OAUTH_GOOGLE_CLIENT_IDGoogle One Tap / 登录按钮的公共 Client ID
PUBLIC_CLERK_PUBLISHABLE_KEY启用 Clerk 时必需NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYClerk 公钥
PUBLIC_SERVER_URLNEXT_PUBLIC_SERVER_URLSITE_URL 不一致时的可选 API origin 覆盖;有 VERCEL_ENV 时目标暴露 /api/v1,否则暴露 /v1
PUBLIC_GA_IDNEXT_PUBLIC_GA_IDGoogle Analytics
PUBLIC_UMAMI_DATA_IDNEXT_PUBLIC_UMAMI_DATA_IDUmami 统计

可选功能变量

变量功能
UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKENKV/Redis 功能
TRIGGER_SECRET_KEY, TRIGGER_PROJECT_IDWeb/API runtime 投递 Trigger.dev 任务;不要放进 Docker build
REDIS_URLBullMQ/CMS 队列、AI 限流/准入指标;使用 BullMQ 时必填
CRON_SECRET/api/cron/ai-cleanup cron 路由鉴权
PUBLIC_GA_ID, PUBLIC_UMAMI_DATA_ID分析统计;旧的 NEXT_PUBLIC_* 名称仍兼容
NEXT_PUBLIC_APP_VERSION, VERCEL_GIT_COMMIT_SHAUI 版本展示
BETTER_UPLOAD_PROVIDER上传 provider 兜底;详见上方上传 provider 小节
OPENAI_API_KEY, OPENAI_API_BASE管理端 AI 命令/协作
REPLICATE_API_TOKENReplicate 服务
KIE_API_KEY, KIE_API_BASE_URLKIE 服务
FAL_API_KEYFAL 服务

本地 .env 示例(Better Auth)

PUBLIC_AUTH_PROVIDER=better-auth

DATABASE_URL=postgresql://postgres:postgres@localhost:5432/bunship
BETTER_AUTH_SECRET=replace-with-long-random-secret
AUTH_SECRET=replace-with-long-random-secret

SITE_URL=http://localhost:3001
TRUSTED_ORIGINS=http://localhost:3001,http://localhost:9001

ADMIN_EMAIL_LIST=admin@example.com
EMAIL_FROM=Bunship <noreply@example.com>
RESEND_API_KEY=re_xxx

OAUTH_GITHUB_CLIENT_ID=xxx
OAUTH_GITHUB_CLIENT_SECRET=xxx
OAUTH_GOOGLE_CLIENT_ID=xxx
OAUTH_GOOGLE_CLIENT_SECRET=xxx
PUBLIC_OAUTH_GOOGLE_CLIENT_ID=xxx

S3_ENDPOINT=https://s3.example.com
S3_REGION=auto
S3_ACCESS_KEY=xxx
S3_SECRET_KEY=xxx
S3_BUCKET=bunship
PUBLIC_S3_URL_BASE=https://cdn.example.com

STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
CREEM_API_KEY=creem_xxx
CREEM_WEBHOOK_SECRET=creem_whsec_xxx
PAYMENT_PROVIDER_DEFAULT=stripe
S_GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxx
CLOUDFLARE_ACCOUNT_ID=xxx

本地 .env 示例(Clerk)

PUBLIC_AUTH_PROVIDER=clerk

DATABASE_URL=postgresql://postgres:postgres@localhost:5432/bunship

SITE_URL=http://localhost:3001
TRUSTED_ORIGINS=http://localhost:3001,http://localhost:9001

PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
CLERK_SECRET_KEY=sk_test_xxx
CLERK_WEBHOOK_SECRET=whsec_xxx

ADMIN_EMAIL_LIST=admin@example.com
EMAIL_FROM=Bunship <noreply@example.com>
RESEND_API_KEY=re_xxx

S3_ENDPOINT=https://s3.example.com
S3_REGION=auto
S3_ACCESS_KEY=xxx
S3_SECRET_KEY=xxx
S3_BUCKET=bunship
PUBLIC_S3_URL_BASE=https://cdn.example.com

STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
S_GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxx
CLOUDFLARE_ACCOUNT_ID=xxx