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 ControllerMinecraft面板DockerDocker ComposeDocker部署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 条排障实录。

一、环境信息

项目值
主机 OSUbuntu 24.04.5 LTS(内核 x86_64)
Docker29.9.2, build a82e528
Docker Composev5.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-4Docker 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(宿主端口按空闲情况取),8123 Dynmap 直通,19132/udp Bedrock,25500-25510 Java 服端口段(官方示例为 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/configconfig.json、db/crafty.sqlite、default-creds.txt、default.json、session.lock、version.json、bigbucket.json、web/certs/备份只备它
./docker/logs/crafty/logscommander.log、audit.log、auth_tracker.logdaemon 模式下 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 仅一个:

变量实测值说明
TZAsia/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 表)。

十一、参考链接

Crafty Controller Docker Compose 部署指南 - Quzuo