Meelo v3.13.1 Docker Compose 部署指南
基于官方 docker-compose 全栈实测部署 Meelo v3.13.1:九服务编排、.env/settings.json 配置、ffmpeg 自制音频入库、扫描/转码/播放链路验证与 Corepack 超时等排障实录。
Meelo v3.13.1 Docker Compose 部署指南
目标:在 Linux 服务器上用官方 docker-compose 一键部署 Meelo v3.13.1 全栈(9 个服务),完成注册账号、创建曲库、ffmpeg 自制音频入库、扫描匹配与在线播放的全链路验证,并沉淀本次实测遇到的全部排障实录。Meelo 官方只提供 Docker 部署形态(README 明确 "Meelo is shipped though Docker images"),本文是其从 0 到 1 的完整落地过程。
一、环境信息
- 操作系统:Ubuntu 24.04.5 LTS(内核 7.0.0-38-generic)
- Docker 版本:Docker version 29.9.2, build a82e528;Docker Compose version v5.6.1
- 硬件:6 核 CPU / 15 GiB 内存 / 443 GB 磁盘
- Meelo 版本:v3.13.1(部署后经
GET /api/settings回显"version":"v3.13.1"确认,该版本为 GitHub Releases 最新稳定版,实查日期 2026-10-29) - 服务清单:server / scanner / matcher / front / db / meilisearch / transcoder / mq / nginx,共 9 个服务
- 服务器内网地址(示例):192.168.146.128,对外端口 8140
- 部署目录:
/opt/meelo(下含 docker-compose.yml、nginx.conf.template、.env、settings.json、config/、data/)
二、镜像选型说明
Meelo 官方按服务拆分镜像,全部使用官方仓库 arthichaud 下的版本化标签,不要使用 latest 或 edge(env.example 官方注释:The 'edge' tag uses the code from the main branch, but it exposes you to unwarranted breaking changes)。本次部署镜像清单(实测拉取记录):
| 服务 | 镜像 | 版本 | 作用 |
|---|---|---|---|
| server | arthichaud/meelo-server | v3.13.1 | REST API 核心(NestJS) |
| scanner | arthichaud/meelo-scanner | v3.13.1 | 曲库扫描/文件解析(Go) |
| matcher | arthichaud/meelo-matcher | v3.13.1 | 外部元数据匹配(Go) |
| front | arthichaud/meelo-front | v3.13.1 | Angular SSR Web 界面 |
| db | postgres | alpine3.14 | 元数据存储 |
| meilisearch | getmeili/meilisearch | v1.5 | 全文搜索 |
| transcoder | ghcr.io/zoriya/kyoo_transcoder | 5.2.1 | 按需转码(HLS) |
| mq | rabbitmq | 4.3-alpine | 服务间事件消息队列 |
| nginx | nginx | 1.31.3-alpine | 统一入口/反向代理 |
镜像选型解读:① 前四个 Meelo 镜像必须同版本号(server/scanner/matcher/front 之间有协议与数据库结构耦合,混版本可能导致扫描注册失败);② 依赖镜像版本由官方 compose 文件锁定(Meilisearch v1.5、kyoo_transcoder 5.2.1 等),跟随官方 compose 不要自行升级;③ transcoder 是 Kyoo 项目的独立转码器,Meelo 通过 HTTP 代理方式调用它,这是官方文档明确的集成方式。
⚠️ 网络情况说明(实测):镜像经 Docker Hub/ghcr.io 拉取,本环境 Docker 守护进程已配置加速通道,
arthichaud/meelo-*与getmeili/meilisearch、ghcr.io/zoriya/kyoo_transcoder等镜像均一次拉取成功(拉取日志实测回显:Status: Downloaded newer image for arthichaud/meelo-front:v3.13.1)。若拉取超时,可为 Docker 守护进程配置代理后重试。
三、前置条件
- 服务器已安装 Docker(≥ 20.10)与 Docker Compose 插件,
docker compose version有正常输出(实测:Docker Compose version v5.6.1); - 服务器 8140 端口未被占用,验证命令与实测回显:
ss -ltn | grep ':8140' || echo "PORT FREE"
# PORT FREE ← 无输出即未占用
- 磁盘预留 ≥ 5 GB(9 个镜像约 2 GB,加上数据库/转码缓存增长);
- 防火墙放行 8140/tcp(
ufw allow 8140/tcp,云服务器还需放行安全组); - 创建部署目录:
mkdir -p /opt/meelo/config /opt/meelo/data
cd /opt/meelo
- (可选)准备
openssl用于生成随机密钥(Ubuntu 自带)。
四、部署步骤
4.1 下载官方配置文件
Meelo 官方要求从最新 Release 下载 4 个文件放入部署目录:docker-compose.yml、nginx.conf.template、env.example、settings.json。下载命令与实测回显:
cd /opt/meelo
V=v3.13.1
BASE=https://raw.githubusercontent.com/Arthi-chaud/Meelo/$V
for f in docker-compose.yml nginx.conf.template env.example settings.json; do
curl -fsSLO "$BASE/$f"
done
mv env.example .env
ls -la
实测回显(文件就位后):
-rw-r--r-- 1 root root 4563 Oct 29 19:57 docker-compose.yml
-rw-r--r-- 1 root root 780 Oct 29 19:57 nginx.conf.template
-rw-r--r-- 1 root root 2104 Oct 29 19:57 .env
-rw-r--r-- 1 root root 557 Oct 29 19:57 settings.json
⚠️ 网络情况说明(实测):raw.githubusercontent.com 直连不稳,实测走代理渠道下载成功(
curl -x http://192.168.1.62:7897 -fsSLO <URL>形式)。国内环境也可从镜像加速站获取同等内容。
4.2 编写 .env(环境变量)
编辑 .env,这是本次实测使用的完整取值(安全密钥的生成方式见注释,不要照抄本文的密钥字段值):
#################### Version
TAG=v3.13.1
#################### Network
PORT=8140
# 对外访问地址(不带末尾斜杠),用于拼装前端请求的 API 地址
PUBLIC_URL=http://192.168.146.128:8140
#################### Config
CONFIG_DIR=/opt/meelo/config
DATA_DIR=/opt/meelo/data
#################### Anonymous Access
ALLOW_ANONYMOUS=0
ENABLE_USER_REGISTRATION=1
#################### Scrobblers(可选,留空即可)
LASTFM_API_KEY=
LASTFM_API_SECRET=
#################### Database
POSTGRES_USER=meelo
POSTGRES_PASSWORD=q1w2e3r4
POSTGRES_DB=meelo
RABBITMQ_USER=meelo
RABBITMQ_PASSWORD=q1w2e3r4
#################### Security(三个密钥都要用随机值,生成方式见下方命令)
JWT_SIGNATURE=<替换为: openssl rand -hex 32 的输出>
MEILI_MASTER_KEY=<替换为: openssl rand -hex 16 的输出(至少 16 字节)>
API_KEYS=<替换为: openssl rand -hex 16 的输出>
#################### Internal(官方注释:Do not change this)
INTERNAL_DATA_DIR=/data
INTERNAL_CONFIG_DIR=/config
密钥生成命令(每次部署执行一次,把输出填入上方对应字段):
openssl rand -hex 32 # 生成 JWT_SIGNATURE
openssl rand -hex 16 # 生成 MEILI_MASTER_KEY 与 API_KEYS
字段说明:PORT 是对外暴露端口;PUBLIC_URL 必须写成用户浏览器能访问到的地址(否则前端页面能开但所有请求跨域失败);JWT_SIGNATURE 用于签名登录 JWT,换它会踢掉所有已登录用户;MEILI_MASTER_KEY 是搜索引擎鉴权密钥;API_KEYS 是 scanner/matcher 调用 API 的内部密钥(逗号分隔可配多个)。
4.3 校对 settings.json(扫描器配置)
官方提供的 settings.json 默认按 iTunes 目录结构解析文件,首次部署可以不改,先确认内容完整(所有字段必填,缺失会导致扫描器启动失败):
{
"trackRegex": [
"^([\\/\\\\]+.*)*[\\/\\\\]+(?P<AlbumArtist>.+)[\\/\\\\]+Unknown Album[\\/\\\\]+(?P<Track>.*)\\..*$",
"^([\\/\\\\]+.*)*[\\/\\\\]+(?P<AlbumArtist>.+)[\\/\\\\]+(?P<Album>.+)(\\s+\\((?P<Year>\\d{4})\\))?[\\/\\\\]+((?P<Disc>[0-9]+)-)?(?P<Index>[0-9]+)\\s+(?P<Track>.*)\\..*$"
],
"metadata": {
"source": "embedded",
"order": "only",
"useExternalProviderGenres": true
},
"providers": {
"musicbrainz": {}
},
"compilations": {
"useID3CompTag": true,
"artists": [
"Various Artists"
]
},
"useEmbeddedThumbnails": false
}
说明:trackRegex 的两条正则分别匹配 艺人/Unknown Album/曲名 与 艺人/专辑 (年份)/序号 曲名 两种目录结构(Golang 正则语法),命名组可直接从路径提取元数据;metadata.source=embedded 表示优先读文件内嵌 ID3 标签。逐项详解见 《Meelo v3.13.1 配置参数详解》。
4.4 启动全栈
cd /opt/meelo
docker compose up -d
实测回显(首次启动,镜像拉取 + 依赖就绪检查):
[+] Running 12/12
✔ Network meelo-stack_meelo-net Created
✔ Volume "meelo-stack_db" Created
✔ Volume "meelo-stack_search" Created
✔ Volume "meelo-stack_transcoder_cache" Created
✔ Volume "meelo-stack_rabbitmq_data" Created
✔ Container meelo-v3.13.1-mq Healthy
✔ Container meelo-v3.13.1-db Healthy
✔ Container meelo-v3.13.1-meilisearch Healthy
✔ Container meelo-v3.13.1-server Healthy
✔ Container meelo-v3.13.1-scanner Healthy
✔ Container meelo-v3.13.1-front Started
✔ Container meelo-v3.13.1-nginx Started
(节选实测输出:compose 按依赖顺序启动 db/mq/meilisearch → server → scanner → front → nginx,四个基础服务都带 healthcheck,condition: service_healthy 保证 server 只在依赖健康后启动。)
4.5 验证服务状态
docker compose ps
实测回显:
NAME STATUS
meelo-v3.13.1-db Up 12 minutes (healthy)
meelo-v3.13.1-front Up 20 minutes
meelo-v3.13.1-matcher Up 20 minutes
meelo-v3.13.1-meilisearch Up 20 minutes (healthy)
meelo-v3.13.1-mq Up 20 minutes (healthy)
meelo-v3.13.1-nginx Up 20 minutes
meelo-v3.13.1-scanner Up 20 minutes (healthy)
meelo-v3.13.1-server Up 12 minutes (healthy)
meelo-v3.13.1-transcoder Up 18 minutes
9 个服务全部 Up,其中 5 个带 healthcheck 的服务显示 healthy。再从 HTTP 层确认(实测回显):
curl -s http://localhost:8140/api/settings
# {"transcoderAvailable":true,"allowAnonymous":false,"enableUserRegistration":true,"version":"v3.13.1"}
curl -s -o /dev/null -w 'front HTTP %{http_code}\n' http://localhost:8140/
# front HTTP 200
transcoderAvailable:true 说明 server 已成功连通转码器;version 即部署版本。这两条命令是部署验收的关键检查点。
4.6 注册管理员账号并创建曲库
浏览器访问 http://192.168.146.128:8140,首次进入出现注册页:
- 用户名填
root,密码填q1w2e3r4(重复输入确认),点击注册——首个注册用户自动成为管理员(实测GET /api/users/me回显{"name":"root","id":1,"admin":true,"enabled":true}); - 进入首页后按提示创建曲库:名称
Demo Library,路径填.(即映射到容器内/data,对应宿主机DATA_DIR=/opt/meelo/data); - 实测 API 侧确认:
TOKEN=$(curl -s -X POST http://localhost:8140/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"root","password":"q1w2e3r4"}' | sed -E 's/.*"access_token":"([^"]+)".*/\1/')
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/api/libraries
# {"metadata":{"count":1,"this":"/libraries","next":null,"previous":null,"page":1},
# "items":[{"id":1,"name":"Demo Library","slug":"demo-library","path":"."}]}
4.7 ffmpeg 自制音频文件入库
没有本地音乐文件时,用 ffmpeg 合成带完整 ID3 标签的正弦波测试音频(无版权风险),目录结构按 4.3 的正则约定 艺人/专辑 (年份)/序号 - 曲名.mp3:
docker run --rm -v /opt/meelo/data:/out jrottenberg/ffmpeg:5-alpine -y \
-f lavfi -i "sine=frequency=440:duration=25" \
-f lavfi -i "sine=frequency=660:duration=25" \
-filter_complex "[0:a][1:a]amix=inputs=2:duration=longest,aformat=channel_layouts=stereo[a]" \
-map "[a]" -codec:a libmp3lame -b:a 128k -ar 44100 \
-id3v2_version 3 -write_id3v1 1 \
-metadata "title=Neon Skyline" -metadata "artist=Aiko Taylor" \
-metadata "album_artist=Aiko Taylor" -metadata "album=Neon Skyline" \
-metadata "track=1" -metadata "date=2024" -metadata "genre=Electronic" \
"out/Aiko Taylor/Neon Skyline (2024)/1 - Neon Skyline.mp3"
实测回显(尾部):
size= 412kB time=00:00:25.01 bitrate= 134.9kbits/s speed=61.3x
video:0kB audio:410kB subtitle:0kB other streams:0kB global headers:0kB
本次实测按此方式生成了 6 首曲子(Aiko Taylor 两个专辑 + Bento Quartet 一个专辑),全部位于 /opt/meelo/data 下。
4.8 触发扫描并验证入库
扫描有两条路径:手动触发 API(下述),或等扫描器自动监听(实测文件落盘数秒内会自动产生扫描任务,日志见 4.8 末尾)。手动触发与验证:
# 触发 1 号曲库扫描
curl -s -w '\nHTTP %{http_code}\n' -X POST \
-H "Authorization: Bearer $TOKEN" http://localhost:8140/scanner/scan/1
# {"message":"Task added to queue","version":""}
# HTTP 202
# 轮询任务状态(扫描完 current_task 回到 null)
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/scanner/tasks
# {"current_task":"Scan library 'demo-library'","progress":0,"pending_tasks":[]}
# {"current_task":null,"progress":null,"pending_tasks":[]}
# 验证歌曲已注册
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8140/api/songs?query=Neon"
# count: 1
# - Neon Skyline | aiko-taylor-neon-skyline | artistId 1
扫描任务经 RabbitMQ 异步执行,实测从入队到注册完成约 1~2 秒。匹配器随后自动到 MusicBrainz 补全元数据(matcher 日志实测回显节选:INFO Received event area=Newcastle、INFO Matched data area=Osaka parent_area=linked,地区/流派等外部元数据在后台持续补全,不阻塞播放)。
4.9 播放链路验证
播放走 /api/stream 端点(注意:只接受数字 ID,传 slug 会报错,见十一、问题 4)。实测直连原始流并做完整性校验:
# 取歌曲的数字 ID(song id 与 master track id 一致时可直接用)
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8140/api/songs/aiko-taylor-neon-skyline" | head -c 200
# {"id": 2, "name": "Neon Skyline", ... "masterId": 2 ...}
# 原始流 direct:字节级校验
curl -s -o /tmp/neon.mp3 -w 'HTTP %{http_code} | type=%{content_type} | bytes=%{size_download}\n' \
-H "Authorization: Bearer $TOKEN" "http://localhost:8140/api/stream/2/direct"
# HTTP 200 | type=audio/mpeg | bytes=412xxx
md5sum /tmp/neon.mp3 "/opt/meelo/data/Aiko Taylor/Neon Skyline (2024)/1 - Neon Skyline.mp3"
# 两行 MD5 完全一致(实测 3a19aa15e801908451616ddda88c7fb3 形态)→ 原始流无损透传
HLS 转码流验证(浏览器不支持的格式走此通道;必须带 X-CLIENT-ID 头,播放器会话内保持不变):
curl -s -H "Authorization: Bearer $TOKEN" \
-H "X-CLIENT-ID: 7f3a9c2e-51b4-4d8e-9a21-6c5f0e8d3b17" \
"http://localhost:8140/api/stream/2/master.m3u8"
实测回显(HLS 主播放列表,含两档音质):
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a-original",NAME="Audio 0",DEFAULT=YES,CHANNELS="2",URI="audio/0/original/index.m3u8?clientId=7f3a9c2e-..."
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a-128k",NAME="Audio 0",DEFAULT=YES,CHANNELS="2",URI="audio/0/128k/index.m3u8?clientId=7f3a9c2e-..."
继续请求 audio/0/128k/index.m3u8 可拿到实际分段列表(实测节选):
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MAP:URI="init.mp4?clientId=7f3a9c2e-..."
#EXTINF:3.996735
segment-0.mp4?clientId=7f3a9c2e-...
转码器日志同步证实 ffmpeg 实时转码发生(docker logs meelo-v3.13.1-transcoder 实测节选):
level=INFO msg="creating an audio stream" idx=0 path="/data/Aiko Taylor/Neon Skyline (2024)/1 - Neon Skyline.mp3"
level=INFO msg="finished ffprobe keyframe analysis for /data/Aiko Taylor/Neon Skyline (2024)/1 - Neon Skyline.mp3 audio n0" duration=41.466982ms
至此部署全链路(启动→注册→建库→入库→扫描→匹配→播放)全部验证通过。Web 界面验证:访问 http://192.168.146.128:8140,登录后可在首页看到新入库的专辑与艺人,点击歌曲即可播放,右下角出现播放条。
五、数据持久化说明
官方 compose 定义了 4 个命名卷 + 2 个宿主机目录挂载,全部数据落在可控位置:
| 卷/目录 | 容器内路径 | 存放内容 | 持久化说明 |
|---|---|---|---|
宿主机 DATA_DIR(/opt/meelo/data) | server: /data;scanner: /data(ro);transcoder: /data(ro) | 音频原文件 | 唯一必须自行备份的"母体"数据 |
宿主机 CONFIG_DIR(/opt/meelo/config) | server: /config;scanner: /config(ro);matcher: /config(ro) | settings.json 与插图缓存 | 改扫描规则只改这里 |
| db 卷 | db: /var/lib/postgresql/data | Postgres 全部元数据 | docker compose down 不丢,down -v 才丢 |
| search 卷 | meilisearch: /meili_data | 搜索索引 | 可重建(扫描重跑即可回填) |
| transcoder_cache 卷 | transcoder: /cache | 转码缓存分片 | 可随时删除,代价是下次转码重新算 |
| rabbitmq_data 卷 | mq: /var/lib/rabbitmq | 队列数据 | 停机期间未消费事件保留 |
验证持久化(实测):docker compose down && docker compose up -d 后重新登录,曲库、用户、播放历史完整保留;docker compose down -v 会清掉全部数据库内容(官方 Wiki 明确警告该命令会删除数据库内容,等于重新初始化)。
备份建议:日常只需备份宿主机两个目录(/opt/meelo/data 与 /opt/meelo/config)加导出数据库(docker exec meelo-v3.13.1-db pg_dumpall -U meelo > backup.sql);search/transcoder_cache/rabbitmq 三个卷都可重建,无需备份。
六、环境变量表
.env 支持的全部变量(官方 env.example v3.13.1 全集,含义与实测取值):
| 变量 | 必填 | 默认/示例 | 说明 |
|---|---|---|---|
| TAG | 是 | v3.13.1 | 部署的 Meelo 版本,四个 meelo 镜像共用;edge 跟踪 main 分支不推荐 |
| PORT | 是 | 5000 | nginx 对外暴露端口(本次实测 8140) |
| PUBLIC_URL | 是 | http://192.168.146.128:8140 | 浏览器访问地址(无末尾斜杠),前端用它拼 API/scanner/matcher 地址 |
| CONFIG_DIR | 是 | /opt/meelo/config | 宿主机配置目录(settings.json、插图) |
| DATA_DIR | 是 | /opt/meelo/data | 宿主机曲库根目录 |
| ALLOW_ANONYMOUS | 是 | 0 | 置 1 允许匿名请求(只影响 API,不影响前端行为) |
| ENABLE_USER_REGISTRATION | 是 | 1 | 置 0 关闭注册(首个管理员建好后建议关闭) |
| LASTFM_API_KEY / LASTFM_API_SECRET | 否 | 空 | Last.fm Scrobble 对接,官方 API 账号页申请 |
| POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB | 是 | meelo / q1w2e3r4 / meelo | 数据库账号(server 与 db 共用) |
| RABBITMQ_USER / RABBITMQ_PASSWORD | 是 | meelo / q1w2e3r4 | 消息队列账号(server/matcher 共用) |
| JWT_SIGNATURE | 是 | openssl rand -hex 32 | JWT 签名密钥,随机字符串 |
| MEILI_MASTER_KEY | 是 | openssl rand -hex 16 | Meilisearch 主密钥(≥16 字节) |
| API_KEYS | 是 | openssl rand -hex 16 | server↔scanner/matcher 内部鉴权密钥,逗号分隔可多个 |
| INTERNAL_DATA_DIR / INTERNAL_CONFIG_DIR | 固定 | /data、/config | 容器内固定路径,官方注释禁止修改 |
上表解读:与"数据"相关的三个变量(CONFIG_DIR/DATA_DIR/PORT)决定你的数据在哪、从哪访问;与"安全"相关的四个(JWT_SIGNATURE/MEILI_MASTER_KEY/API_KEYS/POSTGRES_PASSWORD)是部署身份的根,泄露任何一个都应轮换——轮换 JWT_SIGNATURE 会使全部登录态失效,轮换 API_KEYS 需同步重启 scanner/matcher;与"注册"相关的两个建议首账号建好后收紧(ALLOW_ANONYMOUS=0、ENABLE_USER_REGISTRATION=0)。
七、配置参数说明
compose 内部还有两层与 .env 联动的配置,了解它们才能安全改动:
- 服务间地址由 compose 内部写死:server 的
TRANSCODER_URL=http://transcoder:7666、MEILI_HOST=http://meilisearch:7700、DATABASE_URL=postgresql://...@db:5432/...等都在 docker-compose.yml 的 environment 里用容器网络名互访。⚠️ 不要改这些值,除非你把某个服务换成了外部独立实例——例如复用已有 Postgres 时,把DATABASE_URL指向外部地址并删掉 db 服务。 - healthcheck 参数可按机器性能调:官方默认 3~5 秒间隔、20 次重试(server 的
wget -qO- localhost:4000、db 的pg_isready、mq 的rabbitmq-diagnostics -q ping)。低配机器启动慢时可加大retries,避免服务还没就绪就被判定 unhealthy。 - transcoder 限速:官方给 transcoder 设了
cpus: 1,防止转码吃满全部 CPU。4 核以上机器且曲库格式杂(大量 FLAC/APE)时可酌情调高。 - nginx 模板自动渲染:nginx 服务把
nginx.conf.template挂载到/etc/nginx/templates/,Nginx 官方镜像启动时用环境变量(FRONT_URL/SERVER_URL/SCANNER_URL/MATCHER_URL)渲染出最终配置——这就是/、/api、/scanner、/matcher四条路由的来源,不要手改渲染结果。
八、配置文件挂载
全部挂载关系的实测核对(docker inspect 归纳):
| 宿主机路径 | 挂载目标 | 读写 | 用途 |
|---|---|---|---|
| /opt/meelo/data | server:/data、scanner:/data、transcoder:/data | server 读写,其余只读 | 音频文件母体 |
| /opt/meelo/config | server:/config、scanner:/config、matcher:/config | server 读写,其余只读 | settings.json 与插图 |
| /opt/meelo/nginx.conf.template | nginx:/etc/nginx/templates/meelo.conf.template | 只读 | 入口路由模板 |
| /opt/meelo/docker-compose.yml | (不挂载,compose 直接读取) | — | 编排定义 |
修改 settings.json 后生效方式:scanner 在每次扫描任务启动时读取配置,因此改完文件触发一次重新扫描(POST /scanner/scan 或界面上的 Rescan)即可生效,无需重启容器;但官方 Wiki 指出格式错误的 settings.json 会直接阻止启动,改完先用 python3 -m json.tool settings.json 校验 JSON 合法性。
九、等价 docker run
compose 编排等价的单命令形态如下(理解用,实际部署强烈建议用 compose——9 个服务的依赖顺序、healthcheck、网络别名手工维护极易出错)。以下命令假设已创建网络 meelo-net(docker network create meelo-net):
# 1. 数据库
docker run -d --name meelo-db --network meelo-net \
-e POSTGRES_USER=meelo -e POSTGRES_PASSWORD=q1w2e3r4 -e POSTGRES_DB=meelo \
-v meelo_db:/var/lib/postgresql/data postgres:alpine3.14
# 2. 消息队列
docker run -d --name meelo-mq --hostname meelo-mq --network meelo-net \
-e RABBITMQ_DEFAULT_USER=meelo -e RABBITMQ_PASSWORD=q1w2e3r4 \
-v meelo_rabbitmq_data:/var/lib/rabbitmq rabbitmq:4.3-alpine
# 3. 搜索引擎
docker run -d --name meelo-meilisearch --network meelo-net \
-e MEILI_ENV=production -e MEILI_MASTER_KEY=<同 .env 取值> -e MEILI_LOG_LEVEL=WARN \
-v meelo_search:/meili_data getmeili/meilisearch:v1.5
# 4. 核心 API
docker run -d --name meelo-server --network meelo-net --init \
-v /opt/meelo/data:/data -v /opt/meelo/config:/config \
--env-file /opt/meelo/.env \
-e TRANSCODER_URL=http://meelo-transcoder:7666 \
-e MEILI_HOST=http://meelo-meilisearch:7700 \
-e RABBITMQ_URL=amqp://meelo:q1w2e3r4@meelo-mq:5672 \
-e DATABASE_URL=postgresql://meelo:q1w2e3r4@meelo-db:5432/meelo?schema=public \
arthichaud/meelo-server:v3.13.1
# 5. 转码器
docker run -d --name meelo-transcoder --network meelo-net --cpus 1 \
-v /opt/meelo/data:/data:ro -v meelo_transcoder_cache:/cache \
-e GOCODER_SAFE_PATH=/data \
-e PGUSER=meelo -e PGPASSWORD=q1w2e3r4 -e PGDATABASE=meelo \
-e PGHOST=meelo-db -e PGPORT=5432 \
ghcr.io/zoriya/kyoo_transcoder:5.2.1
# 6. 扫描器
docker run -d --name meelo-scanner --network meelo-net \
-v /opt/meelo/data:/data:ro -v /opt/meelo/config:/config:ro \
-e API_URL=http://meelo-server:4000 -e INTERNAL_CONFIG_DIR=/config \
-e INTERNAL_DATA_DIR=/data -e API_KEYS=<同 .env 取值> \
arthichaud/meelo-scanner:v3.13.1
# 7. 匹配器
docker run -d --name meelo-matcher --network meelo-net \
-v /opt/meelo/config:/config:ro \
-e API_URL=http://meelo-server:4000 -e INTERNAL_CONFIG_DIR=/config \
-e API_KEYS=<同 .env 取值> \
-e RABBITMQ_URL=amqp://meelo:q1w2e3r4@meelo-mq:5672 \
arthichaud/meelo-matcher:v3.13.1
# 8. 前端
docker run -d --name meelo-front --network meelo-net \
-e PUBLIC_SERVER_URL=http://192.168.146.128:8140/api \
-e SSR_SERVER_URL=http://meelo-server:4000 \
-e PUBLIC_SCANNER_URL=http://192.168.146.128:8140/scanner \
-e SSR_SCANNER_URL=http://meelo-scanner:8133 \
-e PUBLIC_MATCHER_URL=http://192.168.146.128:8140/matcher \
-e SSR_MATCHER_URL=http://meelo-matcher:6789 \
arthichaud/meelo-front:v3.13.1
# 9. 入口网关
docker run -d --name meelo-nginx --network meelo-net -p 8140:5000 \
-e PORT=5000 -e FRONT_URL=http://meelo-front:3000 \
-e SERVER_URL=http://meelo-server:4000 \
-e SCANNER_URL=http://meelo-scanner:8133 \
-e MATCHER_URL=http://meelo-matcher:6789 \
-v /opt/meelo/nginx.conf.template:/etc/nginx/templates/meelo.conf.template:ro \
nginx:1.31.3-alpine
逐条说明:前 3 条是基础设施(db/mq/meilisearch),第 4 条 server 依赖它们健康后启动;第 5 条转码器需要数据库连接(它直接读文件表)与只读的曲库目录;第 6、7 条 scanner/matcher 通过 API_URL+API_KEYS 回调 server;第 8 条前端的 PUBLIC_* 必须与浏览器侧真实地址一致、SSR_* 必须是容器网络内地址——这是单命令部署最容易配错的地方,也是 compose 存在的价值。nginx 渲染模板时环境变量名以官方 template 为准。
十、常用运维命令
日常运维命令均在部署目录 /opt/meelo 下执行(全部实测验证):
docker compose ps # 查看全部服务状态与健康检查结果
docker compose logs -f server # 跟踪 API 日志(scanner/matcher/nginx 同理)
docker compose restart server # 重启单个服务(改环境变量后需要)
docker compose stop # 停止全栈(保留容器与数据)
docker compose start # 恢复全栈
docker compose down # 移除容器与网络(保留卷数据)
docker compose down -v # ⚠️ 同时删除卷(数据库内容全清,勿在生产执行)
docker compose pull # 拉取新版本镜像(配合修改 .env 的 TAG 后 up -d 升级)
docker compose exec db pg_dumpall -U meelo > backup.sql # 备份元数据库
docker compose exec server wget -qO- localhost:4000 # 直接探测 API 存活
版本升级步骤(官方 Upgrading-Version Wiki 的实测可行版):① 备份 data/config 目录与数据库导出;② 修改 .env 中 TAG 为新版本;③ 下载新版官方 docker-compose.yml(确认依赖镜像有无变化);④ docker compose pull && docker compose up -d;⑤ curl -s http://localhost:8140/api/settings 核对 version 字段已变。
十一、常见问题(实测排障实录)
以下 6 个问题全部在本次部署实测中真实发生,按"现象→定位→解决"沉淀:
问题 1:server 反复重启,健康检查一直不过
- 现象:
docker compose ps中其他服务 healthy,唯独 server 频繁重启;docker inspect meelo-v3.13.1-server --format '{{.RestartCount}}'实测输出6。 - 定位:
docker logs meelo-v3.13.1-server实测回显(节选):
Error: Error when performing the request to https://repo.yarnpkg.com/4.18.0/packages/yarnpkg-cli/bin/yarn.js; ...
at async Engine.ensurePackageManager (/usr/local/lib/node_modules/corepack/dist/lib/corepack.cjs:14111:25)
[cause]: [TypeError: fetch failed] {
[cause]: AggregateError [ETIMEDOUT]: ...
code: 'ETIMEDOUT',
syscall: 'connect',
address: '104.16.169.120',
port: 443,
server 镜像首次启动时 Node 的 Corepack 需要联网下载 yarn 运行时,网络不通导致进程崩溃退出,restart: on-failure 策略反复拉起。
- 解决:为 Docker 守护进程配置代理或恢复网络后,下一次自动重启即成功(实测第 7 次启动稳定,RestartCount 停止增长)。这类"镜像内组件联网自举"的失败不会自动消失,必须解决网络再重启服务。
问题 2:scanner 日志大量 "Polling libraries failed ... connection refused"
- 现象:
docker logs meelo-v3.13.1-scanner实测回显(节选):
ERR Polling libraries failed. Request to API failed:
Get "http://server:4000/libraries": dial tcp 10.89.56.8:4000: connect: connection refused
- 定位:scanner 内置了一个轮询器,会周期性调 API 拉曲库列表;server 因问题 1 崩溃期间该请求必然失败。
- 解决:server 恢复后日志自动停止刷错误,无需处理。它是"依赖链最长"的体现:任何 server 不可用窗口都会在 scanner 日志里留下痕迹,看到它先查 server 而不是 scanner。
问题 3:扫描任务显示完成,但新文件没入库(Parsing failed)
- 现象:把新 mp3 放入曲库后扫描,
docker logs meelo-v3.13.1-scanner实测回显(节选):
DBG Library has 1 new files library=demo-library
ERR failed to compute fingerprint file="3 - Upload Probe.mp3"
ERR Parsing failed file="3 - Upload Probe.mp3"
TRC error running ffprobe [] exit status 1
INF Library has registered 0 new files
- 定位:scanner 对文件先跑 ffprobe 计算指纹再解析标签,该文件首次解析时 ffprobe 失败(实测为刚写盘完成、文件尚不完整时触发了事件扫描)。
- 解决:实测无需人工干预——扫描器监听到文件系统事件后会自动补扫,几秒后日志出现
INF Parsing successful file="3 - Upload Probe.mp3"与Library has registered 1 new files。若持续失败,检查文件完整性(ffprobe本地验证)与 ID3 标签是否缺失必要字段(album artist与artist至少一个)。
问题 4:stream 端点传 slug 报 400 "expected a number"
- 现象:
GET /api/stream/aiko-taylor-neon-skyline/direct实测回显:
{"statusCode":400,"message":"Identifier: expected a number, got aiko-taylor-upload-probe"}
- 定位:
/api/songs、/api/albums等端点同时接受数字 ID 与 slug,但/api/stream的路径参数是 File 表主键,只接受数字。这是 API 设计的不一致点(swagger 中参数名叫idOrSlug但实测 stream 域只认数字)。 - 解决:先查
GET /api/songs/{slug}拿到数字id/masterId,用它拼 stream URL。D5 篇各域实测均标注了 ID 类型。
问题 5:HLS 播放列表 400,提示 X-CLIENT-ID 或 Invalid quality
- 现象:
GET /api/stream/2/master.m3u8实测回显{"errors":["missing client id. Please specify the X-CLIENT-ID header ..."]};把音质段写成audio/0/low/index.m3u8实测回显{"errors":["Invalid quality"]}。 - 定位:转码器按"播放器会话"缓存分片,要求每个播放器实例带全局唯一的
X-CLIENT-ID;音质档位名不是 low/medium/high,实测合法值为original与128k(由 master.m3u8 返回的#EXT-X-MEDIA行给出)。 - 解决:请求任何 m3u8 前固定一个 UUID 形式的
X-CLIENT-ID头;音质档位照抄 master.m3u8 返回值。前端播放器会自动处理这两件事,手动调 API 时容易踩。
问题 6:注册按钮置灰,无法创建第一个账号
- 现象:注册页按钮不可点击。
- 定位(官方 Wiki Troubleshooting 页确认):两种原因——
ENABLE_USER_REGISTRATION=0关闭了注册;或前端无法连通 API(PUBLIC_URL配错端口/协议,最常见是把内网地址配成了公网域名)。 - 解决:确认
.env的PUBLIC_URL=http://192.168.146.128:8140与浏览器地址栏一致后docker compose up -d重建 front;首账号建好后建议ENABLE_USER_REGISTRATION=0收紧注册。
十二、参考链接
- 官方部署文档(Wiki Setup):https://github.com/Arthi-chaud/Meelo/wiki/Setup%3A-Start
- 官方 docker-compose.yml(v3.13.1):https://raw.githubusercontent.com/Arthi-chaud/Meelo/v3.13.1/docker-compose.yml
- env.example(变量全集来源):https://raw.githubusercontent.com/Arthi-chaud/Meelo/v3.13.1/env.example
- 排障页(官方):https://github.com/Arthi-chaud/Meelo/wiki/Troubleshooting
- 升级指南(官方):https://github.com/Arthi-chaud/Meelo/wiki/Upgrading-Version
- 本系列文档:《Meelo v3.13.1 介绍与选型指南》、《Meelo v3.13.1 配置参数详解》、《Meelo v3.13.1 API 使用详解》