Automatisch Docker Compose 部署实战

Automatisch v0.15.0 官方 Docker Compose 部署实战:官方镜像与仓库源码两条路径差异辨析、三把安全密钥初始化、main/worker 双进程编排与启动验证、WEBHOOK_URL 外部可达性修正实录、升级回滚与卸载清理。

AutomatischAutomatisch工作流自动化2026/10/28

Automatisch Docker Compose 部署实战

目标:在一台 Linux 服务器上以官方 Docker Compose 方式完成 Automatisch v0.15.0 的生产化部署,覆盖官方两条部署路径的差异辨析、三把安全密钥的初始化、main/worker 双进程编排、外部可达性修正(WEBHOOK_URL)与升级回滚。本文全部命令与回显均在 2026-10-28 于 Docker 环境模拟真实服务器实测通过。

一、环境信息

项目实测值
操作系统Linux(Docker 宿主,内核 5.x/6.x 均可)
Automatisch 版本0.15.0(官方最新稳定版,Docker Hub tag 发布于 2025-08-08)
依赖组件版本PostgreSQL 14.5、Redis 7.0.4(与官方仓库 docker-compose.yml 指定版本一致)
实测宿主地址192.168.146.128(内网 IP,下文 webhook 回调地址以此为基准)
Web 服务端口宿主 8140 → 服务 3000(Automatisch 默认监听 3000)
专用网络10.89.56.0/24(自建 bridge 网络,避开默认网段与他栈冲突)
Compose 项目名automatisch-0.15.0-stack

Automatisch 官方技术栈为 Node.js + PostgreSQL + Redis,进程分 main 与 worker 两个角色(同一镜像、同一环境变量,worker 侧多一个 WORKER=true),因此编排文件共 4 个服务:main、worker、postgres、redis。

二、前置条件与版本选型

  1. Docker 与 Compose 插件:实测环境 Docker 27.x、Compose v2(docker compose 子命令形式)。版本查询:
$ docker compose version
Docker Compose version v2.39.2
  1. 镜像准备:三个镜像一次拉齐(Automatisch 官方镜像 + 官方编排锁定的 PostgreSQL/Redis 版本):
$ docker pull automatischio/automatisch:0.15.0
Digest: sha256:3bace7a12d5fb3f5b1305a6a52232270e0e0abd8465a8b78baacb07f6ea89594
Status: Downloaded newer image for automatischio/automatisch:0.15.0
$ docker pull postgres:14.5
docker.io/library/postgres:14.5
$ docker pull redis:7.0.4
docker.io/library/redis:7.0.4

国内网络拉取 Docker Hub 缓慢或超时时,可为 dockerd 配置 HTTP 代理(本次实测经代理 192.168.1.62:7897 完成,全部拉取成功;该代理为实测环境内网代理,读者请替换为自己的可用渠道)。

  1. 版本选型说明:官方仓库 docker-compose.yml 锁定 postgres:14.5 与 redis:7.0.4,生产部署建议严格沿用官方锁定版本,避免跨大版本兼容性问题;Automatisch 本体使用 0.15.0 固定 tag 而非 latest,保证可重现与可回滚。

三、方案 A:官方镜像 Compose 部署(生产推荐)

本方案使用 Docker Hub 官方镜像 automatischio/automatisch:0.15.0 + 自备编排文件,是官方文档「Docker」一节的生产化落地形态:main/worker 共用镜像、环境变量显式注入、版本钉死、启动即迁移建库。

3.1 生成三把安全密钥

Automatisch 的凭据加密与 Webhook 验签依赖三把密钥,官方文档建议用 openssl rand -base64 36 生成(automatisch.io/docs/guide/installation,查询日期 2026-10-28)。实测生成(输出即真实密钥样例,生产环境请自行重新生成并妥善保管,勿照抄本文值):

$ echo "ENCRYPTION_KEY=$(openssl rand -base64 36)"
ENCRYPTION_KEY=18hhdCO6CwRZMTx5uw6WguO78THNb7tqK/NNddXFiIvVjtUd
$ echo "WEBHOOK_SECRET_KEY=$(openssl rand -base64 36)"
WEBHOOK_SECRET_KEY=kDxuA81RZUcYMWOnSEnvzHTxuqVaQsQipTqmcmCX1OLb5BT4
$ echo "APP_SECRET_KEY=$(openssl rand -base64 36)"
APP_SECRET_KEY=QSiIX2+Fvu5FBXaWOvlDSYKdixxpufvUQv5wWGmBEvrV+coz

