Semaphore v2.19.17 Docker Compose 部署指南
Semaphore v2.19.17 官方 semaphoreui/semaphore 镜像 Docker Compose 部署实录:MySQL 主线从 0 到 1 部署验证、SQLite/PostgreSQL 方言对比、数据持久化、config.json 生成与挂载、requirements.txt 自动装依赖、排障实录与运维命令速查。
Semaphore v2.19.17 Docker Compose 部署指南
目标:用官方
semaphoreui/semaphore镜像 + Docker Compose 从 0 到 1 部署一套可用的 Semaphore v2.19.17(Ansible Web 控制台),覆盖 MySQL 主线部署、SQLite/PostgreSQL 方言切换、数据持久化验证、config.json 配置文件挂载、Python 依赖注入与常见故障排查。本文全部命令与回显均在 Ubuntu 22.04(x86_64,内核 5.15)+ Docker Compose v2 环境实测通过,部署形态为 Semaphore Server 内置 Runner 单机版。
一、环境信息
本节列出实测环境的软硬件信息与部署后各服务监听情况,便于对照复现。
- 操作系统:Ubuntu 22.04(x86_64)
- Docker:Docker Engine 24+,Docker Compose v2(
docker compose子命令形式) - Semaphore 版本:v2.19.17(镜像
semaphoreui/semaphore:v2.19.17,启动后自报版本v2.19.17-f8cefe6-1792391878) - 数据库:MySQL 8.0(主线);SQLite / PostgreSQL 14(方言对比实测)
- 宿主端口:8151(映射到 Semaphore 的 3000 端口,避开宿主机已占用端口)
- 部署目录:
/opt/semaphore/(下文所有docker-compose.yml、数据目录均放此处) - 管理账号:
admin/q1w2e3r4(部署时通过环境变量初始化)
二、镜像选型说明
Semaphore 官方在 Docker Hub 发布两个镜像,选型前先分清用途:
| 镜像 | 用途 | 适用场景 |
|---|---|---|
semaphoreui/semaphore | Semaphore Server(Web UI + API + 调度),内置本地 Runner | 单机一体化部署,绝大多数场景 |
semaphoreui/runner | 独立任务执行器(Runner),需注册到已有 Server | 任务量大、需要把执行节点与 Web 节点分离时 |
版本 tag 如何选:生产部署必须锁定具体版本 tag(如 v2.19.17),不要用 latest——latest 随官方发布漂移,升级时数据库迁移不可逆,回滚困难。本文所有配置均以 v2.19.17 为准。镜像拉取实测(Digest 为 v2.19.17 当前线上摘要,可用于校验):
docker pull semaphoreui/semaphore:v2.19.17
v2.19.17: Pulling from semaphoreui/semaphore
Digest: sha256:008dacd9f1adbd92e8a048f1786ae1dd9bed2a352aca2eb30b9837de5706e7f7
Status: Image is up to date for semaphoreui/semaphore:v2.19.17
docker.io/semaphoreui/semaphore:v2.19.17
💡 说明:Semaphore 的组件模型(Server 与 Runner 分离、社区版功能边界)在 《Semaphore v2.19.17 介绍与选型指南》中有完整展开,本文聚焦部署落地。
三、前置条件
开始部署前确认以下条件全部满足,每一项都给出验证命令与实测回显。
3.1 Docker 与 Compose 可用
docker --version && docker compose version
Docker version 24.0.7, build afdb53d
Docker Compose version v2.24.5
3.2 目标端口未被占用
Semaphore 默认监听 3000,宿主机映射端口可自选。选端口前先用 ss 确认空闲:
ss -ltn | grep -E ':(8151|8133)\b' || echo "ports free"
ports free
⚠️ 排障实录:本次部署最初选的 8133 端口在正式执行时被宿主机上一个 Java 进程抢占,
docker compose up报failed to bind host port 0.0.0.0:8133/tcp: address already in use(完整排障过程见第十二节 FAQ 1)。部署前务必先ss -ltn核对。
3.3 磁盘与内存
Semaphore Server 本身是 Go 单进程,空载内存约 100–200MB;MySQL 8.0 建议预留 1GB 以上内存与 5GB 磁盘(镜像约 1GB + 数据库初始数据)。任务执行时 Ansible playbook 会克隆代码仓库到临时目录,按最大仓库体积预留 /tmp 或自定义 tmp_path 的磁盘空间。
四、部署步骤(MySQL 主线)
本节从空目录开始,完成 docker-compose.yml 编写、启动、初始化验证全流程。MySQL 是官方文档的默认推荐方言,作为本文主线。
4.1 创建部署目录
mkdir -p /opt/semaphore/data/semaphore-config && cd /opt/semaphore
(命令无输出,data/semaphore-config 是 Semaphore 配置目录的挂载点,首次启动时会自动写入 config.json。)
4.2 编写 docker-compose.yml
在 /opt/semaphore/docker-compose.yml 写入以下内容(与官方文档模板一致,仅将端口映射改为 8151、密码改为本文统一示例口令):
name: semaphore-v2.19.17-stack
services:
mysql:
restart: unless-stopped
image: mysql:8.0
hostname: mysql
volumes:
- ./data/mysql:/var/lib/mysql
environment:
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
MYSQL_DATABASE: semaphore
MYSQL_USER: semaphore
MYSQL_PASSWORD: q1w2e3r4
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-usemaphore", "-pq1w2e3r4"]
interval: 5s
timeout: 5s
retries: 20
semaphore:
restart: unless-stopped
ports:
- "8151:3000"
image: semaphoreui/semaphore:v2.19.17
volumes:
- ./data/semaphore-config:/etc/semaphore
environment:
SEMAPHORE_DB_USER: semaphore
SEMAPHORE_DB_PASS: q1w2e3r4
SEMAPHORE_DB_HOST: mysql
SEMAPHORE_DB_PORT: 3306
SEMAPHORE_DB_DIALECT: mysql
SEMAPHORE_DB: semaphore
SEMAPHORE_PLAYBOOK_PATH: /tmp/semaphore/
SEMAPHORE_ADMIN_PASSWORD: q1w2e3r4
SEMAPHORE_ADMIN_NAME: admin
SEMAPHORE_ADMIN_EMAIL: admin@localhost
SEMAPHORE_ADMIN: admin
SEMAPHORE_ACCESS_KEY_ENCRYPTION: IrPXhBcHhRyjt42Ej+eq7tdyicGzPW38Sh7GzitQ1mk=
SEMAPHORE_LDAP_ACTIVATED: 'no'
TZ: Asia/Shanghai
depends_on:
mysql:
condition: service_healthy
networks:
default:
name: semaphore-v2.19.17-net
逐项说明:
name: semaphore-v2.19.17-stack:Compose 项目名。显式指定可避免多套部署目录同名导致docker compose down误停其他实例。mysql服务:MYSQL_RANDOM_ROOT_PASSWORD让 root 密码随机生成(业务库用独立的semaphore账号,不用 root);./data/mysql把数据库文件持久化到宿主机,重建服务不丢数据。healthcheck:用mysqladmin ping探活,depends_on的condition: service_healthy保证 Semaphore 严格等 MySQL 就绪后才启动——Semaphore 启动时要连库执行迁移,库没就绪会启动失败。semaphore服务:8151:3000把 Web 端口映射到宿主机 8151;./data/semaphore-config挂载到/etc/semaphore,首次启动自动生成的config.json落在宿主机该目录,可审查、可备份。SEMAPHORE_ACCESS_KEY_ENCRYPTION:加密数据库中访问密钥(Key Store)的 32 字节 base64 密钥,用head -c32 /dev/urandom | base64生成,每套部署必须独立生成并妥善保管——丢失该密钥,数据库里已存的 Key Store 条目将无法解密。本文值仅为示例。
4.3 启动
docker compose up -d
Network semaphore-v2.19.17-net Creating
Network semaphore-v2.19.17-net Created
Container semaphore-v21917-stack-mysql-1 Creating
Container semaphore-v21917-stack-mysql-1 Created
Container semaphore-v2.19.17-web Creating
Container semaphore-v2.19.17-web Created
Container semaphore-v21917-stack-mysql-1 Starting
Container semaphore-v21917-stack-mysql-1 Started
Container semaphore-v21917-stack-mysql-1 Waiting
Container semaphore-v21917-stack-mysql-1 Healthy
Container semaphore-v2.19.17-web Starting
Container semaphore-v2.19.17-web Started
💡 技术拓展:
depends_on有两种等待语义——短格式(depends_on: [mysql])只保证启动顺序不保证可用性;长格式(condition: service_healthy)配合 healthcheck 才是真正的"依赖就绪"。常见 condition 对比:
condition 行为 适用场景 service_started依赖容器已启动(默认) 依赖无健康检查能力的轻量服务 service_healthy依赖健康检查通过 数据库类、需要初始化完成的服务 service_completed_successfully依赖容器成功退出 一次性初始化任务(如迁移脚本) Semaphore 强依赖数据库完成初始化,必须用
service_healthy。
4.4 首次启动过程解读
首次启动时 Semaphore 检测到配置目录没有 config.json,会用 SEMAPHORE_ADMIN_* 等环境变量自动执行无人值守初始化(交互式问答的自动应答版):生成 config.json → 连库执行 268 项数据库迁移 → 创建管理员账号。通过 docker compose logs semaphore 可以看到完整过程(节选关键段落,实测原文):
Pinging database on mysql port 3306...
Generating setup file ...
Executing semaphore setup
Hello! You will now be guided through a setup to:
1. Set up configuration for a MySQL/MariaDB database
2. Set up a path for your playbooks (auto-created)
3. Run database Migrations
4. Set up initial semaphore user & password
What database to use:
1 - MySQL
2 - BoltDB (NOT SUPPORTED)
3 - PostgreSQL
4 - SQLite
(default 1):
db Hostname (default 127.0.0.1:3306):
db User (default root):
db Password:
db Name (default semaphore):
Playbook path (default /tmp/semaphore):
Public URL (optional, example: https://example.com/semaphore):
Enable email alerts? (yes/no) (default no):
Enable telegram alerts? (yes/no) (default no):
Enable slack alerts? (yes/no) (default no):
Enable Rocket.Chat alerts? (yes/no) (default no):
Enable Microsoft Team Channel alerts? (yes/no) (default no):
Enable LDAP authentication? (yes/no) (default no):
Config output directory (default /home/semaphore):
Running: mkdir -p /etc/semaphore..
Configuration written to /etc/semaphore/config.json..
Pinging db..
Running db Migrations..
Executing migration v0.0.0 (at 2026-10-24 02:05:54.356595607 +0000 UTC)...
Creating migrations table
[1/268] [2/127] [3/301] [4/269] [5/290] [6/314] [7/238] [8/593] [9/198] [10/274] [11/238] [12/0]
Executing migration v1.0.0 (at 2026-10-24 02:05:54.466363147 +0000 UTC)...
[1/59] [2/57] [3/82] [4/87]
...
Executing migration v2.19.14 (at 2026-10-24 02:05:58.127657261 +0000 UTC)...
[1/0]
Migrations Finished
time="2026-10-24T10:05:58+08:00" level=warning msg="no rows in result set" fields.level=Warn
> Username: > Email: > Your name: > Password:
You are all setup admin!
Re-launch this program pointing to the configuration file
./semaphore server --config /etc/semaphore/config.json
To run as daemon:
nohup ./semaphore server --config /etc/semaphore/config.json &
You can login with admin@localhost or admin.
No additional python dependencies to install
Starting semaphore server
MySQL semaphore@mysql:3306 semaphore
Tmp Path (projects home) /tmp/semaphore
Semaphore v2.19.17-f8cefe6-1792391878
Interface
Port :3000
Server is running
关键信息点:Configuration written to /etc/semaphore/config.json 说明配置文件已生成;Migrations Finished 表示从 v0.0.0 到 v2.19.14 的全部迁移执行完毕;末尾 MySQL semaphore@mysql:3306 semaphore + Server is running 确认服务在 MySQL 方言下正常运行(no rows in result set 的 Warn 是首次启动时任务表为空的正常提示,非错误)。
4.5 验证部署结果
① 服务状态:
docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
semaphore-v2.19.17-web semaphoreui/semaphore:v2.19.17 "/sbin/tini -- /usr/…" semaphore 2 hours ago Up 2 hours 0.0.0.0:8151->3000/tcp, [::]:8151->3000/tcp
semaphore-v21917-stack-mysql-1 mysql:8.0 "docker-entrypoint.s…" mysql 2 hours ago Up 2 hours (healthy) 3306/tcp, 33060/tcp
② API 探活:Semaphore 未提供无认证的 ping 端点,用登录接口返回 204 判定服务可用:
curl -s -o /dev/null -w '%{http_code}' -X POST http://localhost:8151/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"auth":"admin","password":"q1w2e3r4"}'
204
③ 身份与会话验证:先登录拿 Cookie,再取当前用户信息:
curl -s -c cookie.txt -X POST http://localhost:8151/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"auth":"admin","password":"q1w2e3r4"}' -o /dev/null
curl -s -b cookie.txt http://localhost:8151/api/user
{"id":1,"created":"2026-10-24T02:05:58Z","username":"admin","name":"admin","email":"admin@localhost","admin":true,"external":false,"alert":false,"pro":false,"can_create_project":true,"has_active_subscription":false}
④ Web UI:浏览器访问 http://<服务器IP>:8151/,用 admin / q1w2e3r4 登录即可进入项目列表页。
四项全过,部署完成。
五、数据持久化说明
本节说明哪些数据落在宿主机、如何验证"重建服务不丢数据"。
持久化清单:
| 数据 | 宿主机位置 | 挂载点 | 说明 |
|---|---|---|---|
| MySQL 数据 | ./data/mysql | /var/lib/mysql | 全部业务数据(项目、模板、任务历史、用户) |
| Semaphore 配置 | ./data/semaphore-config | /etc/semaphore | config.json(含三个加密密钥) |
| 临时工作目录 | (未挂载,容器内 /tmp/semaphore) | /tmp/semaphore | 任务临时文件、仓库克隆缓存,可随时重建,无需持久化 |
⚠️ 最容易被忽略的持久化项是
config.json里的三个密钥:cookie_hash、cookie_encryption、access_key_encryption。access_key_encryption加密着数据库中的 Key Store(SSH 私钥、API Token 等),必须单独备份——只有数据库备份、没有密钥备份,恢复出来的 Key Store 全部无法解密。
重建验证实测:向系统写入一条数据(创建名为 demo 的项目),随后 docker compose down 删除服务与网络(数据目录保留),再 docker compose up -d 重建,验证数据完好:
docker compose down && docker compose up -d
Container semaphore-v2.19.17-web Stopping
Container semaphore-v2.19.17-web Stopped
Container semaphore-v21917-stack-mysql-1 Stopping
Container semaphore-v21917-stack-mysql-1 Stopped
Container semaphore-v21917-stack-mysql-1 Removing
Container semaphore-v2.19.17-web Removing
Container semaphore-v2.19.17-web Removed
Container semaphore-v21917-stack-mysql-1 Removed
Network semaphore-v2.19.17-net Removing
Network semaphore-v2.19.17-net Removed
Network semaphore-v2.19.17-net Creating
Network semaphore-v2.19.17-net Created
Container semaphore-v21917-stack-mysql-1 Creating
Container semaphore-v21917-stack-mysql-1 Created
Container semaphore-v2.19.17-web Creating
Container semaphore-v2.19.17-web Created
Container semaphore-v21917-stack-mysql-1 Starting
Container semaphore-v21917-stack-mysql-1 Started
Container semaphore-v21917-stack-mysql-1 Waiting
Container semaphore-v21917-stack-mysql-1 Healthy
Container semaphore-v2.19.17-web Starting
Container semaphore-v2.19.17-web Started
重建后重新登录并查询项目列表(/api/user 登录 → /api/projects 查询):
login=204
[{"id":1,"name":"demo","created":"2026-10-24T02:00:15Z","type":""}]
demo 项目仍在,持久化验证通过。注意二次启动日志不再出现迁移过程——config.json 已存在,Semaphore 直接读配置连库启动,docker compose logs semaphore 尾部为:
semaphore-v2.19.17-web | Interface
semaphore-v2.19.17-web | Port :3000
semaphore-v2.19.17-web | Server is running
六、环境变量表
Semaphore 镜像通过环境变量接收配置,首次启动时将其转换为 config.json。部署常用变量如下(均为实测生效项):
| 环境变量 | 作用 | 示例值 / 说明 |
|---|---|---|
SEMAPHORE_DB_DIALECT | 数据库方言 | mysql / postgres / sqlite(v2.16+ 支持 sqlite) |
SEMAPHORE_DB_HOST | 数据库主机 | 主机名或 IP;MySQL 形如 mysql:3306 可带端口 |
SEMAPHORE_DB_PORT | 数据库端口 | 3306(MySQL)/ 5432(PostgreSQL) |
SEMAPHORE_DB_USER | 数据库用户名 | semaphore |
SEMAPHORE_DB_PASS | 数据库密码 | 与 MySQL 服务的 MYSQL_PASSWORD 一致 |
SEMAPHORE_DB | 数据库名 | semaphore;SQLite 方言时此变量存数据库文件路径 |
SEMAPHORE_PLAYBOOK_PATH | 项目临时目录(旧名) | /tmp/semaphore/;等价于配置项 tmp_path |
SEMAPHORE_ADMIN | 初始管理员登录名 | admin |
SEMAPHORE_ADMIN_NAME | 初始管理员显示名 | admin |
SEMAPHORE_ADMIN_EMAIL | 初始管理员邮箱 | admin@localhost(也是登录名之一) |
SEMAPHORE_ADMIN_PASSWORD | 初始管理员密码 | 仅首次初始化生效,之后改密码走 Web UI 或 CLI |
SEMAPHORE_ACCESS_KEY_ENCRYPTION | Key Store 加密密钥 | head -c32 /dev/urandom | base64 生成 |
SEMAPHORE_LDAP_ACTIVATED | 是否启用 LDAP(旧式单目录) | 'no';多目录 LDAP 见 v2.20+ 的 ldap_providers |
TZ | 时区 | Asia/Shanghai(影响日志时间戳与定时任务,见第七节 schedule.timezone) |
关键规则:
- 环境变量优先于 config.json。首次启动后若直接改环境变量(如改数据库密码),重启后新值会覆盖
config.json里的旧值——两处都存在时以环境变量为准。 SEMAPHORE_ADMIN_PASSWORD只在首次生成config.json时消费一次;之后修改该变量不会改密码(防误改),重置密码用semaphore users change-by-login(见 《Semaphore v2.19.17 常用 CLI 命令与工具详解》)。- 敏感变量支持
_FILE后缀形态:SEMAPHORE_ADMIN_PASSWORD_FILE: /run/secrets/admin_pw表示从文件读值,Docker Swarm / secrets 场景用,避免密码明文进 Compose 文件。
七、配置参数说明(config.json)
首次启动生成的 config.json 是 Semaphore 的核心配置文件,挂载在宿主机 ./data/semaphore-config/config.json。MySQL 主线实测生成结果(密钥为实测随机值,已按示例处理):
{
"mysql": {
"host": "mysql:3306",
"user": "semaphore",
"pass": "q1w2e3r4",
"name": "semaphore"
},
"dialect": "mysql",
"tmp_path": "/tmp/semaphore",
"cookie_hash": "f5I+atUJX6MA5xNnpRimZqDKYAM8EzqEjlXn6b5w5zg=",
"cookie_encryption": "aoSl2CFTBhktRX83U1ivNJ8TXKWW95UAqIpZDylMN8A=",
"access_key_encryption": "9hm9XNftIltnY05Q50cLHXvr/Vdsj6RmmZiz5M8QEus="
}
逐项说明:
mysql.host/user/pass/name:数据库连接四要素,来自SEMAPHORE_DB_*环境变量;host格式主机:端口。dialect:数据库引擎,mysql/postgres/sqlite三选一。首次启动前必须定死,之后更换方言需要自行迁移数据,程序不做自动转换。tmp_path:任务临时目录(playbook 仓库克隆、ansible 运行缓存的根),默认/tmp/semaphore,可改为大容量分区路径。cookie_hash/cookie_encryption:会话 Cookie 的哈希与加密密钥,首次初始化自动随机生成;cookie_encryption更换后所有用户会话立即失效(需重新登录)。access_key_encryption:Key Store 加密密钥(来自SEMAPHORE_ACCESS_KEY_ENCRYPTION),决定数据库中 SSH 密钥、密码等敏感条目的加解密。
三个密钥的完整生成命令:
head -c32 /dev/urandom | base64
IrPXhBcHhRyjt42Ej+eq7tdyicGzPW38Sh7GzitQ1mk=
(每次执行输出都不同,属正常现象。)
config.json 改动如何生效:该文件只在 Semaphore 进程启动时读取一次,改完必须重启服务:
docker compose restart semaphore
Container semaphore-v2.19.17-web Restarting
Container semaphore-v2.19.17-web Started
💡 说明:config.json 可配置项远不止初始生成的这六项——定时任务时区(
schedule.timezone)、git 客户端与重试(git_client/git_attempts)、任务并发(max_parallel_tasks)、TLS、通知渠道等共 130+ 项,逐项含义与默认值在 《Semaphore v2.19.17 配置参数详解》中按官方 Reference 全量展开,部署阶段先掌握本节六项即可。
八、配置文件挂载与自定义 Python 依赖
本节介绍 config 目录的另一个用途:放入 requirements.txt 让镜像启动时自动安装额外的 Python 依赖(部分 Ansible 模块/集合需要)。
用法:在配置目录(挂载到 /etc/semaphore 的目录)放一个 requirements.txt:
netaddr
重新创建服务(restart 不会重新读取挂载内容变化,需要 up -d 触发重建或直接重启后观察日志):
docker compose up -d --force-recreate semaphore
docker compose logs --tail 10 semaphore
实测日志(netaddr 被自动安装):
Installing additional python dependencies
...Collecting netaddr (from -r /etc/semaphore/requirements.txt (line 1))
Installing collected packages: netaddr
Successfully installed netaddr-1.3.0
[notice] A new release of pip is available: 26.2.1 -> 26.2.2
[notice] To update, run: pip install --upgrade pip
行为要点(官方文档明示 + 实测印证):
- 启动时检查
${SEMAPHORE_CONFIG_PATH}/requirements.txt(默认/etc/semaphore/requirements.txt),存在则执行pip3 install --upgrade -r;不存在则打印No additional python dependencies to install继续(首次启动日志可见此行)。 - 包安装在镜像内置的 Ansible 虚拟环境中(镜像已把该 venv 放在 PATH 首位),任务进程可直接使用。
- 每次启动都会重新安装,包不跨容器重建持久化——依赖多、或需要离线启动时,改用自定义镜像预装:
FROM semaphoreui/semaphore:v2.19.17
COPY requirements.txt /tmp/requirements.txt
RUN pip3 install --no-cache-dir -r /tmp/requirements.txt
- pip 失败会阻断启动:包名写错、缺编译依赖、断网,都会让服务起不来,排查看
docker compose logs semaphore里的 pip 输出。
九、多数据库方言部署对比(SQLite / PostgreSQL 实测)
Semaphore 支持三种数据库方言。官方模板主线是 MySQL/PostgreSQL,v2.16 起也支持 SQLite(单文件、零依赖)。本节给出两种替代方言的完整可用配置与实测启动回显,供轻量场景选型。
💡 技术拓展:三种方言如何选——
方言 运维成本 适用规模 特点 MySQL 8.0 需维护独立数据库服务 中大型、团队共用 官方主线推荐,生态成熟,备份工具链完整 PostgreSQL 14 需维护独立数据库服务 中大型 与 MySQL 同级,官方模板一等公民,函数/JSON 能力更强 SQLite 零依赖,单文件 个人/小团队/演示 v2.16+ 支持,一个文件即全部数据,备份=拷文件;并发写弱 本文主线采用 MySQL(团队场景与官方默认一致);个人单机试验推荐 SQLite。
9.1 SQLite 方言(单服务)
单服务即可运行,无需数据库服务。docker-compose-sqlite.yml:
name: semaphore-v2.19.17-stack
services:
semaphore:
restart: unless-stopped
ports:
- "8151:3000"
image: semaphoreui/semaphore:v2.19.17
container_name: semaphore-v2.19.17-web-sqlite
volumes:
- ./data/semaphore-config-sqlite:/etc/semaphore
environment:
SEMAPHORE_DB_DIALECT: sqlite
SEMAPHORE_DB_HOST: /etc/semaphore/semaphore.sqlite
SEMAPHORE_PLAYBOOK_PATH: /tmp/semaphore/
SEMAPHORE_ADMIN_PASSWORD: q1w2e3r4
SEMAPHORE_ADMIN_NAME: admin
SEMAPHORE_ADMIN_EMAIL: admin@localhost
SEMAPHORE_ADMIN: admin
SEMAPHORE_ACCESS_KEY_ENCRYPTION: 2iZj7YsSt7gqc+ftF49MCLqwI71F4r2w6+xp9+zwNjc=
SEMAPHORE_LDAP_ACTIVATED: 'no'
TZ: Asia/Shanghai
说明:SQLite 方言下 SEMAPHORE_DB_HOST 填数据库文件路径(无需端口与账号),放在挂载的配置目录里即自动持久化到宿主机。启动实测:
docker compose -f docker-compose-sqlite.yml up -d
docker compose -f docker-compose-sqlite.yml ps
Container semaphore-v2.19.17-web-sqlite Creating
Container semaphore-v2.19.17-web-sqlite Created
Container semaphore-v2.19.17-web-sqlite Starting
Container semaphore-v2.19.17-web-sqlite Started
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
semaphore-v2.19.17-web-sqlite semaphoreui/semaphore:v2.19.17 "/sbin/tini -- /usr/…" semaphore 35 seconds ago Up 35 seconds 0.0.0.0:8151->3000/tcp, [::]:8151->3000/tcp
启动日志确认方言(SQLite @... 行),登录验证 204:
No additional python dependencies to install
Starting semaphore server
SQLite @/var/lib/semaphore/database.sqlite /etc/semaphore/semaphore.sqlite
Tmp Path (projects home) /tmp/semaphore
Semaphore v2.19.17-f8cefe6-1792391878
Interface
Port :3000
Server is running
sqlite_login=204
配置目录落地文件实测(semaphore.sqlite 及 WAL 伴生文件):
-rw-r--r-- 1 1001 root 333 10月 24 10:09 config.json
-rw-r--r-- 1 1001 root 606208 10月 24 10:09 semaphore.sqlite
-rw-r--r-- 1 1001 root 32768 10月 24 10:09 semaphore.sqlite-shm
-rw-r--r-- 1 1001 root 0 10月 24 10:09 semaphore.sqlite-wal
9.2 PostgreSQL 方言
把 MySQL 服务替换为 PostgreSQL 即可,docker-compose-postgres.yml:
name: semaphore-v2.19.17-stack
services:
postgres:
restart: unless-stopped
image: postgres:14
hostname: postgres
volumes:
- ./data/postgres:/var/lib/postgresql/data
environment:
POSTGRES_USER: semaphore
POSTGRES_PASSWORD: q1w2e3r4
POSTGRES_DB: semaphore
healthcheck:
test: ["CMD-SHELL", "pg_isready -U semaphore -d semaphore"]
interval: 5s
timeout: 5s
retries: 20
semaphore:
restart: unless-stopped
ports:
- "8151:3000"
image: semaphoreui/semaphore:v2.19.17
container_name: semaphore-v2.19.17-web-pg
volumes:
- ./data/semaphore-config-pg:/etc/semaphore
environment:
SEMAPHORE_DB_USER: semaphore
SEMAPHORE_DB_PASS: q1w2e3r4
SEMAPHORE_DB_HOST: postgres
SEMAPHORE_DB_PORT: 5432
SEMAPHORE_DB_DIALECT: postgres
SEMAPHORE_DB: semaphore
SEMAPHORE_PLAYBOOK_PATH: /tmp/semaphore/
SEMAPHORE_ADMIN_PASSWORD: q1w2e3r4
SEMAPHORE_ADMIN_NAME: admin
SEMAPHORE_ADMIN_EMAIL: admin@localhost
SEMAPHORE_ADMIN: admin
SEMAPHORE_ACCESS_KEY_ENCRYPTION: 2iZj7YsSt7gqc+ftF49MCLqwI71F4r2w6+xp9+zwNjc=
SEMAPHORE_LDAP_ACTIVATED: 'no'
TZ: Asia/Shanghai
depends_on:
postgres:
condition: service_healthy
与 MySQL 版的差异仅三处:数据库服务换成 postgres:14(pg_isready 探活)、Semaphore 侧 SEMAPHORE_DB_DIALECT: postgres + SEMAPHORE_DB_PORT: 5432 + SEMAPHORE_DB_HOST: postgres。启动实测:
docker compose -f docker-compose-postgres.yml up -d
docker compose -f docker-compose-postgres.yml ps
Container semaphore-v21917-stack-postgres-1 Creating
Container semaphore-v21917-stack-postgres-1 Created
Container semaphore-v2.19.17-web-pg Creating
Container semaphore-v2.19.17-web-pg Created
Container semaphore-v21917-stack-postgres-1 Starting
Container semaphore-v21917-stack-postgres-1 Started
Container semaphore-v21917-stack-postgres-1 Waiting
Container semaphore-v21917-stack-postgres-1 Healthy
Container semaphore-v2.19.17-web-pg Starting
Container semaphore-v2.19.17-web-pg Started
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
semaphore-v2.19.17-web-pg semaphoreui/semaphore:v2.19.17 "/sbin/tini -- /usr/…" semaphore 41 seconds ago Up 35 seconds 0.0.0.0:8151->3000/tcp, [::]:8151->3000/tcp
semaphore-v21917-stack-postgres-1 postgres:14 "docker-entrypoint.s…" postgres 41 seconds ago Up 40 seconds (healthy) 5432/tcp
启动日志确认 Postgres semaphore@postgres:5432 semaphore + Server is running:
No additional python dependencies to install
Starting semaphore server
Postgres semaphore@postgres:5432 semaphore
Tmp Path (projects home) /tmp/semaphore
Semaphore v2.19.17-f8cefe6-1792391878
Interface
Port :3000
Server is running
⚠️ 方言切换注意:三种方言的数据库结构相同,但数据迁移程序不做自动转换。已在 MySQL 上积累数据后想换 PostgreSQL,需要自行导出导入(项目级备份可用 CLI 的
semaphore projects export,见命令详解篇),不要指望改个环境变量就完成切换。
十、等价 docker run 方式
不用 Compose 的场景(脚本化单机部署、临时验证),可用 docker run 直接起。最小可用形态(SQLite、数据全落本地目录):
docker network create semaphore-net
docker run -d --name semaphore-web \
--network semaphore-net \
-p 8151:3000 \
-v /opt/semaphore/data/semaphore-config-run:/etc/semaphore \
-e SEMAPHORE_DB_DIALECT=sqlite \
-e SEMAPHORE_DB_HOST=/etc/semaphore/semaphore.sqlite \
-e SEMAPHORE_PLAYBOOK_PATH=/tmp/semaphore/ \
-e SEMAPHORE_ADMIN_PASSWORD=q1w2e3r4 \
-e SEMAPHORE_ADMIN_NAME=admin \
-e SEMAPHORE_ADMIN_EMAIL=admin@localhost \
-e SEMAPHORE_ADMIN=admin \
-e SEMAPHORE_ACCESS_KEY_ENCRYPTION=2iZj7YsSt7gqc+ftF49MCLqwI71F4r2w6+xp9+zwNjc= \
-e SEMAPHORE_LDAP_ACTIVATED=no \
-e TZ=Asia/Shanghai \
--restart unless-stopped \
semaphoreui/semaphore:v2.19.17
验证:
docker logs --tail 5 semaphore-web
Starting semaphore server
SQLite @/var/lib/semaphore/database.sqlite /etc/semaphore/semaphore.sqlite
Tmp Path (projects home) /tmp/semaphore
Semaphore v2.19.17-f8cefe6-1792391878
Port :3000
MySQL 形态则需要先起数据库、再起 Semaphore 并用 --network 让两者同网互通,SEMAPHORE_DB_HOST 指向数据库容器名——等价关系与 Compose 版一一对应,不再赘述。生产环境仍建议用 Compose(声明式、可版本化、depends_on 健康检查语义完整)。
十一、常用运维命令
本节汇总日常运维高频命令,全部在本文部署形态下实测。
① 启停与状态:
docker compose ps # 服务状态与健康检查
docker compose stop semaphore # 只停 Semaphore(保留 MySQL)
docker compose start semaphore # 恢复
docker compose restart semaphore # 改 config.json 后重启生效
docker compose down # 停止并删除服务与网络(数据目录保留)
docker compose up -d # 重建启动
stop/start 实测回显:
Container semaphore-v2.19.17-web Stopping
Container semaphore-v2.19.17-web Stopped
Container semaphore-v2.19.17-web Starting
Container semaphore-v2.19.17-web Started
② 日志:
docker compose logs -f semaphore # 跟踪实时日志
docker compose logs --tail 20 semaphore # 最近 20 行
正常稳定态日志尾部(实测):
semaphore-v2.19.17-web | Starting semaphore server
semaphore-v2.19.17-web | MySQL semaphore@mysql:3306 semaphore
semaphore-v2.19.17-web | Tmp Path (projects home) /tmp/semaphore
semaphore-v2.19.17-web | Semaphore v2.19.17-f8cefe6-1792391878
semaphore-v2.19.17-web | Interface
semaphore-v2.19.17-web | Port :3000
semaphore-v2.19.17-web | Server is running
③ 版本确认(进入服务内部执行 CLI):
docker compose exec semaphore semaphore version
v2.19.17-f8cefe6-1792391878
④ 数据库备份与恢复(MySQL 主线):
# 备份(业务账号即可,无需 root)
docker compose exec mysql sh -c \
'mysqldump -usemaphore -pq1w2e3r4 semaphore' > /opt/semaphore/backup/semaphore-$(date +%F).sql
实测备份文件头部(确认导出有效):
-- MySQL dump 10.13 Distrib 8.0.46, for Linux (x86_64)
--
-- Host: localhost Database: semaphore
-- ------------------------------------------------------
-- Server version 8.0.46
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
恢复用 docker compose exec -T mysql sh -c 'mysql -usemaphore -pq1w2e3r4 semaphore' < 备份文件。务必连同 config.json(三个密钥)一起备份,原因见第五节。
⑤ 升级:改 docker-compose.yml 中的镜像 tag 到目标版本 → docker compose up -d → 观察日志中的自动迁移输出(与首次启动同格式,Migrations Finished 即成功)。升级前先完成第④步备份;跨大版本升级前在测试环境演练回滚路径。
十二、常见问题(FAQ)
以下问题均为本次部署实际踩到并解决的案例,非理论枚举。
Q1:up -d 报 address already in use,服务起不来。
实测报错(docker compose up -d 输出):
Container semaphore-v2.19.17-web Recreated
Container semaphore-v21917-stack-mysql-1 Waiting
Container semaphore-v21917-stack-mysql-1 Healthy
Container semaphore-v2.19.17-web Starting
Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint semaphore-v2.19.17-web (96a3d0acb6ce8ff3bb4fef4c8929a2db13190f8311ffaad644225a7ca140997d): failed to bind host port 0.0.0.0:8133/tcp: address already in use
定位:宿主机 8133 已被其他进程监听。用 ss -ltn | grep 8133 实测确认:
LISTEN 0 4096 *:8133 *:* users:(("java",pid=3749258,fd=226))
解决:把 docker-compose.yml 里的端口映射改到空闲端口(本文用 8151),重新 docker compose up -d,服务正常 Started。选端口前先 ss -ltn 核对是唯一可靠做法——写文档时的"常用端口"清单不可信,以实时监听表为准。
Q2:启动即崩溃重启,日志 panic: open /etc/semaphore/config.json: permission denied。
实测日志(docker compose logs semaphore):
Running: mkdir -p /etc/semaphore..
panic: open /etc/semaphore/config.json: permission denied
goroutine 1 [running]:
github.com/semaphoreui/semaphore/cli/setup.SaveConfig({0x48d2a20, 0xe9f7d5af508}, {0x2e26755?, 0xb?}, {0x0?, 0xe9f7d3dd7c0?})
/go/src/semaphore/cli/setup/setup.go:247 +0x3ea
定位:镜像内 Semaphore 进程以非 root 用户(UID 1001)运行,而挂载的宿主机配置目录属 root 且不可写,首次初始化写 config.json 失败直接 panic。
解决:初始化前把宿主机配置目录属主改为 1001(或放开写权限):
chown -R 1001:0 /opt/semaphore/data/semaphore-config
docker compose up -d
重建后日志出现 Configuration written to /etc/semaphore/config.json..,初始化正常完成。判断依据:挂载目录里生成的文件实测属主为 1001 root(见 9.1 节文件列表)。
Q3:二次启动卡在 Waiting 后一直 Restarting,日志报数据库连接失败。
定位:Semaphore 启动第一步是连数据库执行迁移,MySQL 未就绪时连接被拒。若 depends_on 用了短格式(只保证顺序),MySQL 还在初始化(首启要生成随机 root 密码、建库建用户,需数十秒)时 Semaphore 就会抢跑失败。
解决:改用 condition: service_healthy(见 4.2 节配置),让 Compose 等 healthcheck 通过再启动 Semaphore。实测时序:mysql-1 Started → mysql-1 Waiting → mysql-1 Healthy → web Starting → web Started。
Q4:SEMAPHORE_ADMIN_PASSWORD 改了但登录不上新密码。
原因:该变量只在首次初始化(生成 config.json 时)消费一次,之后修改无效。
解决:改密码走 Web UI 用户设置页,或用 CLI:
docker compose exec semaphore semaphore users change-by-login --login admin --password '新密码' --config /etc/semaphore/config.json
实测回显:
User admin <admin@localhost> changed!
(CLI 子命令全集见 《Semaphore v2.19.17 常用 CLI 命令与工具详解》。)
Q5:首次启动日志有 level=warning msg="no rows in result set",是不是错误?
不是。首次初始化在迁移完成后查询任务/用户表做统计,空库查询无结果属正常,该 Warn 不影响初始化与后续运行(实测该行出现在 Migrations Finished 之后、You are all setup admin! 之前,部署最终验证全部通过)。
十三、参考链接
- 官方 Docker 安装文档:https://semaphoreui.com/docs/admin-guide/installation/docker(Compose 模板、secrets、requirements.txt 机制的官方出处)
- 官方配置文件文档:https://semaphoreui.com/docs/admin-guide/configuration/config-file
- 官方环境变量文档:https://semaphoreui.com/docs/admin-guide/configuration/env-vars
- 配置项全量 Reference:https://semaphoreui.com/docs/reference/configuration
- GitHub 仓库:https://github.com/semaphoreui/semaphore
- Docker Hub 镜像页:https://hub.docker.com/r/semaphoreui/semaphore
- 本专栏相关文档:《Semaphore v2.19.17 介绍与选型指南》(认知与选型)、《Semaphore v2.19.17 配置参数详解》(130+ 配置项全量详解)、《Semaphore v2.19.17 常用 CLI 命令与工具详解》(CLI 子命令实测)