
最近把我折腾 GitHub self-hosted runner 的一些经验,整理成了一个开源小工具 Runner Fleet(千帆)。它的目标很简单,把 Runner 当作服务来管,而不是当作一堆目录来管。
我希望它能少点手工、少点踩坑,把 1~5 台机器上自托管 Runner 的部署、扩容、维护,收敛成一套可复用的流程。前期小规模好上手,后面要提高并发度、提升利用率、扩大吞吐,也不至于失控。
在私有化环境里跑 GitHub Actions,自托管 Runner 往往不是难在跑起来,而是难在长期稳定地管起来。
一开始你可能只是手工执行几次 config.sh(GitHub Actions Runner 的配置脚本),把 Runner 挂到某个 repo 或 org 上。 但当你同时维护多个 Runner、多个标签、多个仓库,并且把它们跑在内网宿主机上之后,问题会很快集中爆发

Runner Fleet - 开源 GitHub Runner 私有化部署方案
所以我写了一个开源项目 soulteary/runner-fleet[1] 如果你也在用 self-hosted runner 跑 CI/CD,欢迎来试试、提 issue 或 PR。如果确实帮你省了时间,也欢迎一键三连。
Runner Fleet 做的事情很明确:提供一个轻量的 Web 管理面,把 Runner 的安装、注册、启停、编辑、状态聚合,以及自愈巡检,收拢成一套可重复的工程流程。
同时它提供两种运行模式:既可以在宿主机上直接跑多个 Runner(非容器模式),最大化复用宿主机环境;也可以做到一 Runner 一容器(容器模式),在同一台机器上通过多个容器做环境隔离,方便你在私有化环境里按需选择。
自托管 Runner 真正容易失控的根源不是规模,而是缺少一套稳定范式。
我更推荐把 Runner 的拆分逻辑从按仓库改成按职责,让每个 Runner 的环境边界足够清晰。这样出问题能快速定位,迁移扩容也更容易。
通常可以这样拆分
Runner Fleet 的设计天然适配这种范式:因为它把每一个 Runner 都当作配置项加生命周期对象,而不是你记得的某台机器上的某个目录。
这篇文章中,我们主要来聊聊“容器”场景的使用,其他的部分,有机会的时候再聊。
服务器配置参考《搭建 Ubuntu 24.04 基础开发环境指南[2]》,把 Ubuntu 更新到最新状态,安装 Docker Engine 与 Compose 插件,并让当前用户可以免 sudo 使用 Docker 即可:
sudo apt update && sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg lsb-release
curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudochmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/ \
$(lsb_release -cs) stable" | sudotee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo gpasswd -a ${USER} docker安装完毕,通常服务器会提示“需要重启”:
*** System restart required ***输入 sudo reboot,完成服务器重启,一个能够运行 GitHub Action Runner 的服务器环境就准备好啦。
容器模式不是为了“看起来更云原生”,而是为了解决真实的三个痛点:
我们从 GitHub Release 页面[3],找到最新的版本,使用 docker pull 对镜像进行下载:
docker pull ghcr.io/soulteary/runner-fleet:v1.1.0
docker pull ghcr.io/soulteary/runner-fleet:v1.1.0-runner创建独立的 Docker 容器网络:
docker network create runner-net创建两个目录:
mkdir -p config && chown 1001:1001 config
mkdir -p runners && chown 1001:1001 runners然后,创建容器使用的 .env 基础配置:
# 此处账号/密码换成自己需要的,仅为示例
BASIC_AUTH_USER=soulteary
BASIC_AUTH_PASSWORD=soulteary
# 使用镜像
MANAGER_IMAGE=ghcr.io/soulteary/runner-fleet:v1.1.0
RUNNER_IMAGE=ghcr.io/soulteary/runner-fleet:v1.1.0-runner
# 固定配置
CONTAINER_MODE=true
CONTAINER_NETWORK=runner-net
JOB_DOCKER_BACKEND=host-socket
RUNNERS_BASE_PATH=/app/runners
SERVER_PORT=8080
SERVER_ADDR=0.0.0.0接下来,我们分别执行下面的命令,先将 Docker GID 添加到刚刚的 .env 文件中:
DOCKER_GID=$(getent group docker 2>/dev/null | cut -d: -f3)
[ -n "$DOCKER_GID" ] && echo "DOCKER_GID=$DOCKER_GID" >> .env然后,将 runner 在宿主机中的真实路径也传递到 .env 中:
VOLUME_HOST_PATH=$(realpath runners 2>/dev/null || (cd runners 2>/dev/null && pwd -P))
echo "VOLUME_HOST_PATH=$VOLUME_HOST_PATH" >> .env最后,编写 docker-compose.yml:
services:
runner-manager:
# 默认使用稳定版 v1.0.0;开发/尝鲜可设 .env:MANAGER_IMAGE=ghcr.io/soulteary/runner-fleet:main
image:${MANAGER_IMAGE:-ghcr.io/soulteary/runner-fleet:v1.0.0}
container_name:runner-manager
# 使用 job_docker_backend: dind 时请先启动 DinD:docker compose --profile dind up -d
# 容器模式下必须用宿主机 socket,否则 Manager 无法创建 Runner 容器;默认 unix,勿改为 tcp://runner-dind:2375
# 全容器时可通过 .env 传入以下变量覆盖 config/config.yaml,无需改 config 文件:CONTAINER_MODE、VOLUME_HOST_PATH、JOB_DOCKER_BACKEND、CONTAINER_NETWORK、RUNNER_IMAGE、RUNNERS_BASE_PATH 等
environment:
DOCKER_HOST:${DOCKER_HOST:-unix:///var/run/docker.sock}
BASIC_AUTH_USER:${BASIC_AUTH_USER:-}
BASIC_AUTH_PASSWORD:${BASIC_AUTH_PASSWORD:-}
# 以下可选:覆盖 config/config.yaml,全容器时无需改 config 文件(见 .env.example)
CONTAINER_MODE:${CONTAINER_MODE:-}
VOLUME_HOST_PATH:${VOLUME_HOST_PATH:-}
JOB_DOCKER_BACKEND:${JOB_DOCKER_BACKEND:-}
CONTAINER_NETWORK:${CONTAINER_NETWORK:-}
RUNNER_IMAGE:${RUNNER_IMAGE:-}
RUNNERS_BASE_PATH:${RUNNERS_BASE_PATH:-}
ports:
-"${MANAGER_PORT:-8080}:8080"
# 仅挂载 config 目录(勿挂载 config/config.yaml 单文件,否则宿主机无该文件时 Docker 会创建空文件导致启动失败)
volumes:
-./config:/app/config
-./runners:/app/runners
-/var/run/docker.sock:/var/run/docker.sock
# 容器模式需 Manager 访问宿主机 Docker:加入宿主机 docker 组(GID 用 getent group docker 查看,常见为 999)
group_add:
-"${DOCKER_GID:-999}"
networks:
-runner-net
restart:unless-stopped
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8080/health"]
interval:30s
timeout:5s
start_period:15s
retries:3
# runner-net 设为 external,避免 compose down 时删除网络导致已注册的 Runner 容器
#(由 Manager 动态创建、未在 compose 中定义)无法启动。首次使用请执行:docker network create runner-net
networks:
runner-net:
external: true当所有配置都就绪后,执行 docker compose up -d,软件部署就完成了。默认情况下,我们打开 http://IP或域名:8080,输入上面配置中定义的账号和密码:

Runner Fleet - Basic Auth 登录
回车之后,就能够打开 Runner 配置界面,开始使用啦。

Runner Fleet - WebUI 界面

GitHub - Action Runner 配置页面
当 Fleet 部署好之后,我们访问 GitHub 上任意项目的配置页面,打开 Action 菜单的 Runner 配置页面。

GitHub - 复制配置 token 指令
点击 “New self-hosted runner”,来到不同操作系统的 Runner 配置页面。我们在官方提供的 macOS / Linux 架构配置示例中,找到带有 token 的配置命令,单机该命令,命令会自动粘贴到剪贴板。

Fleet - 粘贴带有 Token 的命令到解析器
我们将命令粘贴到 fleet 的解析器中,点击“解析并填充”按钮。

Fleet - 一键解析
配置中的内容,就会都自动装填到创建 Runner 表单中,我们可以在创建之前,调整或修改各种细节(比如命名):

Fleet - 自动添加 Runner
如果你觉得上面的内容都就绪了,点击“添加 Runner” 按钮,程序将自动初始化 Runner。

Fleet - 刷新页面的 Runner 列表
添加完毕,我们在 Fleet Runner 列表中就能够看到刚刚添加的内容啦。

GitHub - Runner 列表实例就绪
与此同时,GitHub Runner 页面的 Runner 实例也就就绪啦。
到现在为止,我们已经将所有 GitHub Action CI/CD 的环境和服务都配置就绪啦。真正使用上私有化的 Runner 只差一步,就是修改 .github/workflows 中的 CI 文件,让他们使用自部署的 Runner,而非 GitHub 上的共享 Runner。
我们随便找一个之前已经启用了 GitHub Action 的项目,打开它任意的 workflows 配置文件:
jobs:
# Code formatting check
fmt:
name:CodeFormattingCheck
runs-on:ubuntu-latest
steps:
-name:Checkoutcode
uses: actions/checkout@v6上面配置文件中,有一句配置是: runs-on: ubuntu-latest,我们将它修改为 runs-on: self-hosted 。

GitHub - 使用自托管 Runner 运行任务
再次运行 CI 任务的时候,就会调用我们的私有化服务来执行任务啦,是不是很简单?
Fleet 里还有一些其他的玩法,包括裸金属使用、Dind 模式使用,这些在后续有机会的时候,我会陆续分享出来。
在设计工具的时候,我希望我们能够通过写明确的、简单的配置来规避掉大量奇奇怪怪的错误,也希望 UI 操作都能够直接落盘,让排错的证据可追踪。当然,因为环境的复杂性,报错的信息也应该即时反馈在界面该有的地方。
或许,这才是工程化工具该有的样子。
--EOF
我们有一个小小的折腾群,里面聚集了一些喜欢折腾、彼此坦诚相待的小伙伴。
我们在里面会一起聊聊软硬件、HomeLab、编程上、生活里以及职场中的一些问题,偶尔也在群里不定期的分享一些技术资料。
关于交友的标准,请参考下面的文章:
致新朋友:为生活投票,不断寻找更好的朋友[4]
当然,通过下面这篇文章添加好友时,请备注实名和公司或学校、注明来源和目的,珍惜彼此的时间 :D
关于折腾群入群的那些事[5]
如果你觉得内容还算实用,欢迎点赞分享给你的朋友,在此谢过。
如果你想更快的看到后续内容的更新,请戳 “点赞”、“分享”、“在看” ,这些免费的鼓励将会影响后续有关内容的更新速度。
本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)
[1] soulteary/runner-fleet: https://github.com/soulteary/runner-fleet
[2] 搭建 Ubuntu 24.04 基础开发环境指南: https://soulteary.com/2025/01/17/guide-to-setting-up-ubuntu-24-04-basic-development-environment.html
[3] GitHub Release 页面: https://github.com/soulteary/runner-fleet/releases
[4] 致新朋友:为生活投票,不断寻找更好的朋友: https://zhuanlan.zhihu.com/p/557928933
[5] 关于折腾群入群的那些事: https://zhuanlan.zhihu.com/p/56159997