三把密钥的职责(官方配置文档口径):ENCRYPTION_KEY 加密第三方连接凭据、WEBHOOK_SECRET_KEY 校验 webhook 请求、APP_SECRET_KEY 用于用户认证签名。官方以 DANGER 级别警示:密钥一经使用不可更换,否则既有连接与 flow 全部失效。

3.2 编排文件

落盘为 /opt/automatisch/docker-compose.yml(本次实测路径为 /opt/data/quzuo/tmp/automatisch/stack/docker-compose.yml,读者按自己目录习惯放置即可):

name: automatisch-0.15.0-stack

services:
  main:
    image: automatischio/automatisch:0.15.0
    container_name: automatisch-0.15.0-main
    ports:
      - '8140:3000'
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      - APP_ENV=production
      - HOST=localhost
      - PROTOCOL=http
      - PORT=3000
      - WEB_APP_URL=http://192.168.146.128:8140
      - WEBHOOK_URL=http://192.168.146.128:8140
      - REDIS_HOST=redis
      - POSTGRES_HOST=postgres
      - POSTGRES_DATABASE=automatisch
      - POSTGRES_USERNAME=automatisch_user
      - POSTGRES_PASSWORD=automatisch_password
      - ENCRYPTION_KEY=18hhdCO6CwRZMTx5uw6WguO78THNb7tqK/NNddXFiIvVjtUd
      - WEBHOOK_SECRET_KEY=kDxuA81RZUcYMWOnSEnvzHTxuqVaQsQipTqmcmCX1OLb5BT4
      - APP_SECRET_KEY=QSiIX2+Fvu5FBXaWOvlDSYKdixxpufvUQv5wWGmBEvrV+coz
      - ENABLE_BULLMQ_DASHBOARD=true
      - BULLMQ_DASHBOARD_USERNAME=admin
      - BULLMQ_DASHBOARD_PASSWORD=q1w2e3r4
    volumes:
      - automatisch_storage:/automatisch/storage
    networks:
      - automatisch_net

  worker:
    image: automatischio/automatisch:0.15.0
    container_name: automatisch-0.15.0-worker
    depends_on:
      - main
    environment:
      - APP_ENV=production
      - REDIS_HOST=redis
      - POSTGRES_HOST=postgres
      - POSTGRES_DATABASE=automatisch
      - POSTGRES_USERNAME=automatisch_user
      - POSTGRES_PASSWORD=automatisch_password
      - WEB_APP_URL=http://192.168.146.128:8140
      - WEBHOOK_URL=http://192.168.146.128:8140
      - ENCRYPTION_KEY=18hhdCO6CwRZMTx5uw6WguO78THNb7tqK/NNddXFiIvVjtUd
      - WEBHOOK_SECRET_KEY=kDxuA81RZUcYMWOnSEnvzHTxuqVaQsQipTqmcmCX1OLb5BT4
      - APP_SECRET_KEY=QSiIX2+Fvu5FBXaWOvlDSYKdixxpufvUQv5wWGmBEvrV+coz
      - WORKER=true
      - ENABLE_BULLMQ_DASHBOARD=true
      - BULLMQ_DASHBOARD_USERNAME=admin
      - BULLMQ_DASHBOARD_PASSWORD=q1w2e3r4
    volumes:
      - automatisch_storage:/automatisch/storage
    networks:
      - automatisch_net

  postgres:
    image: postgres:14.5
    container_name: automatisch-0.15.0-postgres
    environment:
      - POSTGRES_DB=automatisch
      - POSTGRES_USER=automatisch_user
      - POSTGRES_PASSWORD=automatisch_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}']
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - automatisch_net

  redis:
    image: redis:7.0.4
    container_name: automatisch-0.15.0-redis
    volumes:
      - redis_data:/data
    networks:
      - automatisch_net

volumes:
  automatisch_storage:
  postgres_data:
  redis_data:

networks:
  automatisch_net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.56.0/24

编排要点逐条说明:

  1. name: automatisch-0.15.0-stack:显式声明 Compose 项目名,多套自动化栈并存时避免同名项目互相覆盖(Compose 默认取目录名);
  2. main 依赖条件:postgres: service_healthy(等健康检查通过)+ redis: service_started,与官方仓库编排一致,保证启动顺序正确;
  3. worker 依赖 main:沿用官方编排的启动次序(worker 等 main 先起);
  4. WORKER=true 仅出现在 worker:这是两个进程唯一的镜像行为开关(官方文档明示两服务环境变量应保持一致,仅此一项差异);
  5. 共用 automatisch_storage 卷:main 与 worker 挂同一存储卷(/automatisch/storage),这是官方编排的约定,为源码构建方式下的密钥落盘与后续版本扩展保留语义;
  6. BullMQ 仪表盘三变量(ENABLE_BULLMQ_DASHBOARD + BULLMQ_DASHBOARD_USERNAME/PASSWORD):可选配置,启用后 worker 队列状态可通过 /admin/queues 查看,三个变量缺一不生效(详见本专栏运维篇);
  7. WEB_APP_URL 与 WEBHOOK_URL:把对外可达地址从默认的 http://localhost:3000 修正为宿主真实地址,机制与排障过程见第六章;
  8. 健康检查:pg_isready 原样沿用官方定义;
  9. 数据卷三个:应用存储、PostgreSQL 数据、Redis 数据各一,备份盘盘点见运维篇。

