Crafty Controller Docker Compose 部署指南
Ubuntu 24.04.5 用 Docker Compose 部署 Crafty Controller 4.11.0 全纪实:GitLab registry 镜像路线、5 卷持久化与 crafty:root 权限自愈、首启初始化序列、登录闭环与 Web 验证、等价 docker run 实测,含权限崩溃循环、logging.json 缺失、BigBucket 超时等 7 条排障实录,全部回显 2026-11-02 实测。
Crafty Controller Docker Compose 部署指南
目标:在 Ubuntu 24.04.5 上用 Docker Compose 以官方 GitLab registry 镜像部署 Crafty Controller 4.11.0,完成首启验证、管理员登录、数据持久化与运维闭环。本文全部命令与回显为 2026-11-02 实测,含权限修复循环、BigBucket 超时、logging.json 缺失等 7 条排障实录。
一、环境信息
| 项目 | 值 |
|---|---|
| 主机 OS | Ubuntu 24.04.5 LTS(内核 x86_64) |
| Docker | 29.9.2, build a82e528 |
| Docker Compose | v5.6.1 |
| 组件版本 | Crafty Controller 4.11.0(GitLab tag,2026-09-16) |
| 镜像 | registry.gitlab.com/crafty-controller/crafty-4:4.11.0(镜像 ID 7b6e87514259,磁盘占用 2.64GB / 内容 670MB) |
| 运行期 | 容器内 Ubuntu 24.04.5 + Python 3.12.3,非 root 用户 crafty |
| Web 端口 | 8135(宿主)→ 8443(面板 HTTPS) |
| 自定义网络 | crafty-net,bridge,子网 10.89.62.0/24,面板 IP 10.89.62.2 |
| compose 项目名 | crafty-4.11.0-stack(显式 name:,防多代理互删) |
| 实测日期 | 2026-11-02 |
二、镜像选型说明
官方提供两个镜像地址(README 实查,2026-11-02):
| 镜像 | 说明 |
|---|---|
registry.gitlab.com/crafty-controller/crafty-4 | 官方主推,GitLab Container Registry,tag 与 GitLab tag 同步(4.11.0/latest/dev) |
arcadiatechnology/crafty-4 | Docker Hub 镜像,同一构建产物 |
要点:
- 4.x 起提供预构建镜像,不需要像 3.x 那样 clone 仓库自建;
- 镜像开箱支持
arm64与amd64;SteamCMD 依赖(Bedrock 等)仅 amd64 提供,arm64 启动时 launcher 打印⚠️ SteamCMD is only available on amd64/x86_64 images.并禁用相关功能; - 容器内以
crafty:root组权限结构运行(仿 OpenShift/K8s 实践),bind mount 无需匹配宿主 UID/GID; - 生产部署建议钉住版本号 tag(如
4.11.0),避免latest漂移。
⚠️ 官方警告(README 原文):Docker 路线不要在 WSL/WSL2、Windows 11 / Docker Desktop 上跑 Minecraft 服务器——停服/重启时世界区块有约 90% 概率不可逆损坏。Windows 用户官方建议改用便携安装。本指南的容器路线仅面向 Linux 宿主。
三、前置条件
# Docker 与 Compose 版本确认(实测)
$ docker --version
Docker version 29.9.2, build a82e528
$ docker compose version
Docker Compose version v5.6.1
# 宿主端口占用检查:8135 未被占用(实测 ss -ltn 摘录)
$ ss -ltn | grep -E ':(8135|8123|19132|2550[0-9])' || echo 'ports free'
ports free
准备部署目录与 5 个持久化子目录(对应官方 compose 的 5 个卷):
$ mkdir -p crafty-controller/docker/{backups,logs,servers,config,import}
四、部署步骤
4.1 编写 docker-compose.yml
完整实测版本(钉版本 tag、显式项目名、自定义网络槽位):
name: crafty-4.11.0-stack
services:
crafty:
container_name: crafty-4.11.0-panel
image: registry.gitlab.com/crafty-controller/crafty-4:4.11.0
restart: always
environment:
- TZ=Asia/Shanghai
ports:
- "8135:8443" # HTTPS
- "8123:8123" # DYNMAP
- "19132:19132/udp" # BEDROCK
- "25500-25510:25500-25510" # MC SERV PORT RANGE
volumes:
- ./docker/backups:/crafty/backups
- ./docker/logs:/crafty/logs
- ./docker/servers:/crafty/servers
- ./docker/config:/crafty/app/config
- ./docker/import:/crafty/import
networks:
- crafty-net
networks:
crafty-net:
driver: bridge
ipam:
config:
- subnet: 10.89.62.0/24
逐项说明:
name:显式项目名——compose 默认用目录名做项目名,多栈并存时down会误删他人栈,必须显式命名;TZ是官方 compose 示例中唯一的环境变量,面板日志与计划任务时区依赖它;- 端口映射按用途拆:
8135:8443面板 HTTPS(宿主端口按空闲情况取),8123Dynmap 直通,19132/udpBedrock,25500-25510Java 服端口段(官方示例为 25500-25600 全段,按实际服数收窄即可); - 5 个卷全部 bind mount,
./docker/config:/crafty/app/config是唯一必须备份的目录(SQLite 库、config.json、凭据、证书全在里面); import目录是"投放待修复权限的旧服"通道,同时它的非空状态会触发 launcher 的权限自愈流程(见 4.4 与 FAQ 10.1)。
4.2 拉取镜像
$ docker pull registry.gitlab.com/crafty-controller/crafty-4:4.11.0
4.11.0: Pulling from crafty-controller/crafty-4
Digest: sha256:7b6e87514259549647e15ceeaf73d238792e6d77e2c79fc744d5badc8d6e997c
Status: Image is up to date for registry.gitlab.com/crafty-controller/crafty-4:4.11.0
registry.gitlab.com/crafty-controller/crafty-4:4.11.0
4.3 起栈
$ docker compose -f docker-compose.yml up -d
Container crafty-4.11.0-panel Running
(首次创建时输出为 Creating/Started;上面回显为栈已在跑时的幂等输出。)
4.4 首启日志与初始化序列
docker compose logs 实测首启主线(全新空 config 卷,节选):
Wrapper | 🚂✅ SteamCMD dependencies are available in this image!
Wrapper | 🏗️ Config not found, pulling defaults...
Wrapper | 📋 Looking for problem bind mount permissions globally...
Wrapper | 📋 (1/3) Ensuring root group ownership...
Wrapper | 📋 (2/3) Ensuring group read-write is present on files...
Wrapper | 📋 (3/3) Ensuring sticky bit is present on directories...
Wrapper | ✅ Initialization complete!
Wrapper | 🚀 Launching crafty with [-d -i]
Logging set to: 0
///////////////////////////////////////////////////////////////////////////
# Welcome to Crafty Controller - v.4.11.0 #
///////////////////////////////////////////////////////////////////////////
# Server Manager / Web Portal for your Minecraft server #
# Homepage: www.craftycontrol.com #
///////////////////////////////////////////////////////////////////////////
要点:空 config 卷时 launcher 自动从镜像内 app/config_original/ 复制出厂配置(config.json、logging.json、credits.json、motd_format.json、version.json、default.json),随后以 python3 main.py -d -i(daemon 模式 + 忽略 session.lock)拉起。
启动主线继续(实测节选):peewee 迁移回放(Starting migrations → Migrate "20210813111015_init" … 共 64 条至 20260609_custom_logos)→ 检测到全新安装(Fresh Install Detected - Creating Default Settings)→ 生成自签证书(Generating a self signed SSL → Self-signed certificate and key generation completed.)→ Web 服务监听:
INFO:app.classes.web.tornado_handler:Starting Web Server on ports https:8443
INFO:app.classes.web.tornado_handler:https://10.89.62.2:8443 is up and ready for connections.
[+] Crafty: 11/02/26 21:13:09 - INFO: Server Init Complete: Listening For Connections!
INFO:apscheduler:Stats collection frequency set to 30 seconds
面板还会打印一条新鲜安装提醒:We have detected a fresh install. Please be sure to forward Crafty's port, 8443, through your router/firewall if you would like to be able to access Crafty remotely.——公网暴露时按需在路由器/防火墙放行 8443(本例经 8135 映射)。
4.5 Web 验证
$ curl -sk -o /dev/null -w '%{http_code} -> %{redirect_url}\n' https://localhost:8135/
302 -> https://localhost:8135/panel/dashboard
$ curl -sk https://localhost:8135/login | grep -oE '<title>[^<]*</title>'
<title>Crafty Controller</title>
$ curl -sk -o /dev/null -w '%{http_code}\n' https://localhost:8135/api/v2/servers
403
三条回显含义:根路径 302 跳仪表盘(未登录时实际先到 /login);登录页标题正常;/api/v2/* 未带 API Key 一律 403——API 认证是独立体系(见本专栏 CLI 篇的 API Key 管理)。
4.6 管理员登录与凭据机制
首启随机口令写在 docker/config/default-creds.txt(实测文件内容形态):
{
"username": "admin",
"password": "bttanty3SSaV*RwELqI!yor2NCcsuAw$%WT$hSa7a$OsIdKs!Jd8yvtHEkVvmfz2",
"info": "This is NOT where you change your password. This file is only a means to give you a default password."
}
⚠️ 注意:
default-creds.txt只在首次登录前有效,改密后此文件不再代表当前口令;文件自身也写明"这里不是改密码的地方"。
另一种确定性做法:在 docker/config/ 放 default.json 预置口令(出厂镜像自带 {"username":"admin","password":"crafty"}),首次登录用该口令。本文环境实测用演示口令 admin / q1w2e3r4 完成登录,数据库侧证据(users 表实测回显见 CLI 篇):
USERS: [(1, 'admin', 1, 1, 'default@example.com', '2026-11-02 21:13:08.274723')]
last_login 已写入即证明登录闭环成功。登录页为 JS 前端发起 JSON fetch,传统表单 POST 会原样返回登录页 HTML(实测脚本回显):
GET /login -> 200 xsrf found: True
POST /login -> 200 final url: https://127.0.0.1:8443/login
GET / -> 200 title: Crafty Controller
浏览器访问 https://<宿主IP>:8135 时自签证书会告警,信任后继续即可(生产建议按配置篇换发正式证书或前置反代)。
五、数据持久化说明
5 个卷的分工与实测状态:
| 宿主目录 | 容器路径 | 内容 | 备注 |
|---|---|---|---|
./docker/config | /crafty/app/config | config.json、db/crafty.sqlite、default-creds.txt、default.json、session.lock、version.json、bigbucket.json、web/certs/ | 备份只备它 |
./docker/logs | /crafty/logs | commander.log、audit.log、auth_tracker.log | daemon 模式下 stdout 为主日志 |
./docker/servers | /crafty/servers | 各 MC 服务器实例目录 | 建服时自动生成 |
./docker/backups | /crafty/backups | 备份产物(面板默认监控挂载点) | |
./docker/import | /crafty/import | 旧服投放/权限修复触发区 | 保持空可跳过开机修复 |
config 目录实测清单(面板运行态):
-rw-r--r-- 1 crafty root 118355 Nov 2 22:05 bigbucket.json
-rw-rw-r-- 1 crafty root 2030 Nov 2 22:05 config.json
drwxrwsr-x 2 crafty root 4096 Nov 2 22:05 db
-rw-rw---- 1 crafty root 228 Nov 2 21:13 default-creds.txt
-rw-rw-r-- 1 root root 56 Nov 2 22:07 default.json
-rw-r--r-- 1 crafty root 56 Nov 2 22:05 session.lock
-rw-rw-r-- 1 root root 44 Nov 2 22:05 version.json
drwxrwsr-x 3 crafty root 4096 Nov 2 21:13 web
权限结构即 crafty:root + 目录 setgid(drwxrwsr-x),与宿主用户 UID 无关,bind mount 直接可用。
六、环境变量表
Crafty 容器不靠环境变量做配置,官方 compose 仅一个:
| 变量 | 实测值 | 说明 |
|---|---|---|
TZ | Asia/Shanghai | 容器时区,影响日志时间戳与计划任务;官方示例为 Etc/UTC |
面板自身全部配置走 config/config.json(Web 设置页或直接改文件,详解见配置篇);启动参数走 launcher 传参(默认 -d -i,见 CLI 篇)。
七、配置文件挂载与关键文件
- 挂载点:
./docker/config:/crafty/app/config。整目录覆盖镜像内默认配置;目录为空时 launcher 从/crafty/app/config_original/复制出厂件。 version.json(实测内容)每次启动由 launcher 从镜像同步,保证与运行版本一致:
{
"major": 4,
"minor": 11,
"sub": 0
}
session.lock(实测内容)记录当前进程 pid 与启动时间,-i参数即用于忽略它:
{
"pid": 23,
"started": "02-11-2026, 22:05:38"
}
web/certs/commander.cert.pem/commander.key.pem:首启自动生成的自签证书(实测ls:2171 / 3243 字节),生产替换方式见配置篇。
八、等价 docker run
与 4.1 compose 等价的单命令路线(实测验证过:独立容器 crafty-4.11.0-runtest,宿主端口 8136,验证后已删除):
$ docker run --rm -d --name crafty-4.11.0-runtest \
-e TZ=Asia/Shanghai \
-p 8136:8443 \
-v "$(pwd)/dkrun/backups:/crafty/backups" \
-v "$(pwd)/dkrun/logs:/crafty/logs" \
-v "$(pwd)/dkrun/servers:/crafty/servers" \
-v "$(pwd)/dkrun/config:/crafty/app/config" \
-v "$(pwd)/dkrun/import:/crafty/import" \
registry.gitlab.com/crafty-controller/crafty-4:4.11.0
4d36460dd88bb98004fc7536c74f7825bad779b11244ef8c7aca0ef04ee0be80
$ docker logs crafty-4.11.0-runtest 2>&1 | tail -4
[+] Crafty: 11/02/26 22:35:10 - INFO: Self-signed certificate and key generation completed.
[+] Crafty: 11/02/26 22:35:10 - INFO: https://172.17.0.2:8443 is up and ready for connections.
[+] Crafty: 11/02/26 22:35:10 - INFO: Server Init Complete: Listening For Connections!
$ curl -sk -o /dev/null -w '%{http_code} -> %{redirect_url}\n' https://localhost:8136/
302 -> https://localhost:8136/panel/dashboard
全新空 config 卷下该路线 25 秒内完成"复制出厂配置 → 权限初始化 → 迁移 → 自签证书 → 监听",并生成随机 default-creds.txt。官方 README 亦提供 docker run 示例(含 -P 5520-5550:5520-5550/udp 写法,其中 -P 为大写笔误,实际应为 -p,照抄会报错)。
九、常用运维命令
# 状态
$ docker compose -f docker-compose.yml ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
crafty-4.11.0-panel registry.gitlab.com/crafty-controller/crafty-4:4.11.0 "/crafty/docker_laun…" crafty About an hour ago Up 21 minutes 5520-5550/tcp, 8000/tcp, 0.0.0.0:8123->8123/tcp, [::]:8123->8123/tcp, 19132/tcp, 25511-25600/tcp, 0.0.0.0:25500-25510->25500-25510/tcp, [::]:25500-25510->25500-25510/tcp, 0.0.0.0:19132->19132/udp, [::]:19132->19132/udp, 0.0.0.0:8135->8443/tcp, [::]:8135->8443/tcp
# 网络核对(compose 项目名会规范化掉点号:crafty-4.11.0-stack → crafty-4110-stack)
$ docker network inspect crafty-4110-stack_crafty-net --format '{{.Name}} | {{.Driver}} | {{range .IPAM.Config}}{{.Subnet}} {{end}}| {{range .Containers}}{{.Name}} {{.IPv4Address}} {{end}}'
crafty-4110-stack_crafty-net | bridge | 10.89.62.0/24 | crafty-4.11.0-panel 10.89.62.2/24
# 日志跟踪
docker compose -f docker-compose.yml logs -f
# 进入面板 CLI(详见 CLI 篇)
docker exec -u crafty -w /crafty crafty-4.11.0-panel bash -c "source ./.venv/bin/activate && python3 main.py --help"
# 停栈(保留数据)
docker compose -f docker-compose.yml down
# 升级:改 compose 中 tag → 拉新镜像 → 重建
docker pull registry.gitlab.com/crafty-controller/crafty-4:<新版本>
docker compose -f docker-compose.yml up -d
# 迁移自动回放,无需手工升级数据库
# 备份(停栈后打包 config 卷最稳)
docker compose -f docker-compose.yml stop
tar czf crafty-config-$(date +%F).tar.gz docker/config
docker compose -f docker-compose.yml start
十、常见问题
10.1 宿主重启后面板反复崩溃:[Errno 13] Permission denied
实测故障现场(宿主重启后 docker compose logs 节选,restart 策略导致循环拉起):
Wrapper | 🚀 Launching crafty with [-d -i]
Check generated exception due to permssion error: [Errno 13] Permission denied: '/crafty/app/config/db'
[+] Crafty: 11/02/26 21:12:11 - CRITICAL: Unable to open log file! [Errno 13] Permission denied: './logs/commander.log'
该循环重复了 9 次。定位:bind mount 目录组权限丢失(crafty 用户写不进 config/db 与 logs)。解决:往 docker/import/ 放任意一个文件触发 launcher 权限自愈,下一次重启即修复:
Wrapper | 📋 Files present in import directory, checking/fixing permissions...
Wrapper | 📋 (1/3) Ensuring root group ownership...
Wrapper | 📋 (2/3) Ensuring group read-write is present on files...
Wrapper | 📋 (3/3) Ensuring sticky bit is present on directories...
Wrapper | ✅ Permissions Fixed! (This will happen every boot until /import is empty!)
Wrapper | 🚀 Launching crafty with [-d -i]
INFO:__main__:***** Crafty Controller Started *****
修复完成后清空 import 目录,否则每次开机都会全量扫修权限拖慢启动。手动等价操作:对 docker/{config,logs} 执行 chgrp -R root、chmod -R g+rw、目录 chmod g+s(即 launcher repair_permissions 函数三步)。
10.2 Unable to read logging config from /crafty/app/config/logging.json
启动日志出现 [+] Crafty: ... - CRITICAL: Unable to read logging config from /crafty/app/config/logging.json。非致命:面板照常启动监听。成因是 config 卷里缺 logging.json(如手工建目录时只放了 default.json,未走"空目录复制出厂件"路径)。补齐办法:从同版本镜像取一份——
docker run --rm registry.gitlab.com/crafty-controller/crafty-4:4.11.0 cat /crafty/app/config_original/logging.json > docker/config/logging.json
(logging.json 是 Python dictConfig 格式,定义 commander/tornado_access/schedule/auth/audit 等日志器与格式,audit 用 JSON formatter。)
10.3 BigBucket 拉取超时:Read timed out. (read timeout=2)
实测首启日志:
ERROR:app.classes.big_bucket.bigbucket:Unable to connect to big bucket due to error: HTTPSConnectionPool(host='jars.arcadiatech.org', port=443): Read timed out. (read timeout=2)
ERROR:app.classes.big_bucket.bigbucket:big bucket API is not available.
定位:面板启动时向 jars.arcadiatech.org 拉 jar 清单缓存,首次请求超时(客户端超时仅 2 秒)。影响:仅服务器"下载核心"列表暂缺,不影响面板与已有服。解决:无需干预,面板会周期性重试,实测稍后成功:
INFO:app.classes.big_bucket.bigbucket:Big bucket is alive and responding as expected
DEBUG:urllib3.connectionpool:https://jars.arcadiatech.org:443 "GET /manifest.json HTTP/1.1" 200 57109
INFO:app.classes.big_bucket.bigbucket:Manual refresh completed.
成功后 config/bigbucket.json(实测 118355 字节)落盘,含 vanilla 各版本 piston-data.mojang.com URL 与 sha256。受限网络环境给容器配 HTTP 代理或预置该缓存文件即可。
10.4 ntp.org ping failed. Falling back to google
启动日志两条 ERROR:app.classes.helpers.helpers:ntp.org ping failed. Falling back to google——面板探测时间同步源的噪音日志,非致命,忽略即可;内网无外网时也会如此。
10.5 浏览器自签证书告警
首启自动生成 commander.cert.pem/commander.key.pem 自签对,浏览器提示不安全属预期。生产用正式证书:把证书文件放到 config/web/certs/ 并在面板设置中指定路径(见配置篇)。
10.6 /api/v2/servers 直接 403
API 需 Key 认证(api_keys 表),未认证一律 403(实测回显见 4.5)。在面板 设置→API Keys 生成后按 Bearer 方式调用,Key 管理实操见 CLI 篇。
10.7 忘记管理员口令
default-creds.txt 与 default.json 都只在首次登录前生效,改密后无法回读。SQLite 单文件库可直接操作(停栈后):删掉 users 表中 admin 行并清空 config/default.json 预设口令,重启后会按全新用户流程重建——或按官方文档用 root 账户恢复。生产更推荐提前配置 Passkey/TOTP(4.11 已内置 passkey_data、totp_data 表)。