本指南以默认 docker-compose.yml 为主,它拉取固定版本的公开 GHCR
镜像。docker-compose.build.yml 和 docker-compose.build-cn.yml
分别用于官方上游及中国大陆下载端点的源码构建。三份配置的运行参数
等价。直接运行二进制时,仍应复用相同的安全、持久化和健康检查原则。
当前 Compose 服务具备:
- 宿主机只绑定
127.0.0.1:8080; ./music:/music:ro;- 命名卷
shufflemuse-data:/data; restart: unless-stopped;- 40 秒停止宽限期;
- 只读根文件系统;
- 64 MiB
/tmptmpfs; - 丢弃全部 Linux capabilities;
no-new-privileges;- PID 上限 256;
/api/readyhealthcheck。
镜像只保证以非 root shufflemuse 用户运行。只读根、cap drop、PID
限制和 healthcheck 都来自 Compose;改用裸 docker run 时不会自动继承。
Compose 创建的实际卷名通常带项目名前缀,例如 shufflemuse_shufflemuse-data。用 docker volume ls 或容器 mount 信息确认,不要仅凭 YAML 中的逻辑名猜测。
-
准备音乐目录:
mkdir -p music
-
根据访问范围编辑
docker-compose.yml:- 仅本机:保持 loopback,可保留空密码;
- 局域网:绑定
0.0.0.0,设置密码和允许 Host; - 公网:再增加 HTTPS 反向代理、Secure Cookie 和正确代理 IP 信任。
-
验证并启动:
docker compose config --quiet docker compose pull docker compose up -d docker compose ps docker compose logs -f shufflemuse
-
等待首次扫描:
curl -i http://127.0.0.1:8080/api/ready
首次扫描成功前容器 health 状态可能为 starting 或 unhealthy,数据 API 返回 503。大型曲库扫描只读取目录项,不运行 ffprobe,但文件系统速度、权限错误和损坏符号链接仍会影响结果。
需要从当前检出构建时,所有命令都使用
docker-compose.build.yml:
docker compose -f docker-compose.build.yml config --quiet
docker compose -f docker-compose.build.yml up -d --build
docker compose -f docker-compose.build.yml logs -f shufflemuse大陆网络使用独立文件,不修改默认国际构建:
docker compose -f docker-compose.build-cn.yml config --quiet
docker compose -f docker-compose.build-cn.yml up -d --build
docker compose -f docker-compose.build-cn.yml logs -f shufflemuse三份 Compose 的服务名、端口、volume、环境变量、healthcheck、只读根和 权限限制一致;两份源码构建配置的区别只在下载端点:
| build arg | 默认值 | 用途 |
|---|---|---|
DOCKERHUB_MIRROR |
m.daocloud.io/docker.io |
DaoCloud 前缀代理三个 Docker Hub 基础镜像 |
BUN_REGISTRY |
https://registry.npmmirror.com |
Bun/npm registry 与 lockfile tarball 前缀映射 |
GOPROXY |
https://goproxy.cn,direct |
Go modules;代理缺少模块时允许直接回源 |
GOSUMDB |
sum.golang.google.cn |
Go 官方提供给中国大陆访问的 checksum database alias,保留模块校验 |
ALPINE_MIRROR |
https://mirrors.aliyun.com/alpine |
Alpine apk repository |
工作树中的 web/bun.lock 使用官方 registry.npmjs.org。Dockerfile.cn 复制 lockfile 后,只在该镜像层内把精确官方前缀映射为 BUN_REGISTRY,再执行 frozen install;版本和 integrity 不变,工作树也不会被改写。
Dockerfile frontend 由文件首行 # syntax= 独立固定到 DaoCloud 地址和 digest,不能通过 DOCKERHUB_MIRROR build arg 覆盖。若改用组织自建的 Dockerfile frontend 镜像,必须直接修改该行并重新核对 digest。
三个基础镜像继续固定与默认 Dockerfile 相同的 tag 和 manifest digest。DaoCloud 当前返回的 manifest digest 已逐一核对相同,但它、npmmirror、Goproxy.cn 和阿里云仍是额外第三方可用性与信任边界。若组织有自建镜像,可编辑大陆 Compose 中的 build args;不要移除 digest、frozen lock、Go checksum 或 apk 签名校验。
部署后应始终使用同一份 Compose 文件执行 stop、backup、upgrade 和 logs。例如大陆版本的停止命令是:
docker compose -f docker-compose.build-cn.yml stop shufflemuse不要同时用多份 Compose 启动同一个项目;它们显式使用相同项目名、服务名 和命名卷。
有些环境变量不能孤立修改:
| 修改 | 还必须检查 |
|---|---|
MUSIC_PORT |
Compose 端口映射的容器 target、healthcheck URL |
MUSIC_DIR |
volume target、只读权限和符号链接最终目标 |
MUSIC_BOLTDB_PATH |
新路径必须位于可写 mount;只读根文件系统其他位置不可写 |
| 对外主机名/IP | MUSIC_ALLOWED_HOSTS、反向代理 Host 保留、浏览器 Origin |
| HTTP 改 HTTPS | MUSIC_COOKIE_SECURE=true、代理超时和流式转发 |
| 代理连接来源 | MUSIC_TRUSTED_PROXY_SUBNETS 和真实 IP header 模式 |
MUSIC_ALLOWED_HOSTS 只是 HTTP Host-header 校验,不是防火墙。真正限制默认网络暴露的是 127.0.0.1 端口绑定。
反向代理应:
- 保留浏览器访问使用的 Host/port;
- 覆盖并正确构造真实 IP header;
- 对长音频流使用足够长的 read timeout;
- 禁止或减少转码响应缓冲;
- 终止 HTTPS;
- 不把代理地址放入认证免登录白名单。
宿主机 nginx 代理到 Compose loopback 的示例:
server {
listen 443 ssl;
server_name music.example.com;
# ssl_certificate /path/fullchain.pem;
# ssl_certificate_key /path/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
proxy_send_timeout 60s;
proxy_buffering off;
}
}对应配置通常类似:
environment:
MUSIC_PASSWORD: "replace-with-a-long-random-password"
MUSIC_ALLOWED_HOSTS: "music.example.com"
MUSIC_TRUSTED_PROXY_SUBNETS: "ACTUAL_PROXY_PEER_CIDR"
MUSIC_REAL_IP_HEADER: "x-forwarded-for"
MUSIC_COOKIE_SECURE: "true"ACTUAL_PROXY_PEER_CIDR 必须替换为 ShuffleMuse 在 TCP 连接上实际看到的 nginx 来源地址或网段。即使 nginx 监听在宿主机,经过 Docker 端口映射后,容器看到的也未必是 127.0.0.1;代理在另一个容器中时通常是 Docker 网络地址。先从实际连接或日志确认,再配置最窄网段,不要直接照抄示例值。
Origin 校验比较浏览器 Origin authority 与转发后的 Host。代理若把 Host 改为内部 127.0.0.1:8080,浏览器写请求会得到 403 CSRF_BLOCKED。
GET /api/ready:
- 首次扫描成功前 503;
- 后续重扫期间 200;
- 已有快照的后续扫描失败后仍为 200;
- 关机开始后 503。
Compose 每 30 秒检查一次,timeout 10 秒,连续 3 次失败判定 unhealthy,首次启动有 30 秒 start period。
已认证 GET /api/status 提供文件数、generation、扫描状态、最后成功时间和错误。密码模式未登录时只返回认证字段,不能作为完整监控源。
curl -b shufflemuse.cookies http://127.0.0.1:8080/api/status首次扫描失败后周期定时器不会自动重试。修复目录、权限或符号链接后:
curl -b shufflemuse.cookies -X POST \
http://127.0.0.1:8080/api/rescan也可在 Browse 点击 Rescan。请求返回 202 后继续轮询状态。后续重扫失败会保留旧快照。默认 MUSIC_RESCAN_INTERVAL=0 不会自动再试,应再次手动 Rescan;配置正数周期时才会在下一周期重试。
generation 只反映音频路径集合,不反映文件内容、mtime、标签或 metadata 变化。不要把 generation 当作整个数据库版本。
docker compose ps -a
docker compose logs --tail=200 shufflemuse
docker compose logs -f shufflemuse
docker compose exec shufflemuse sh -c 'id; test -r /music; test -w /data'
docker compose exec shufflemuse ffmpeg -version
docker compose exec shufflemuse ffprobe -version服务日志包括:
- 配置和认证警告;
- 客户端 IP 模式及可信代理数;
- 媒体并发上限;
- 每个 API 的方法、路径、状态和耗时;
- 扫描发布/失败;
- FFmpeg 错误;
- 关机步骤。
日志不提供结构化 JSON、rotation 或 request ID。Compose 默认使用 Docker logging driver,保留策略应在 Docker daemon 或 Compose 扩展配置中管理。
应用需要备份的唯一内部持久化数据是 /data/tags.db。音乐目录、源代码和 Compose 配置应由各自的备份策略管理。
docker compose stop shufflemuse
docker compose run --rm --no-deps --entrypoint tar shufflemuse \
-C /data -czf - . > shufflemuse-data.tar.gz
docker compose start shufflemuse
tar -tzf shufflemuse-data.tar.gz为什么先停止:复制正在写入的 bbolt 文件不是应用级一致性快照。停止服务同时阻止标签写入和路径迁移。
归档不包含:
./music;- Session 和登录封禁;
- 内存 Index、generation 和 metadata cache;
- 浏览器队列;
- Compose 配置或密码。
Tags CSV 是人类可读导出,没有 import API,不能作为灾难恢复备份。
恢复命令会删除 /data 中现有内容。先验证文件存在且归档可读:
test -s shufflemuse-data.tar.gz
tar -tzf shufflemuse-data.tar.gz然后:
docker compose down
docker compose run --rm --no-deps --entrypoint sh \
-v "$PWD/shufflemuse-data.tar.gz:/backup.tar.gz:ro" shufflemuse \
-c 'find /data -mindepth 1 -delete && tar -xzf /backup.tar.gz -C /data'
docker compose up -d
docker compose ps
docker compose logs --tail=200 shufflemuse
curl -i http://127.0.0.1:8080/api/readydocker compose down 默认不删除命名卷;不要为普通恢复加入 -v。
恢复命令故意使用镜像默认的非 root shufflemuse 用户:Compose 已丢弃全部 capabilities,切到 UID 0 后再依赖 chown 并不可靠;以卷的正常 owner 写入时,解压文件会直接属于应用用户。若 /data 本身已经被外部操作改成不可写,应先单独诊断并修复卷权限,不要在恢复脚本里长期加入 root 权限。
如果旧部署使用 ./data:/data:
- 停止旧服务并单独备份
./data; - 确认当前 Compose 已改为
shufflemuse-data:/data; - 把旧目录复制到新命名卷:
docker compose down
docker compose run --rm --no-deps --entrypoint sh \
-v "$PWD/data:/legacy:ro" shufflemuse \
-c 'find /data -mindepth 1 -delete && cp -R /legacy/. /data/'
docker compose up -d若旧标签 key 保存绝对音乐路径,再临时设置 MUSIC_LEGACY_MUSIC_ROOT 为旧部署的精确绝对音乐根。迁移只在一次成功扫描发布前执行;确认标签恢复后清空该变量。
-
备份标签库和 Compose 配置。
-
查看 CHANGELOG 和配置差异,尤其是环境变量、volume 和数据格式。
-
将 Compose 的固定镜像标签或 digest 改为目标版本。
-
拉取并观察启动:
docker compose pull docker compose up -d docker compose logs -f shufflemuse
源码构建部署应在上述命令中持续加入
-f docker-compose.build.yml 或 -f docker-compose.build-cn.yml,并执行
up -d --build。Dockerfile 固定 Bun、Go、Alpine 的 tag 和 digest,也固定
Alpine FFmpeg 包版本;--pull 不会自动升级这些固定值。
项目没有数据库 schema 版本、迁移历史或自动回滚工具。回滚前:
- 保存当前
tags.db; - 恢复到明确的源码/镜像版本;
- 如果新版本曾改变标签数据语义,恢复升级前数据库备份;
- 启动后核对 Tags、Graveyard 和 CSV。
回滚版本可通过 shufflemuse --version、镜像 OCI labels 和 GHCR digest
确认。运维记录应保存不可变 digest,而不是只记录 latest。
默认 Dockerfile 使用 Docker Hub、官方 npm registry、proxy.golang.org 和 Alpine CDN。只需替换默认构建的 Go proxy 时可传:
docker compose -f docker-compose.build.yml build \
--build-arg GOPROXY=https://your-go-proxy.example,direct需要替换全部构建工具链时使用中国大陆网络替代构建,不要直接改主 lockfile 的 URL。两套构建的依赖更新和验证规则见开发指南。
- 非本机访问已设置长随机密码。
- 端口绑定与实际访问范围一致。
-
MUSIC_ALLOWED_HOSTS只含需要的主机名/IP。 - 公网由 HTTPS 反向代理保护。
- HTTPS 部署启用了 Secure Cookie。
- 可信代理仅包含实际直连代理来源。
- 代理不在认证白名单内。
- 代理保留外部 Host 并覆盖真实 IP header。
- 音乐目录中没有可下载的密钥、备份或配置。
-
/data位于持久化、可写 volume。 - 定期执行停止服务的一致性备份并验证归档。
- Docker 日志和磁盘有容量策略。
-
ffmpeg与ffprobe都可执行。
| 现象 | 可能原因 | 检查与处理 |
|---|---|---|
启动时报 invalid config |
值越界、CIDR 非法、legacy root 非绝对路径 | 查看完整日志和配置表 |
open tag store / permission denied |
/data 不可写或 owner 不正确 |
检查 mount、id、test -w /data |
| healthcheck 长期失败 | 首次扫描失败、端口联动错误、服务退出 | 看 /api/ready 和容器日志 |
503 LIBRARY_SCANNING |
尚无成功快照 | 等待;初始失败修复后手动 Rescan |
400 INVALID_HOST |
请求 Host 未允许 | 补 MUSIC_ALLOWED_HOSTS,不是关闭校验 |
403 CSRF_BLOCKED |
反代未保留 Host,或 Origin/Host 不一致 | 检查代理的 Host header |
| 登录后仍 401 | Cookie 未保存/回传,或服务已重启 | HTTPS 检查 Secure;纯 HTTP 不应设 Secure=true |
429 LOGIN_IP_BLOCKED |
该解析 IP 达到失败阈值 | 等待 Retry-After;不要持续重试 |
| 所有用户共享同一封禁 | 真实 IP header/可信代理配置错误 | 核对 ShuffleMuse 看到的 TCP peer 和链路 |
503 MEDIA_BUSY |
运行槽和等待队列已满或超时 | 降低并发请求,或按资源调优限制 |
| metadata 不可用 | ffprobe 缺失、媒体损坏、busy | 执行 ffprobe -version,看日志 |
| 封面错误 | 无封面、超 20 MiB/8192 单边/40 MP、FFmpeg 失败或 busy | 区分 COVER_NOT_FOUND、COVER_ERROR 与 MEDIA_BUSY;服务端不会保留转换图或降级发送超大原图 |
| 标签进入 Graveyard | 路径移动、根目录变化或旧路径未迁移 | 核对路径;恢复同路径或清理孤立标签 |
| 改端口后 unhealthy | 容器 target/healthcheck 未同步 | 同步三处端口配置 |
| 默认 Docker 前端安装失败 | 无法访问官方 npm registry | 检查网络,或使用独立大陆 Compose |
| 大陆 Docker 构建访问镜像失败 | 第三方服务不可达或 build arg 已过期 | 检查五个 build args;必要时切换到组织自建镜像并保留校验 |
以下命令会删除标签数据,日常停止/升级不要使用:
docker compose down -vGraveyard 的删除按钮只删指定路径的标签记录;down -v 会删除整个 Compose 命名卷,影响所有标签和收藏。