3.3 启动与日志验证

$ cd /opt/automatisch && docker compose up -d
 Container automatisch-0.15.0-postgres Started
 Container automatisch-0.15.0-redis Started
 Container automatisch-0.15.0-postgres Waiting
 Container automatisch-0.15.0-postgres Healthy
 Container automatisch-0.15.0-main Started
 Container automatisch-0.15.0-worker Starting
 Container automatisch-0.15.0-worker Started

回显次序验证了编排设计的依赖链:postgres/redis 先起 → postgres 健康检查通过 → main 启动 → worker 启动。等待约 30 秒(首次启动需完成数据库迁移与种子用户创建),确认四进程状态:

$ docker compose ps --format 'table {{.Name}}\t{{.Status}}\t{{.Ports}}'
NAME                          STATUS                   PORTS
automatisch-0.15.0-main       Up 45 seconds            0.0.0.0:8140->3000/tcp, [::]:8140->3000/tcp
automatisch-0.15.0-postgres   Up 2 minutes (healthy)   5432/tcp
automatisch-0.15.0-redis      Up 2 minutes             6379/tcp
automatisch-0.15.0-worker     Up 45 seconds            3000/tcp

main 进程日志(关键段落节选,其中 seed 用户创建与服务监听两行是启动成功的标志):

$ docker logs automatisch-0.15.0-main | tail -15
yarn run v1.22.22
warning package.json: License should be a valid SPDX license expression
$ node --loader ./alias-loader.mjs ./bin/database/seed-user.js
(node:61) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:
--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("./alias-loader.mjs", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)
2026-10-28 04:12:10:1210 [info]: User has been saved: user@automatisch.io
Done in 1.12s.
yarn run v1.22.22
warning package.json: License should be a valid SPDX license expression
$ node --loader ./alias-loader.mjs src/server.js
(node:94) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:
--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("./alias-loader.mjs", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)
2026-10-28 04:12:11:1211 [info]: Server is listening on http://localhost:3000

从日志可见镜像内置入口的行为:main 进程启动时依次执行 db:migrate(数据库迁移)、db:seed:user(创建种子用户 user@automatisch.io)、start(启动 Web 服务)。

worker 进程日志(节选):

$ docker logs automatisch-0.15.0-worker | tail -5
yarn run v1.22.22
warning package.json: License should be a valid SPDX license expression
$ node --loader ./alias-loader.mjs src/worker.js
(Use `node --trace-warnings ...` to show where the warning was created)
2026-10-28 04:12:10:1210 [info]: Workers are ready!

Workers are ready! 表示 worker 已连上 Redis 队列并就位消费。四进程各就各位后,验证 HTTP 服务:

$ curl -s -o /dev/null -w 'HTTP %{http_code} (%{time_total}s)\n' http://127.0.0.1:8140/
HTTP 200 (0.007357s)
$ curl -s http://127.0.0.1:8140/healthcheck -w 'HTTP %{http_code}\n'
HTTP 200

3.4 登录 Web 界面

浏览器访问 http://192.168.146.128:8140,使用官方内置演示账号登录:邮箱 user@automatisch.io、密码 sample(官方安装文档明示的开箱账号,fullName 为 Initial admin、角色 Admin)。登录后第一件事:在 Settings 页修改邮箱与密码(官方文档提示「Please do not forget to change your email and password from the settings page」)。

一个与直觉不同的行为需要提前知晓:首次安装后无法再通过注册接口创建新用户——Automatisch 0.15.0 的用户创建接口(POST /internal/api/v1/installation/users)只在整个实例零用户时开放,种子用户已占位,再调用注册接口返回 403(实测回显:空响应体 + HTTP 403)。社区版单用户的边界即在于此,多人使用需企业版(见《Automatisch 介绍与选型指南》第八章)。

四、方案 B:官方仓库源码编排部署(体验路径)

官方 README 与安装文档给出的默认路径是克隆仓库直接启动:

