如何在 VPS 上自托管 KiroCrew 并保持代理常驻
使用 Docker 和 systemd 在 VPS 上运行 KiroCrew gateway,持久化记忆与计划任务,支持 SSH 访问、备份及升级失败后的回滚。
为何在 VPS 上自托管 KiroCrew,而不是在笔记本电脑上
只有在一台永不休眠的机器上自托管 KiroCrew 才有意义,因此 VPS 适合运行它,笔记本电脑则不适合。KiroCrew 会将会话历史、语义记忆、计划任务和审批队列保存到磁盘,并在进程重启时重新加载这些数据。如果计划任务在 03:00 到期时进程没有运行,这些数据都无法发挥作用;而合上的笔记本电脑不会运行该进程。
KiroCrew 是 Kiro 团队开发的开源代理工作区,采用 Apache 2.0 许可证,首批公开版本于 2026 年 8 月初发布。一个称为 gateway 的进程负责管理状态,并在 5476 端口提供 Web 仪表板。您可以从仪表板、kirocrew CLI,或 Slack 等聊天频道访问该 gateway。您自托管的只有 gateway,因此本指南重点介绍如何让它持续运行、避免将其暴露到公网,以及在升级失败后如何恢复。
开始前需要了解两点。KiroCrew 会驱动 kiro-cli,首次使用时需要通过 Kiro 账户登录;代理推理费用计入 Kiro 计划,因此截至 2026 年 8 月,这不是离线部署。该项目也只有几周历史。请假设您迟早需要回滚,并采用支持回滚的安装方式。如果您以前没有在服务器上运行过代理,在 VPS 上运行编码代理介绍了本指南所依据的基本规则。如果您对代理的了解少于对服务器的了解,建议先阅读了解代理循环、工具和记忆的实际含义,这样下面的选择会更容易理解为具体决策,而不是一组无法解释的命令。
KiroCrew 的依赖项及其状态存储位置
原生安装需要 Python 3.10 或更高版本(项目推荐使用 3.12)。如果从源代码构建控制面板,还需要 Node.js 18 或更高版本,以及 kiro-cli;首次启动时,系统会自动安装并完成登录。容器安装不要求主机具备这些依赖项,只需要 Docker。这是优先选择容器安装的主要原因。
状态存储在 ~/.kiro/crew 中,KIROCREW_HOME 环境变量可将其移动到其他位置。其中包含:
config.json:网关设置和聊天频道凭据。.env:机密信息。workspace/memory/:偏好设置、项目笔记和聊天历史记录。memory.db和memory_index.db:语义索引和全文索引。models/:首次运行时下载的嵌入模型。gateway.log和security_events.jsonl:运行时日志和安全事件日志。
该目录就是整个安装内容。将它复制到新的 VPS,即可迁移您的代理。因此,下面的备份部分比安装部分更重要。
规划磁盘空间时,不要只考虑 RAM。网关是一个 Python 进程;真正占用主机资源的是代理运行的任务,例如构建或测试套件。状态目录会随着聊天历史记录增长,嵌入模型会在首次启动时下载。因此,请在运行几周后使用 du -sh ~/.kiro/crew 测量您自己的主机,而不要依赖项目第一个月发布的任何容量数据。相比之下,另一种运行时会为每个工作进程提供独立的容器和浏览器;在这种情况下,自行托管 OpenBot 的 AI 协作者时,应先考虑 RAM 容量,再考虑磁盘空间。
应选择哪种安装方式
该项目提供三种安装方式。单行安装程序会下载一个 wheel,并将 kirocrew 添加到 PATH:
curl -fsSL https://download.crew.kiro.dev/cli.sh | sh它接受 channel 标志和 version 标志:
curl -fsSL https://download.crew.kiro.dev/cli.sh | sh -s -- --channel insider
curl -fsSL https://download.crew.kiro.dev/cli.sh | sh -s -- --version 0.1.3容器镜像发布在 ghcr.io/kirodotdev/kirocrew,每个标签都包含 linux/amd64 和 linux/arm64。源码构建需要 git clone 和 make build,适用于修改代码的人员,不适用于运行该项目的人员。
请使用容器。原生安装会将 Python 软件包、Node 和 kiro-cli 安装到运行其他服务的同一台主机上。升级失败后,您需要手动清理这些变更。容器将运行时保存在一个镜像中,并将状态保存在一个卷中。这样,回滚只需更改标签并重启容器。
将镜像固定到发布标签,而不是固定到 stable
项目自身的示例使用 stable 标签:
docker run -d --name kirocrew \
-p 127.0.0.1:5476:5476 \
-v kirocrew-home:/home/kirocrew \
ghcr.io/kirodotdev/kirocrew:stablestable 是一个会移动的标签。它始终指向当前最新的稳定版本,因此下一次拉取可能在您未主动选择的情况下更改正在运行的版本,而且标签本身无法记录具体使用的是哪个版本。版本标签不可变,因此应固定到某个版本。截止 6 August 2026,最新发布版本是 0.1.3,发布于 5 August 2026。另有一个 nightly 标签。对于这样一个刚成立不久的项目,这意味着代码可能就在今天早上发生过变化。
写入 /opt/kirocrew/compose.yaml:
services:
kirocrew:
image: ghcr.io/kirodotdev/kirocrew:0.1.3
container_name: kirocrew
restart: unless-stopped
ports:
- "127.0.0.1:5476:5476"
volumes:
- kirocrew-home:/home/kirocrew
volumes:
kirocrew-home:启动容器,然后检查该镜像用于自身 HEALTHCHECK 的健康检查端点:
cd /opt/kirocrew
docker compose up -d
docker compose ps
curl -s http://127.0.0.1:5476/api/health大约 1 分钟内,docker compose ps 应将容器报告为健康状态;/api/health 无需令牌即可返回响应,/api/live 和 /api/ready 也一样,因此它们可用作探针。如果状态一直停留在 starting,请先阅读 docker logs kirocrew,再进行任何更改。首次运行时会下载嵌入模型,因此网络连接较慢会使首次启动耗时较长。
使用 systemd 保持服务运行
restart: unless-stopped 会在容器崩溃或系统重启后恢复容器,前提是 Docker 本身会在启动时运行。单元文件可以明确声明这一依赖,并提供一条命令,在备份前停止整个服务栈。在启动时运行 Docker Compose 服务栈介绍了一般模式。下面是 KiroCrew 在 /etc/systemd/system/kirocrew.service 中的配置方式:
[Unit]
Description=KiroCrew gateway
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/kirocrew
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now kirocrew
systemctl status kirocrewsystemctl status kirocrew 应显示为 active (exited),这表示该单元运行正常。这里使用 Type=oneshot 和 RemainAfterExit=yes 是正确的,因为 docker compose up -d 会在容器启动后立即返回:systemd 跟踪的是服务栈已启动这一状态,而不是前台进程。如果改用 Type=simple,systemd 会看到命令立即退出,将服务标记为已停止,然后根据 Restart= 设置选择放弃重启或进入重启循环。对于原生安装,项目提供了自己的等效单元 kirocrew service install;该单元会写入 /etc/systemd/system/kirocrew.service,并以当前用户身份运行网关。不要同时运行这两个单元。VPS 上的 systemd 服务和计时器详细介绍了这一主题。单元如果无法恢复运行,默认不会主动通知;因此应添加 OnFailure= 处理程序,将告警推送到您自己的 ntfy 服务器。这样,您可以通过手机得知网关已停止,而不是等到某个从未运行的计划任务才发现问题。
首次运行:登录并获取 dashboard 令牌
容器会启动网关,但 agent runtime 尚未登录。请在容器内登录:
docker exec -it kirocrew kiro-cli login命令会输出设备代码和 URL。请在您自己的浏览器中打开该 URL。然后生成 dashboard 令牌:
docker exec kirocrew kirocrew token --ttl 2hdashboard URL 为 http://localhost:5476/?token=<the token>。令牌会过期:会话默认有效期为 1 小时,文档规定的最长有效期为 20 小时。dashboard 加载为空白,或打开后立即将您重定向回登录页面,通常表示令牌已过期,请重新生成令牌。切勿将令牌粘贴到工单或聊天消息中,因为持有令牌的人可以控制您的 agent。
通过 SSH 访问仪表板,绝不要发布端口 5476
再次查看项目示例中的绑定地址:-p 127.0.0.1:5476:5476。容器内的网关监听 0.0.0.0,因为它必须能够通过端口映射访问;但该映射只将端口发布到主机的 loopback 接口。删除 127.0.0.1: 前缀后,网关就会暴露在公网,任何扫描该端口的人都可以访问。防火墙规则也无法解决这个问题:Docker 通过写入 DNAT 规则来发布端口,而这些规则会在 ufw 的过滤规则之前处理,因此 ufw deny 5476 对已发布的端口不起作用。绕过 ufw 的 Docker 端口详细说明了这一机制。
在笔记本电脑上通过 SSH 转发该端口:
ssh -N -L 5476:127.0.0.1:5476 you@your-server.example.com保持该命令运行,然后在本地打开 http://localhost:5476/?token=<the token>。要在每次连接时自动创建转发,将其写入 ~/.ssh/config:
Host your-server.example.com
LocalForward 5476 127.0.0.1:5476如果端口 5476 已在笔记本电脑上被占用,只修改左侧的端口号:ssh -N -L 45476:127.0.0.1:5476 you@your-server.example.com,然后访问 http://localhost:45476/?token=...。第二个代理共享此服务器后,你很快就需要像这样叠加端口转发,因为自托管 open-kritt 进行安全扫描会在同一台服务器上、5173 端口创建另一个仅绑定 loopback 的仪表板。
通过隧道访问时,需要注意一个已记录的行为:网关会将转发的请求识别为远程请求,因此仪表板中的配置写入和密钥显示端点会拒绝这些请求。通过 SSH 无法保存设置并不表示程序出错,而是该行为的正常结果。请直接在主机上编辑配置:
docker cp kirocrew:/home/kirocrew/.kiro/crew/config.json .
# edit config.json here
docker cp config.json kirocrew:/home/kirocrew/.kiro/crew/config.json
docker exec -u 0 kirocrew chown kirocrew:kirocrew /home/kirocrew/.kiro/crew/config.json
docker restart kirocrew对于手机访问,该项目指向 Tailscale 的 tailscale serve,使控制面板保持在您自己的 tailnet 内,而不是暴露在公网主机名下。优先采用这种方式,不要使用公网反向代理。令牌会出现在 URL 中,而 URL 会被写入沿途每个访问日志。该规则针对的是端口后面运行的内容,而不是端口本身:例如 Halcyon,它会将 Jellyfin 媒体库重建为可浏览的 90 年代视频商店 供其他人访问,适合作为反向代理的候选;而能够在您的服务器上执行命令的网关则不适合。
尽可能缩小代理的影响范围
容器首次启动时会探测是否支持沙箱,探测结果决定代理能否执行任何操作。如果支持命名空间隔离,代理的子进程会在隔离环境中运行。如果不支持且未设置 KIROCREW_ALLOW_UNSANDBOXED=1,系统会拒绝执行,而不是在无隔离的情况下运行。因此,如果网关看似正常,但每个任务都停滞不前,通常就是这个原因。该决定会记录在首次运行生成的 docker logs kirocrew 中。项目还提供了一个 seccomp(安全计算模式)配置文件,可以应用该配置:
curl -fsSL https://raw.githubusercontent.com/kirodotdev/KiroCrew/main/docker/seccomp/kirocrew-seccomp.json \
-o /opt/kirocrew/kirocrew-seccomp.json security_opt:
- seccomp:./kirocrew-seccomp.json如果确实设置了 KIROCREW_ALLOW_UNSANDBOXED=1,必须明确发生了什么变化:现在,容器是代理与服务器之间唯一的隔离边界。项目的警告值得完整重申。不要挂载任何不愿直接交给代理的主机路径。实际上,这意味着不能挂载 Docker socket、/ 的任何 bind mount,也不能挂载存放其他服务数据的目录。
其余安全措施适用于所有获准执行命令的代理。将凭据权限限制在它所需的一个代码仓库或一个 bucket,不要使用具有整个账户权限的个人令牌。使用专用用户运行代理,并确保该用户的 home 目录中没有其他内容;这正是 VPS 上的最小权限用户 的用途。代理写入代码后还会运行这些代码,因此应为它提供一台允许被破坏的机器:用于代码代理的一次性 VM 比 compose 文件中的任何标志都更可靠,因为可以直接删除它,而不必清理环境。同样的思路也适用于 在 VPS 上安全运行 OpenClaw 和 在 VPS 上自行托管 Hermes 代理。工具同样会扩大影响范围:向代理提供网页搜索后,它获取的每个页面都可能成为不可信输入,因此 将代理指向自己的 SearXNG 实例 不只是网络配置决定,也是提示注入防护决定。计划任务还会在您休息时持续产生费用,因为推理费用会计入您的 Kiro 计划;因此,在添加每晚执行的任务前,请先设置 控制 VPS 上 AI 代理的费用 中说明的限制。
在每次升级前备份状态卷
先查找实际的卷名称。Compose 会使用项目名称为命名卷添加前缀。项目名称默认为目录名称,因此,/opt/kirocrew/compose.yaml 中声明为 kirocrew-home 的卷会创建为 kirocrew_kirocrew-home:
docker volume ls复制任何内容前,先停止网关。memory.db 和 memory_index.db 是 SQLite 数据库。在数据库写入期间复制,可能会捕获未完成的事务;恢复后,该文件会损坏。项目自身的迁移说明也提出了相同要求:只有在网关停止后,才能迁移 memory。先停止服务再复制并非 KiroCrew 特有的规则。如果同一台主机上还运行 photo server,PhotoPrism 和 Immich 对比给出了这两个服务各自需要使用的确切备份命令。
sudo systemctl stop kirocrew
docker run --rm -v kirocrew_kirocrew-home:/data:ro -v "$PWD":/backup \
alpine tar czf /backup/kirocrew-2026-08-06.tgz -C /data .
sudo systemctl start kirocrew将归档复制到主机之外的位置。恢复时使用相同的命令,但要先停止容器,并用 tar xzf 替换 tar czf:
sudo systemctl stop kirocrew
docker run --rm -v kirocrew_kirocrew-home:/data -v "$PWD":/backup \
alpine tar xzf /backup/kirocrew-2026-08-06.tgz -C /data
sudo systemctl start kirocrew迁移到新主机与在原主机上恢复是两项不同的工作,项目对此有明确说明。workspace/memory/ 下的聊天记录和项目备注会保留,两个数据库文件以及 config.json 也会保留。PID 文件、安全事件日志和 .env 与旧主机绑定,因此不要迁移它们,并在新主机上重新输入密钥。
如何回滚有问题的升级
升级操作很短。之所以安全,是因为您固定了版本。先创建备份,再修改标签:
sudo systemctl stop kirocrew
# take the backup here, as above
sudo nano /opt/kirocrew/compose.yaml # set the new image tag
sudo systemctl start kirocrew
docker compose -f /opt/kirocrew/compose.yaml ps
curl -s http://127.0.0.1:5476/api/health如果本机尚未有该镜像,docker compose up -d 会拉取它,因此修改标签就是完整的升级过程。回滚时使用相同的步骤,但改回旧版本号。这样可以获得升级前使用的确切镜像,因为版本标签不可变。
二进制文件可以顺利回滚。状态数据可能无法回滚。较新的网关可能会重写 config.json,或将内存数据库迁移为旧版网关无法读取的结构。截至 August 2026,没有记录在案的降级路径。因此,如果旧镜像启动后行为异常,不要继续排查。停止它,恢复升级前创建的备份,然后重新启动。这正是必须先创建备份的原因,也说明了在这个项目仍处于早期阶段时,先升级、之后再备份的做法为什么不可行。
这里尚未得到证明的内容
请如实看待此软件的版本成熟度。撰写本文时,版本 0.1.3 发布还不到几天,其发行说明只是自动生成的变更日志链接,而不是迁移说明,目前也没有升级记录可供参考。本指南中的任何内容都不是长期运行结果。因此,应在您自己的服务器上测量内存增长、数据库大小和调度器可靠性,不要直接假设它们符合预期。
有两种行为值得在依赖它们之前自行测试。第一,降级后的版本是否能读取较新版本写入的状态:应在问题尚不影响业务时,对卷的副本进行测试,不要等到服务中断期间再测试。第二,当计划任务即将运行时,Kiro 登录是否已过期,以及网关会如何处理这种情况。这两类问题都是新项目可能在版本之间悄然修复的边界情况,现在检查它们的成本很低。
FAQ
为什么 KiroCrew 控制面板无法通过服务器的公网 IP 打开?
因为示例配置将端口绑定到 loopback。-p 127.0.0.1:5476:5476 仅将容器端口映射到主机的 loopback 地址,这是有意设计的。通过 ssh -N -L 5476:127.0.0.1:5476 you@your-server 使用 SSH 转发端口,然后在笔记本电脑上打开 http://localhost:5476/?token=<token>。删除 127.0.0.1: 前缀后,网关将可从公网访问;防火墙规则无法限制它,因为 Docker 的已发布端口 DNAT 规则会在 ufw 过滤流量之前生效。
KiroCrew 将数据存储在哪里?应备份哪些内容?
所有数据都位于 ~/.kiro/crew 下;在容器镜像中,该路径是 /home/kirocrew/.kiro/crew,而 KIROCREW_HOME 可将其迁移到其他位置。在网关停止后,备份整个目录或整个 Docker 卷。memory.db 和 memory_index.db 是 SQLite 数据库,因此在网关写入期间复制可能导致数据不一致。迁移到新主机时,workspace/memory/、两个数据库文件和 config.json 需要一并迁移;PID 文件、安全事件日志和 .env 属于旧主机。
应使用 stable 标签还是版本标签?
使用版本标签。stable 会在每次发布新版本时更新,因此下一次 pull 后,正在运行的版本可能发生变化,而标签本身无法说明当前运行的具体版本。0.1.3 等版本标签不可变,这正是回滚能够生效的原因:恢复旧版本号后,即可获取完全相同的镜像。截至 6 August 2026,最新版本是 0.1.3。
为什么我的代理拒绝执行所有命令?
容器首次启动时会检测沙箱支持。如果无法隔离代理子进程,且未设置 KIROCREW_ALLOW_UNSANDBOXED=1,容器会拒绝执行这些进程,而不是在无隔离状态下运行它们。因此,网关看起来正常,但每个任务都会停滞。docker logs kirocrew 可显示首次运行时的沙箱决策。设置该变量后,容器将成为代理与主机之间唯一的隔离边界。因此,如果设置该变量,不要挂载任何你不愿直接交给代理的内容。
自托管 KiroCrew 需要 Kiro 帐户吗?
需要,截至 August 2026。KiroCrew 是基于 Apache 2.0 许可的自由软件,但它驱动 kiro-cli;该组件需要一次性登录,代理推理费用则计入 Kiro 方案。在容器中运行 docker exec -it kirocrew kiro-cli login,然后在浏览器中批准设备代码。在登录完成前,网关会启动,控制面板也会加载,但代理没有可连接的模型。