Melso Docs

自托管快速上手

用 Docker Compose 启动 Melso,完成登录,并连接第一台执行电脑。

自托管 Melso 分成两部分:

部分运行什么放在哪里
Melso 服务Web、API 和 PostgreSQL一台安装了 Docker 的机器
执行电脑Melso 守护进程和 AI 编程工具开发者实际工作的电脑

它们可以是同一台机器,也可以分开。自托管替换的只是 Melso Cloud 部分。

这篇文档使用 Docker Compose。Kubernetes 部署见仓库中的 Self-hosting guide

开始前

运行 Melso 服务的机器需要:

  • Docker Engine 或 Docker Desktop,并且 docker compose 可以运行
  • Git、Make、curl 和 OpenSSL
  • 本机的 30008080 端口未被占用

先确认 Docker 和 Compose 可用:

docker info
docker compose version

Melso 使用 Compose v2,也就是 docker compose。旧版的 docker-compose v1 不受支持。

执行电脑还需要至少一款已经安装并登录的 AI 编程工具,例如 Claude Code、Codex 或 Cursor。第 5 步再安装 Melso CLI。

1. 启动 Melso

在准备运行服务的机器上执行:

git clone --depth 1 https://github.com/albertsalgueda/melso.git
cd multica
make selfhost

第一次运行时,make selfhost 会:

  1. .env.example 创建 .env
  2. 随机生成 JWT_SECRET、PostgreSQL 密码和 MULTICA_VCS_SECRET_KEY(自托管 Git 集成的加密密钥)
  3. 拉取 PostgreSQL、Melso backend 和 Melso frontend 镜像
  4. 创建持久化数据卷并启动三个容器
  5. 等待 backend 开始响应健康检查

之后再次运行 make selfhost 会继续使用现有的 .env 和数据卷,不会重新生成密钥。

make selfhost 拉取已发布的镜像,不会编译当前 checkout 中的代码。需要测试本地源码时,使用 make selfhost-build

2. 确认服务已经就绪

查看容器状态:

docker compose -f docker-compose.selfhost.yml ps

postgres 显示为 healthybackendfrontend 应处于运行状态。然后检查 backend、数据库和 migration:

curl -fsS http://localhost:8080/readyz

正常响应是:

{"status":"ok","checks":{"db":"ok","migrations":"ok"}}

Backend 容器每次启动时都会先运行数据库 migration,再启动服务,不需要手动执行 migration 命令。

3. 选择访问方式

本机访问

直接打开 http://localhost:3000。后面运行 melso setup self-host 时也不需要传 URL。

远程访问

Docker Compose 默认只把 30008080 绑定到 127.0.0.1。不要直接改成 0.0.0.0 暴露到公网;使用带 HTTPS 的反向代理。

下面以两个域名为例:

  • app.example.com:Melso Web
  • api.example.com:API、健康检查和守护进程连接

先在 .env 中设置公开地址:

FRONTEND_ORIGIN=https://app.example.com
MULTICA_APP_URL=https://app.example.com
MULTICA_PUBLIC_URL=https://api.example.com

以上配置让浏览器流量全部走 app 域,cookie 不跨域,因此不需要配置 COOKIE_DOMAIN。如果改为让浏览器直接访问 api 域,则必须配置它,见环境变量

然后配置 DNS,并用 Caddy 代理本机端口:

app.example.com {
    # 浏览器的 WebSocket 直接交给 backend
    @ws path /ws /ws/*
    handle @ws {
        reverse_proxy 127.0.0.1:8080 {
            flush_interval -1
        }
    }

    # 其余路径交给 frontend;它会转发 API 和登录请求
    handle {
        reverse_proxy 127.0.0.1:3000
    }
}

api.example.com {
    reverse_proxy 127.0.0.1:8080 {
        flush_interval -1
    }
}

Caddy 会申请 TLS 证书并转发 WebSocket。修改 .env 后,用 up -d 重新创建容器,让新配置生效:

docker compose -f docker-compose.selfhost.yml up -d
curl -fsS https://api.example.com/readyz

docker compose restart 只重启现有容器,不会重新读取 .env。修改配置后,重新读取 .env 需要运行 docker compose -f docker-compose.selfhost.yml up -d

4. 登录并创建工作区

打开本机的 http://localhost:3000,或刚才配置的 https://app.example.com,输入邮箱获取验证码。

默认没有配置邮件服务。请求验证码后,可以从 backend 日志中读取:

docker compose -f docker-compose.selfhost.yml logs backend \
  | grep "Verification code"

日志中会出现类似内容:

[DEV] Verification code for you@example.com: 123456

输入验证码并创建第一个工作区。配置 Resend 或 SMTP 后,验证码通过邮件发送,成员无需读取容器日志,具体见登录与注册配置

自托管部署默认使用 APP_ENV=production,不会启用固定验证码。不要在公网实例上设置 MULTICA_DEV_VERIFICATION_CODE

5. 连接执行电脑

下面的命令在运行 AI 编程工具的电脑上执行,不一定是运行 Docker 的服务器。

任务以运行守护进程的用户的全部权限执行——该用户能读写的一切,任务都能读写。请用专用 Unix 用户、容器或虚拟机来运行守护进程,而不是你的个人账号。详见安全模型

先安装 Melso CLI:

macOS / Linux

curl -fsSL https://downloads.melso.ai/install.sh | bash

Windows PowerShell

irm https://downloads.melso.ai/install.ps1 | iex

如果 Melso 服务也在这台电脑上,运行:

melso setup self-host

如果 Melso 服务在另一台机器上,传入刚才配置的两个地址:

melso setup self-host \
  --server-url https://api.example.com \
  --app-url https://app.example.com

这条命令会先检查 <server-url>/health,再打开浏览器完成登录。登录完成后,它会保存本机凭据并启动守护进程。

确认连接状态:

melso daemon status

正常情况下,输出中会显示:

  • Daemon: running
  • Agents 中包含本机已安装的 AI 编程工具
  • Workspaces 大于 0

6. 完成第一次执行

回到 Melso。运行时列表中出现在线的运行时后,创建一个智能体,并把第一个 issue 分配给它。

执行日志变为"已完成"、时间线中出现智能体的回复,说明自托管服务、执行电脑和 AI 编程工具已经连通。详细步骤见快速上手第 3-5 步。

常用管理命令

以下命令都在 multica 仓库目录中运行:

# 查看状态
docker compose -f docker-compose.selfhost.yml ps

# 查看 backend 日志
docker compose -f docker-compose.selfhost.yml logs -f backend

# 应用 .env 变化
docker compose -f docker-compose.selfhost.yml up -d

# 停止服务,保留数据卷
docker compose -f docker-compose.selfhost.yml down

升级已发布的镜像:

git pull --ff-only
docker compose -f docker-compose.selfhost.yml pull
docker compose -f docker-compose.selfhost.yml up -d
curl -fsS http://localhost:8080/readyz

Docker Compose 部署有两种升级写法。在已有部署上两者结果相同——Makefile 里的 selfhost target 跑的就是同一套 docker compose pull + up -d,只是额外多做了两件事:.env 缺失时生成一份,以及等 /health 就绪后打印一段状态汇总。用哪个都行:

cd multica
git pull
make selfhost
cd multica
git pull
docker compose -f docker-compose.selfhost.yml pull
docker compose -f docker-compose.selfhost.yml up -d

git pull 到底更新了什么

git pull 更新的是 docker-compose.selfhost.yml 本身——新增的环境变量、新增的服务、改过的 healthcheck。它不是拉新版本 Melso 的机制。

真正决定你跑哪个版本的是 docker compose pull:它去 GHCR 查这个 tag 当前指向哪个镜像。所以一个几个月没更新的 checkout 照样能拉到今天的 latest 镜像;反过来,只跑 git pull 而不拉镜像、不重建容器,什么都不会变。

如果你在 .env 里钉死了 MULTICA_IMAGE_TAG,两种写法都升不上去。 两个镜像都写成 ${MULTICA_IMAGE_TAG:-latest}docker-compose.selfhost.yml:42:125),而 .env.example 里默认是 MULTICA_IMAGE_TAG=latest。一旦你把它改成了具体版本,pull 只是把同一个 tag 重拉一遍,版本原地不动——不报错,也没有任何警告。升级前先确认:

grep MULTICA_IMAGE_TAG .env
# MULTICA_IMAGE_TAG=v0.4.5   ← 钉死了:先改成 latest(或你想要的版本)

.env 不会被覆盖

make selfhost 只在 .env 不存在的时候才生成它。在已有部署上重复跑,你的 JWT_SECRET、Postgres 密码、邮件配置、FRONTEND_ORIGIN 都原样保留。

升级前先备份 Postgres

migration 只往前走、不回滚,所以正式环境升级前先导一份:

docker compose -f docker-compose.selfhost.yml exec -T postgres \
  pg_dump -U multica multica > multica-backup.sql && gzip multica-backup.sql

不要把 pg_dump 直接管道接给 gzip shell 取的是管道里最后一条命令的退出码,所以 pg_dump ... | gzip > backup.sql.gz 在导出失败时照样返回 0,留下一个格式完全合法、但里面什么都没有的 20 字节压缩包。先重定向到文件,pg_dump 自己的退出码才算数,&& 也只会压缩真正成功的那份导出。

如果你改过 POSTGRES_USER / POSTGRES_DB,把 multica 换成 .env 里的实际值。数据存在名为 multica_pgdata 的 volume 里,docker compose down 不会删它——但 down -v 会。

migration 自动执行

第 1 步一样,backend 容器启动时会在对外提供服务之前先跑 ./migrate updocker/entrypoint.sh)。没有单独的升级命令——把新镜像起起来,本身就是升级这一步。想看过程:

docker compose -f docker-compose.selfhost.yml logs -f backend

Migration 在 backend 启动时自动运行;需要回填历史数据的 migration(如 103)也会自动完成回填。自动回填极少数情况下失败,报 refusing to drop legacy daily rollups,处理见故障排查

/readyz 验证,不要用 /health

/healthliveness 探针,只要进程还活着就返回 {"status":"ok"},migration 失败了它照样是 ok。/readyzserver/cmd/server/router.go:680/healthz 是它的别名)会真正检查数据库和已应用的 migration 集合,升级出问题能被它拦下来:

curl -s localhost:8080/readyz
# {"status":"ok","checks":{"db":"ok","migrations":"ok"}}

只要不是 HTTP 200 且两项都是 ok,就说明新版本没把 migration 跑完,先去看 backend 日志,别急着放流量进来。

Kubernetes

Helm 有自己的升级路径:在 values 文件里把 images.backend.tag / images.frontend.tag 设成你要的版本,然后 helm upgrade。改 tag 就改了 pod spec,Kubernetes 会拉新镜像并滚动更新——这是可靠的路径。

kubectl -n multica rollout restart 本身不是升级。chart 默认 pullPolicy: IfNotPresentdeploy/helm/multica/values.yaml),节点上已经缓存过这个 tag 的话,restart 只会用回旧镜像,什么都没变——和钉死 MULTICA_IMAGE_TAG 是同一类坑。真要靠浮动 tag 走这条路,得先把 images.backend.pullPolicy / images.frontend.pullPolicy 设成 Always。详见仓库的 Self-hosting guide

docker compose down 会保留 pgdatabackend_uploads。加上 -v 会删除这些数据卷,包括数据库;除非确定要清空实例,否则不要运行 docker compose down -v

常见问题

现象先检查什么
/readyz 没有返回 ok运行 docker compose -f docker-compose.selfhost.yml logs backend postgres
收不到验证码请求一次验证码,再从 backend 日志中查找 Verification code
setup self-host 提示 server 不可达在执行电脑上请求 https://api.example.com/health,确认 DNS、TLS 和反向代理都可达。
守护进程没有列出 Agents确认 AI 编程工具在 PATH 中并已登录,然后运行 melso daemon restart
issue 一直排队运行 melso daemon status,确认守护进程正在运行并连接了工作区。

更多情况见故障排查

接下来