$ git clone https://github.com/automatisch/automatisch.git
$ cd automatisch
$ docker compose up

该编排(仓库内 docker-compose.yml)与方案 A 有三处本质差异,选型前必须理解:

差异点方案 B(仓库编排)方案 A(镜像编排)
服务进程来源从源码构建(build: context: ./docker, dockerfile: Dockerfile.compose)直接使用 Docker Hub 官方镜像 automatischio/automatisch:0.15.0
入口脚本entrypoint: /compose-entrypoint.sh(仓库镜像内才存在)镜像自带 /entrypoint.sh
三把密钥首次启动自动生成并写入存储卷 .env(openssl rand -base64 36)部署者显式注入环境变量
版本控制跟随仓库 main 分支漂移tag 钉死,可重现可回滚
首启耗时需完整构建(Node.js monorepo,分钟级到十分钟级)秒级(仅拉镜像)

踩坑实录(本文实测过程中的真实报错):直接复用仓库编排文件、仅把 build 段替换为 image: automatischio/automatisch:0.15.0 是不可行的——仓库编排里的 entrypoint: /compose-entrypoint.sh 指向的脚本只存在于源码构建产物中,官方镜像内并没有这个文件,启动直接失败:

$ docker compose up -d
 Container automatisch-0.15.0-postgres Started
 Container automatisch-0.15.0-redis Started
 Container automatisch-0.15.0-postgres Waiting
 Container automatisch-0.15.0-postgres Healthy
 Container automatisch-0.15.0-main Starting
Error response from daemon: failed to create task for container: failed to create shim task:
OCI runtime create failed: runc create failed: unable to start container process:
error during container init: exec: "/compose-entrypoint.sh": stat /compose-entrypoint.sh:
no such file or directory

修正方法:删除编排文件中 main 与 worker 两处的 entrypoint: /compose-entrypoint.sh 行,让镜像使用自带入口 /entrypoint.sh(其逻辑为:main 执行 yarn db:migrate && yarn db:seed:user && yarn start,worker 检测到 WORKER 变量后执行 yarn start:worker)。密钥补偿措施:镜像自带入口不会自动生成三把密钥,必须按 3.1 节显式注入——这正是方案 A 编排文件把三密钥写全的原因。

两个方案的取舍结论:生产环境选方案 A(版本可控、启动快、密钥显式治理);只想快速体验且能接受构建耗时选方案 B(密钥零配置)。本文后续章节均基于方案 A 继续展开。

五、部署形态核对

部署完成后盘点栈的资源 footprint,确认与设计一致:

$ docker volume ls --format '{{.Name}}' | grep automatisch
automatisch-0150-stack_automatisch_storage
automatisch-0150-stack_postgres_data
automatisch-0150-stack_redis_data
$ docker network ls --format '{{.Name}}' | grep automatisch
automatisch-0150-stack_automatisch_net

说明:Compose 项目名中的 . 在资源命名时被规范化为 -(automatisch-0.15.0-stack → automatisch-0150-stack),这是 Compose 的命名约定,脚本化引用卷名/网络名时需注意。三个卷的用途与备份方法(pg_dump 逻辑备份 + 卷快照)在本专栏运维篇完整展开。

检查应用存储卷内容时有一个值得记录的实测事实:镜像方式下 automatisch_storage 卷是空的——只有方案 B 的 compose-entrypoint 才会把 .env 密钥文件写入该卷:

$ docker run --rm -v automatisch-0150-stack_automatisch_storage:/storage alpine ls -la /storage/
total 8
drwxr-xr-x    2 root     root          4096 Oct 28 04:10 .
drwxr-xr-x    1 root     root          4096 Oct 28 12:18 ..

因此方案 A 的密钥治理责任完全在编排文件与部署者:三把密钥应随编排文件纳入加密管理(vault/密管系统),不要只存在于某台机器的 yml 里。

六、外部可达性修正(WEBHOOK_URL 实测实录)

现象:按官方默认环境变量(HOST=localhost、PORT=3000)部署后,flow 触发器返回的 webhook 地址是 http://localhost:3000/webhooks/flows/<flowId>/sync——在本机 curl 可用,但外部系统回调必然失败。

定位:Automatisch 的 URL 拼装逻辑在服务端配置层(packages/backend/src/config/app.js,源码核实):

const apiUrl = new URL(process.env.API_URL || `${protocol}://${host}:${port}`).toString();
let webAppUrl = apiUrl;
if (process.env.WEB_APP_URL) {
  webAppUrl = new URL(process.env.WEB_APP_URL).toString();
}
let webhookUrl = new URL(process.env.WEBHOOK_URL || apiUrl).toString();

