Dokploy(生产环境)
使用 Dokploy 拉取 GHCR 应用与迁移镜像部署 Makeugcads 到自有 VPS
Dokploy 是一个自托管 PaaS,可在 VPS 上管理 Docker Compose 服务,提供自动 HTTPS、滚动部署和环境变量管理 Web UI。生产环境推荐由 GitHub Actions 构建并推送 GHCR 应用镜像、迁移镜像与 RBAC 初始化镜像,Dokploy 只负责拉取镜像、运行一次性部署任务、注入运行时环境变量并启动容器。
部署路径概览
本仓库的生产路径为:
- 推送代码到
main或dev - GitHub Actions 构建四个镜像:
ghcr.io/chenhaonan-eth/ugcad-web:<tag>ghcr.io/chenhaonan-eth/ugcad-agents:<tag>ghcr.io/chenhaonan-eth/ugcad-migrations:<tag>ghcr.io/chenhaonan-eth/ugcad-rbac-init:<tag>
- Dokploy 使用
compose.yml拉取镜像,连接 Dokploy Databases 中的 PostgreSQL,运行一次性migrations/rbac-init,并启动redis、agents、web
PR 只执行构建校验,不推送镜像。生产 Dokploy 不在服务器上构建源码,只拉取 GHCR 已构建镜像。
前置要求
- VPS:至少 4 核 CPU、8 GB 内存、80 GB 磁盘(推荐 Ubuntu 24.04)
- 一个域名,DNS A 记录指向服务器 IP
- LLM API 密钥(OpenAI 或兼容接口)
- GHCR access,可拉取仓库镜像
- GitHub repository variables 已配置生产构建期公开变量
- Dokploy Databases 中已准备 PostgreSQL 实例
Step 1 — 安装 Dokploy
SSH 登录服务器,执行:
curl -sSL https://dokploy.com/install.sh | sh安装完成后,访问 http://YOUR_SERVER_IP:3000 进入 Dokploy UI,创建管理员账号。
Step 2 — 配置 GitHub Actions 构建变量
web 镜像的 Next.js 客户端 bundle 会在构建期固化公开变量。请在 GitHub 仓库 Settings → Secrets and variables → Actions → Variables 中配置:
NEXT_PUBLIC_APP_URL=https://yourdomain.com
NEXT_PUBLIC_APP_NAME=Makeugcads
NEXT_PUBLIC_THEME=huxibo
NEXT_PUBLIC_APPEARANCE=system
NEXT_PUBLIC_DEFAULT_LOCALE=enNEXT_PUBLIC_APP_URL 必须在 GitHub repository variables 中于构建期注入,不能只在 Dokploy 运行时设置。更换域名后必须重新构建并发布新的 web 镜像。
上线前必须完成密钥历史审计,确认 Git 历史、Actions 日志和镜像层中没有提交过真实 .env、API Key、数据库密码或 AUTH_SECRET。密钥历史审计是上线前阻塞项,未完成不得部署生产流量。
Step 3 — 准备 Compose 文件
仓库根目录 compose.yml 已切换为生产拉取 GHCR 四镜像:
migrations:
image: ${GHCR_IMAGE_PREFIX:-ghcr.io/chenhaonan-eth/ugcad}-migrations:${IMAGE_TAG:-latest}
rbac-init:
image: ${GHCR_IMAGE_PREFIX:-ghcr.io/chenhaonan-eth/ugcad}-rbac-init:${IMAGE_TAG:-latest}
agents:
image: ${GHCR_IMAGE_PREFIX:-ghcr.io/chenhaonan-eth/ugcad}-agents:${IMAGE_TAG:-latest}
web:
image: ${GHCR_IMAGE_PREFIX:-ghcr.io/chenhaonan-eth/ugcad}-web:${IMAGE_TAG:-latest}如需部署 fork 或指定版本,在 Dokploy 环境变量中设置:
GHCR_IMAGE_PREFIX=ghcr.io/chenhaonan-eth/ugcad
IMAGE_TAG=latest也可以把 IMAGE_TAG 设置为 GitHub Actions 生成的分支 tag 或 sha tag,用于灰度或回滚。同一次部署中,web、agents、migrations、rbac-init 必须使用同一个 tag。
Step 4 — 在 Dokploy 创建项目
- 在 Dokploy UI 中点击 Create Project
- 输入项目名称(如
makeugcads) - 进入项目,点击 Create Service → Docker Compose
- 连接 GitHub 仓库读取
compose.yml,或确保服务器部署目录包含完整仓库文件 - 确认 Dokploy 部署模式为拉取镜像,不要在服务器上构建源码
生产 PostgreSQL 由 Dokploy Databases 管理,并通过标准 PostgreSQL URL 注入到 Compose 服务。默认生产部署不启用 Compose 内置 postgres;该服务只保留在 legacy-postgres profile 中,用于短期回滚窗口或本地排障。
本次生产路径不引入 Supabase Auth、Supabase Storage、Supabase Realtime、自托管 Supabase 或自托管 Neon。数据库切换只使用 Dokploy Databases 提供的 PostgreSQL 与标准 PostgreSQL URL。
Step 5 — 配置 Dokploy 运行时环境变量
先在 Dokploy Databases 中创建一个 PostgreSQL 服务,再在该服务里准备两个 logical database:一个存业务数据,一个存 LangGraph runtime 状态。应用和数据库位于同一 Dokploy 项目/网络时,优先复制 Dokploy 提供的 internal PostgreSQL URL;保留 Dokploy 提供的 sslmode 等查询参数,并对用户名或密码里的特殊字符做 URL encode。
DATABASE_URL指向业务 logical database,供migrations、rbac-init和web使用,承载用户、认证、RBAC、品牌工作区和 UGC 业务表。LANGGRAPH_DATABASE_URL指向 LangGraph runtime logical database,供agents作为 checkpoint/state/store 使用,不与业务表混用。
在 Dokploy 服务设置中添加以下环境变量。真实凭据只能保存到 Dokploy 环境变量/密钥管理中,不要提交到 .env、.env.production、Compose override、MDX 文档、GitHub Actions 日志或镜像层:
# ── 镜像选择 ────────────────────────────────────────────
GHCR_IMAGE_PREFIX=ghcr.io/chenhaonan-eth/ugcad
IMAGE_TAG=latest
# ── 基础设施 ──────────────────────────────────────────
DATABASE_URL=postgresql://<user>:<password>@<host>:5432/<business-db>
LANGGRAPH_DATABASE_URL=postgresql://<user>:<password>@<host>:5432/<langgraph-db>
REDIS_PASSWORD=<强随机密码>
# ── Web 运行时 ─────────────────────────────────────────
AUTH_SECRET=<openssl rand -base64 32>
NEXT_PUBLIC_APP_URL=https://yourdomain.com
# ── 内部服务认证 ────────────────────────────────────────
AGENTS_SHARED_SECRET=<随机字符串>
BRAND_WORKSPACE_ACTIVE_BRAND_ID=brand-live
# ── LLM ───────────────────────────────────────────────
OPENAI_API_KEY=sk-your-key
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4o
# ── 可选 ──────────────────────────────────────────────
RBAC_ADMIN_EMAIL=
ANTHROPIC_API_KEY=
TAVILY_API_KEY=
MINERU_API_KEY=
TIKHUB_API_TOKEN=
LANGSMITH_API_KEY=
LANGSMITH_TRACING=false
LANGSMITH_TRACING_V2=false
LANGCHAIN_TRACING=false
LANGCHAIN_TRACING_V2=false禁用 tracing 时,请在 Dokploy 中保持以上四个 tracing 开关为 false 或不设置。残留的 LANGCHAIN_TRACING_V2=true / LANGCHAIN_TRACING=true 仍可能触发 LangSmith 上传 trace,并在 agents 日志中出现 Failed to send multipart request ... 403 Forbidden。
Dokploy 中的 NEXT_PUBLIC_APP_URL 仍用于运行时 AUTH_URL 等服务端配置,但客户端可见地址以 GitHub Actions 构建镜像时注入的 repository variable 为准。
Dokploy-managed PostgreSQL 仍是自托管数据库。切换前至少保留一次可恢复备份,并为生产库规划定期备份、恢复演练、磁盘容量监控、连接数/错误日志监控。
Step 6 — 部署
在 Dokploy UI 中点击 Deploy,Dokploy 将:
- 从 GHCR 拉取
web、agents、migrations与rbac-init镜像 - 启动
redis - 运行一次性
migrations服务,把 SQL migration 应用到DATABASE_URL - 在
migrations成功后运行一次性rbac-init服务,初始化 RBAC 基础数据 - 使用
LANGGRAPH_DATABASE_URL启动agents,随后启动web - 注入 Dokploy 环境变量并等待健康检查通过
Step 7 — 应用数据库迁移
数据库迁移通过 compose.yml 中的一次性 migrations 服务执行:
migrations:
image: ${GHCR_IMAGE_PREFIX:-ghcr.io/chenhaonan-eth/ugcad}-migrations:${IMAGE_TAG:-latest}该镜像内置 scripts/migrate-postgres.sh 和 apps/web/src/config/db/migrations 下的 SQL 文件,并与 web、agents、rbac-init 使用同一个 IMAGE_TAG,确保应用代码、RBAC 初始化脚本和数据库结构来自同一 commit。
迁移镜像会按 apps/web/src/config/db/migrations/meta/_journal.json 的顺序执行 SQL,并把成功执行的 tag 写入 public.__ugcad_migrations。首次运行会从空 PostgreSQL 建立基础表;之后重复运行会跳过已执行项。不要手动复制 SQL 到容器,也不要在生产用 db:push 替代 SQL 迁移。
如果你的 Dokploy 版本没有自动运行一次性 Compose 服务,请在 Dokploy UI 中手动运行或重启 migrations 服务,并在日志中确认出现 applied 或 skipped 后再测试登录注册。
如果你接管的是已有本地/开发库,请先阅读 apps/web/src/config/db/migrations/README.md 的 baseline 说明;不要把本地 journal 不一致误判为生产 Dokploy 初始化失败。
Step 8 — 配置域名和 HTTPS
- 在 Dokploy 中进入 web 服务 → Domains
- 添加域名(如
app.yourdomain.com) - Dokploy 通过 Traefik 自动申请 Let's Encrypt 证书
启用 HTTPS 前,确保 DNS A 记录已指向服务器 IP。
Step 9 — 生产数据库切换 smoke test 清单
不要只用容器状态判断切换成功。按下面清单确认 DATABASE_URL、LANGGRAPH_DATABASE_URL、RBAC 初始化、业务读写和回滚窗口都符合预期。
migrations 日志
-
migrations一次性服务已成功完成。 - 首次连接新库时,新 migration 日志显示
applied。 - 重复运行时,已记录在
public.__ugcad_migrations的 migration 显示skipped。 - 日志中没有 SQL error、认证失败、权限错误或连接错误。
- 发布记录中写下最后一个
applied/skipped的 migration tag。
RBAC init
-
rbac-init一次性服务在migrations成功后完成。 - 未设置
RBAC_ADMIN_EMAIL的首次部署只初始化 roles / permissions / role_permissions,不要求管理员邮箱已存在。 - 如果已设置
RBAC_ADMIN_EMAIL,确认该邮箱对应的已注册用户获得super_admin。 - 管理员账号可以访问受保护的管理功能。
- 普通用户不能访问管理员功能。
- 未登录用户访问受保护页面时会进入认证入口或被拒绝。
Web 运行时
# Web 应用
curl https://yourdomain.com/api/health- 公开页面可访问。
-
https://yourdomain.com/sign-up和https://yourdomain.com/sign-in可访问。 -
/api/health返回健康状态。 - 至少执行一条生产已启用的业务写入路径,例如保存品牌工作区设置或创建/更新一个核心业务对象。
- 刷新页面或重新登录后能读回刚才写入的数据,证明写入落在
DATABASE_URL指向的业务库。
Agents / LangGraph runtime
# Agents API(仅内网访问)
docker compose exec web wget -qO- http://agents:8000/ok
# → {"ok":true}-
agents服务启动成功。 - 从
web容器访问http://agents:8000/ok返回成功响应。 - 执行一次会触发 LangGraph runtime checkpoint/state/store 的 agent 流程。
- 日志或数据库检查确认 runtime 状态写入
LANGGRAPH_DATABASE_URL,没有混入DATABASE_URL的业务表。
品牌工作区读写
- 使用
BRAND_WORKSPACE_ACTIVE_BRAND_ID打开当前生产品牌工作区。 - 成功读取品牌资料、素材或核心配置。
- 完成一次代表性写入,例如保存品牌配置、更新素材元数据或创建/更新当前生产已启用的核心对象。
- 刷新后确认写入仍存在。
- 确认写入没有落到旧 Compose PostgreSQL volume。
回滚准备
- 旧 Compose PostgreSQL volume 或切换前备份仍然保留。
- 备份位置、生成时间和负责人已记录。
- 观察窗口结束前没有删除旧 volume。
- 回滚需要明确切换
DATABASE_URL和LANGGRAPH_DATABASE_URL;系统没有双写路径。
更新部署
推荐按 commit tag 更新,同一 tag 必须同时用于 web、agents、migrations 和 rbac-init。
- 推送代码到
main或dev - 等待 GitHub Actions 成功,确认四个镜像都发布了同一个 tag
- 在 Dokploy 中把
IMAGE_TAG改成新的 tag,推荐使用sha-...;如果只是改运行时密钥,直接更新 Dokploy 环境变量即可,不需要重新构建镜像 - 点击 Redeploy,让 Dokploy 拉取新镜像并重新运行迁移与 RBAC 初始化服务
- 查看
migrations服务日志,确认新的 migration 显示applied,已执行过的 migration 显示skipped - 按 Step 9 的 smoke test 清单验证
应用版本回滚的方式相同:把 IMAGE_TAG 改回上一个可用 tag,再 Redeploy,同时保持 DATABASE_URL 和 LANGGRAPH_DATABASE_URL 不变。如果新版本已经执行了不可逆 schema 或数据变更,单纯回滚镜像可能不够,需要恢复切换前的数据库备份。
旧 Compose PostgreSQL volume 建议保留一个短期回滚窗口。如需临时回到内置 PostgreSQL,启用 legacy-postgres Compose profile,并显式设置强 POSTGRES_PASSWORD;未设置时容器会拒绝启动。把业务库和 LangGraph 库都恢复到内置 PostgreSQL 后,先将 DATABASE_URL 指向恢复后的业务库,将 LANGGRAPH_DATABASE_URL 指向恢复后的 LangGraph 库,再重新部署。不要把旧 volume 当作长期生产路径、双写目标或唯一备份策略。
如果你修改的是 NEXT_PUBLIC_APP_URL,先更新 GitHub repository variable,重新构建 web 镜像,再让 Dokploy 拉取新镜像。
常见问题
Dokploy 拉取 GHCR 镜像失败
确认镜像名称与 tag 正确:
GHCR_IMAGE_PREFIX=ghcr.io/chenhaonan-eth/ugcad
IMAGE_TAG=latest如果仓库镜像为 private,需要在 Dokploy 服务器上配置可读取 GHCR package 的凭据。
修改了域名但页面仍跳转到旧地址
NEXT_PUBLIC_APP_URL 是构建期变量。请先更新 GitHub repository variable,然后重新运行 GitHub Actions 构建并发布新的 web 镜像,再让 Dokploy 拉取新镜像。
agents 无法连接 PostgreSQL 或 Redis
确认 LANGGRAPH_DATABASE_URL 指向 LangGraph logical database,并且 Dokploy 应用能访问该数据库 host。如果 Dokploy 同时显示 internal 和 external PostgreSQL URL,同一 Dokploy 项目/网络内优先使用 internal URL。Redis 仍由 compose.yml 运行,因此所有服务仍需位于同一个 Compose 项目网络。
Web 或 migrations 无法连接 PostgreSQL
确认 DATABASE_URL 指向业务 logical database,而不是 LangGraph database。保存到 Dokploy 环境变量前,保留 Dokploy 提供的 sslmode 等 TLS 参数,并对凭据里的特殊字符做 URL encode。
Web 应用显示 502 Bad Gateway
agents 容器可能还未健康就绪。检查日志:
docker compose logs agents --tail=50部署后出现 schema 错误
有新的迁移文件未应用,或首次部署后 migrations 服务还没有成功完成。请在 Dokploy 中检查 migrations 服务日志,并重新运行该服务。成功执行时,新 migration 会显示 applied,已记录在 public.__ugcad_migrations 中的 migration 会显示 skipped。