三个结论:webAppUrl 优先取 WEB_APP_URL(默认回落 PROTOCOL://HOST:PORT);webhookUrl 只认 WEBHOOK_URL,不受 WEB_APP_URL 影响;两者都缺省时统一回落到 http://localhost:3000。

实测验证:先只加 WEB_APP_URL=http://192.168.146.128:8140(main 与 worker 两服务同步修改,官方文档明确要求「Please do not forget to change in main and worker services of docker-compose since the following variables might be used in both」),重启后查询 flow 详情:

$ docker compose up -d
 Container automatisch-0.15.0-main Started
 Container automatisch-0.15.0-worker Started
$ curl -s http://127.0.0.1:8140/internal/api/v1/flows/<flowId> -H "Authorization: <token>" \
  | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['steps'][0]['webhookUrl'])"
webhookUrl: http://localhost:3000/webhooks/flows/438ba068-5018-49f8-a60e-1018c9450566/sync

WEB_APP_URL 并没有改变 webhookUrl——与源码逻辑一致。补上 WEBHOOK_URL=http://192.168.146.128:8140(同样双服务同步)后重启再查:

webhookUrl: http://192.168.146.128:8140/webhooks/flows/438ba068-5018-49f8-a60e-1018c9450566/sync

修正生效。结论:对外提供 webhook 回调的部署必须设置 WEBHOOK_URL(反代场景同理);WEB_APP_URL 的职责是界面连接地址与 CORS 域,按需配置。全套环境变量的语义与优先级在《Automatisch 配置参数详解》中逐项展开。

七、常见问题(FAQ)

Q1:启动报 exec: "/compose-entrypoint.sh": no such file or directory 怎么处理? 这是把官方仓库编排(方案 B)的 entrypoint 惯例套在官方镜像上导致。删除编排中两处 entrypoint: /compose-entrypoint.sh,改用镜像自带入口,并按 3.1 节显式注入三把密钥(见第四章踩坑实录)。

Q2:main 一直起不来,日志报数据库连接失败? 确认 POSTGRES_HOST=postgres(服务名而非 localhost)、postgres 容器健康(docker compose ps 中 (healthy))、POSTGRES_PASSWORD 三个服务一致。postgres 健康检查未通过前 main 不会启动(depends_on 条件依赖)。

Q3:首次登录用什么账号? 官方内置演示账号 user@automatisch.io / sample(官方文档明示)。种子用户已占位注册接口,实例内无法再自注册(实测 403),改密在 Settings 页完成。

Q4:flow 激活时返回 422 All steps should be completed before updating flow status!? 每条 flow 的所有步骤(trigger + action)必须先通过测试或显式跳过(前端「Continue without test」,接口 POST /internal/api/v1/steps/<stepId>/continue-without-test)把状态从 incomplete 置为 completed,然后才能发布激活。完整操作链见运维篇。

Q5:修改了 ENCRYPTION_KEY 后所有连接报错? 官方 DANGER 警示的直接后果:ENCRYPTION_KEY 加密的凭据无法用新密钥解开。唯一出路是改回原密钥;确需轮换时必须按官方指引先导出重置相关凭据,不能直接换值重启。

Q6:升级怎么做? 方案 B 官方路径:git pull origin main 后 docker compose up --force-recreate --build(官方文档原文)。方案 A(镜像方式,生产推荐):改编排中的镜像 tag 至新版(如 0.15.0 → 后续版本),docker compose up -d 重建;main 进程启动时自动执行数据库迁移。升级前务必完成数据备份(方法见运维篇),并留意跨版本可能引入的迁移不可逆风险。

八、卸载与清理

$ docker compose down          # 停止并删除四个服务进程与网络(保留数据卷)
$ docker compose down -v       # 连同三个数据卷一起删除(彻底清空数据,慎用)

只卸载 Automatisch 保留数据时用前者;确认不再回迁时用后者。镜像清理:docker rmi automatischio/automatisch:0.15.0 postgres:14.5 redis:7.0.4。

九、参考链接

  1. 官方安装文档(Docker Compose / Docker 两路径):https://automatisch.io/docs/guide/installation (查询日期 2026-10-28)
  2. 官方配置文档(环境变量表与 main/worker 同步修改警示):https://automatisch.io/docs/advanced/configuration (查询日期 2026-10-28)
  3. Docker Hub 官方镜像:https://hub.docker.com/r/automatischio/automatisch (tag 0.15.0,2025-08-08 发布)
  4. 官方仓库(编排文件与入口脚本源码):https://github.com/automatisch/automatisch