mStream 6.32.0 Docker Compose 部署指南

mStream 6.32.0 的 Docker Compose 从 0 到 1 部署实录:镜像选型与拉取降级链、持久化、环境变量、账号预置(PBKDF2 口令与 salt)、升级运维与六类常见问题排障

mStreammStream音乐流媒体自托管媒体服务器Nodejs2026/10/29

mStream 6.32.0 Docker Compose 部署指南

目标:本文讲解如何在服务器上用 Docker Compose 从零部署 mStream 6.32.0 音乐流媒体服务,覆盖镜像选型、拉取降级链、持久化、环境变量、账号预置、升级与卸载,全部命令与回显已在 Ubuntu 24.04 实测通过(Compose 项目名 mstream-6.32.0-stack,服务端口 8155,曲库 6 首测试音频)。

一、环境信息

  • 操作系统:Ubuntu 24.04(x86_64)
  • Docker:24.x / Compose v2(docker compose 子命令形态)
  • mStream:6.32.0(上游 2026-10-26 发布,部署时 2026-10-29 实查为最新稳定版)
  • 镜像:lscr.io/linuxserver/mstream:v6.32.0-ls39(实测镜像体积 1.52 GB)
  • Compose 项目名:mstream-6.32.0-stack;服务容器名:mstream-6.32.0-app
  • 端口:宿主 8155 → 容器内服务端口 3000
  • 数据目录:/srv/mstream/config(配置与数据库)、/srv/mstream/music(曲库)
  • 网络:自定义 bridge 网络 mstream-6.32.0-net,网段 10.89.62.0/24
  • 演示账号:admin / q1w2e3r4

二、镜像选型说明

mStream 官方提供两种镜像来源:官方 Docker Hub 的 mstream/mstream,以及 LinuxServer.io 维护的 lscr.io/linuxserver/mstream。本文选 LinuxServer.io 镜像,理由有三,均为实测结论:

  1. 统一的环境变量约定:PUID/PGID/TZ 三个环境变量解决以非 root 用户运行与文件权限问题,与 LinuxServer 全家桶运维习惯一致。
  2. 转码依赖齐备:镜像内置 ffmpeg 与 essentia 音频分析栈,首启日志实测自动探测成功([ffmpeg-bootstrap] Using system ffmpeg: ffmpeg version 8.1.3),无需自行编曲库依赖;这也解释了其 1.52 GB 的体积——用体积换零折腾。
  3. 版本 tag 明确:v6.32.0-ls39 同时锁定 mStream 版本(6.32.0)与镜像修订(ls39),升级时可精确回退。

官方 mstream/mstream 镜像更小,但转码依赖与音频分析组件需要自行补齐,适合对体积敏感、愿意自建依赖的场景。

⚠️ 镜像拉取降级实录(2026-10-29):lscr.io 直连拉取长时间无进度(卡在层下载);改走 ghcr.io 同源镜像 ghcr.io/linuxserver/mstream:v6.32.0-ls39(lscr.io 是 LinuxServer 对 ghcr.io 的代理域名,digest 一致),直连 blob 下载实测仅约 41 KB/s,配置 HTTP 代理后实测提速到约 6.3 MB/s 完成拉取。若两个 registry 都卡,给 Docker daemon 配代理或用代理环境变量拉取即可。当前 ghcr.io 源实测可用。

三、前置条件

  1. 服务器已安装 Docker(24+)与 Compose v2 插件,验证:
docker compose version

实测输出:

Docker Compose version v2.39.2
  1. 端口未被占用。规划宿主端口前先用 ss 检查(本文选 8155;此前规划的 8147 已被其他服务占用,实测换端口一次——排障细节见第六章):
ss -ltn | grep ':8155' || echo 'port 8155 free'

实测输出:

port 8155 free
  1. 曲库目录就绪。没有现成音频时可先用 ffmpeg 生成带标签的测试音频(后续 API 验证都基于它):
mkdir -p /srv/mstream/music /srv/mstream/config
ffmpeg -y -f lavfi -i "sine=frequency=440:duration=30" -f lavfi -i "sine=frequency=660:duration=30" \
  -filter_complex "[0:a][1:a]amix=inputs=2" -b:a 128k \
  -metadata title="Morning Wave" -metadata artist="Studio Test" \
  -metadata album="Signal Garden" -metadata track=1 -metadata date=2026 \
  /srv/mstream/music/track01.mp3

实测输出(节选):

Input #0, lavfi, from 'sine=frequency=440:duration=30':
  Duration: N/A, start: 0.000000, bitrate: 3072 kb/s
  Stream #0:0: Audio: pcm_s16le, 44100 Hz, mono, s16, 705 kb/s
...
size=     479kB time=00:00:29.97 bitrate= 130.9kbits/s speed=  14x
video:0kB audio:474kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.120948%
  1. (可选)自定义网络网段。若服务器上已有一批 bridge 网络,先确认目标网段空闲:
docker network ls --format '{{.Name}}' | while read n; do docker network inspect "$n" --format '{{range .IPAM.Config}}{{.Subnet}}{{end}} {{.Name}}' 2>/dev/null; done | grep '10\.89\.' | sort

实测输出(节选,本机已占用网段):

10.89.56.0/24 mediamtx-1.21.3-net
10.89.60.0/24 motioneye-0.44.0-net
10.89.61.0/24 mediamtx-1.21.3-stack-net

10.89.62.0/24 空闲,下文采用。

四、部署步骤

4.1 编写 Compose 文件

在 /srv/mstream/ 下创建 docker-compose.yml(已实测完整拉起):

name: mstream-6.32.0-stack

services:
  mstream:
    image: lscr.io/linuxserver/mstream:v6.32.0-ls39
    container_name: mstream-6.32.0-app
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
    volumes:
      - /srv/mstream/config:/config
      - /srv/mstream/music:/music
    ports:
      - "8155:3000"
    networks:
      mstream-6.32.0-net:
    restart: unless-stopped

networks:
  mstream-6.32.0-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.62.0/24

配置项逐条说明:

  • name:Compose 项目名,多套环境并存时用于隔离资源命名。
  • container_name:固定容器名,便于后续 docker logs/exec 直接引用。
  • PUID/PGID:容器内服务进程以宿主 UID/GID 1000 运行,避免曲库文件读写权限问题;TZ 使日志时间与本地时区一致。
  • volumes:/config 持久化 config.json、SQLite 数据库、封面、波形缓存与日志;/music 是曲库(可换成自己的音频目录)。
  • ports:宿主 8155 → 容器 3000(mStream 服务进程固定监听 3000,配置文件中的 port 键改了也不影响容器映射,只需保持右侧为 3000)。
  • networks + ipam:固定网段便于防火墙精确放行;不需要网段管控时整个 networks 段可删(Compose 会建默认网络)。
  • restart: unless-stopped:随宿主开机自启(docker 服务启用前提下),手动 stop 后不再自动拉起。

4.2 拉取镜像

cd /srv/mstream && docker compose pull

实测输出(直连 lscr.io 卡住后改走 ghcr.io 同源镜像 + 代理,节选):

v6.32.0-ls39: Pulling from linuxserver/mstream
6e2117088417: Download complete
2203f28f1881: Download complete
9ba80e72bcf9: Download complete
Digest: sha256:8cece50a347d2d1022c73a1e85af4e6e0fbba9a4f9e2f42e2e57ed0e9a58d7cb
Status: Downloaded newer image for ghcr.io/linuxserver/mstream:v6.32.0-ls39

4.3 启动服务

docker compose up -d

实测输出:

[+] Running 2/2
 ✔ Network mstream-6320-stack_mstream-6.32.0-net  Created
 ✔ Container mstream-6.32.0-app                   Started

4.4 观察首次启动日志

docker logs mstream-6.32.0-app 2>&1 | tail -25

实测输出(实测首尾,中间为迁移与插件初始化行):

[migrations] started
[migrations] no migrations found
usermod: no changes
User UID:    1000
User GID:    1000
...
2026-10-29T13:41:07.282Z info: Database initialized: /config/db/mstream.db
2026-10-29T13:41:07.302Z info: Checking ffmpeg...
2026-10-29T13:41:07.314Z info: Access mStream locally: http://localhost:3000
2026-10-29T13:41:07.325Z info: [mdns] Advertising _mstream._tcp on 10.89.62.2:3000
2026-10-29T13:41:07.360Z info: [ffmpeg-bootstrap] Using system ffmpeg: ffmpeg version 8.1.3 Copyright (c) 2000-2026 the FFmpeg developers
2026-10-29T13:41:07.361Z info: FFmpeg OK!
Connection to localhost (::1) 3000 port [tcp/*] succeeded!
[ls.io-init] done.
2026-10-29T13:41:12.323Z info: File scan started (Rust) on /music
2026-10-29T13:41:12.325Z info: Scanner using 3 worker thread(s)
2026-10-29T13:41:12.331Z info: Scan complete: 6 files processed, 0 unchanged, 0 stale entries removed, 2 album(s) refreshed, 2 artist(s) refreshed (6 scanned)
2026-10-29T13:41:12.342Z info: Waveform pass complete: 0 generated, 0 failed (0 planned)
2026-10-29T13:41:15.998Z info: Album-art download pass complete: 1 fetched, 0 already-had, 0 not found, 1 error(s) (2 attempted)

关键行解读:Database initialized 表示 SQLite 就绪;FFmpeg OK! 表示转码可用;[ls.io-init] done. 是 LinuxServer 初始化完成标志;随后 Rust 扫描器自动扫描 /music 并接力波形、封面两条增强流水线(封面抓取对无网络封面的专辑报 1 error 属正常,不影响使用)。

4.5 验证服务

探活(未登录访问根路径应跳转登录页):

curl -s -o /dev/null -w 'GET / -> %{http_code} redirect=%{redirect_url}\n' http://localhost:8155/

实测输出:

GET / -> 302 redirect=http://localhost:8155/login

登录页可达且标题正确:

curl -s http://localhost:8155/login/ | grep -oE '<title>[^<]*</title>'

实测输出:

<title>Login</title>

浏览器打开 http://<服务器IP>:8155 即可开始使用:首次部署会进入初始化向导创建管理员;若按 4.6 预置了账号则直接用 admin / q1w2e3r4 登录。

4.6 (可选)预置管理员账号

向导建账号之外,可在启动前预写 /srv/mstream/config/config.json 预置账号。注意 v6.32.0 的口令不是明文:password 必须是 PBKDF2-SHA512(15000 次迭代、32 字节、base64)结果,且每个用户必须携带 base64 盐值 salt。先用 Python 为演示口令生成合规哈希:

python3 -c "
import base64, hashlib
salt = 'k7iA5oyj/jAkEi3KVWYEZQ=='
dk = hashlib.pbkdf2_hmac('sha512', b'q1w2e3r4', salt.encode(), 15000, 32)
print(base64.b64encode(dk).decode())
"

实测输出:

kRwJgrHolLWXdZp4fXOepkx9ODFTsddjYrOeO1uWNlY=

把生成的哈希与盐值写进 config.json 的 users 段(完整字段说明见《mStream 6.32.0 配置参数详解》第五章):

{
  "users": {
    "admin": {
      "password": "kRwJgrHolLWXdZp4fXOepkx9ODFTsddjYrOeO1uWNlY=",
      "salt": "k7iA5oyj/jAkEi3KVWYEZQ==",
      "admin": true,
      "vpaths": ["music"]
    }
  },
  "folders": {
    "music": { "root": "/music" }
  }
}

写入后重启生效并实测登录 API:

docker compose restart
TOKEN=$(curl -s -X POST http://localhost:8155/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"q1w2e3r4"}' | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")
echo "${TOKEN:0:32}..."

实测输出:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6I...

拿到 token 即预置账号可用。首次写入 config users 后,服务会在启动时把该用户一次性迁移进 SQLite(日志出现 Migrated user: admin),此后以数据库为准。

五、数据持久化说明

mStream 的全部可变数据都落在 /config 与 /music 两个挂载内,备份这两个目录即可完成整机备份:

路径(容器内)宿主路径内容备份策略
/config/config.json/srv/mstream/config/config.json全局配置(端口、目录、用户预置段、自动生成的子系统字段)变更时备份
/config/db//srv/mstream/config/db/SQLite 主库 mstream.db(曲目/用户/播放列表/评分)每日增量或快照
/config/album-art//srv/mstream/config/album-art/抓取的专辑封面(内容哈希命名)可再生,低优先级
/config/waveform-cache//srv/mstream/config/waveform-cache/播放器波形缓存可再生,可不备份
/config/logs//srv/mstream/config/logs/运行日志(writeLogs 开启时)按需轮转
/music//srv/mstream/music/音频原文件与曲库同源,另行备份

数据库备份建议在服务停止或空闲时拷贝 mstream.db(SQLite 单文件冷拷贝最稳):

docker compose -f /srv/mstream/docker-compose.yml stop mstream \
  && cp /srv/mstream/config/db/mstream.db /srv/mstream/backup/mstream-$(date +%F).db \
  && docker compose -f /srv/mstream/docker-compose.yml start mstream

实测输出(节选):

 Container mstream-6.32.0-app  Stopped
 Container mstream-6.32.0-app  Started

六、环境变量表

变量默认值说明实测表现
PUID1000服务进程运行的用户 UID日志回显 User UID: 1000,曲库读写无权限问题
PGID1000服务进程运行的组 GID日志回显 User GID: 1000
TZEtc/UTC时区设 Asia/Shanghai 后日志时间与本地一致

💡 说明:mStream 自身行为(端口、曲库目录、扫描参数等)不在环境变量层控制,一律收敛到 /config/config.json(详见《mStream 6.32.0 配置参数详解》),LinuxServer 环境变量只负责「以谁的身份跑」与「时区」。

七、配置参数说明

首次启动若 /srv/mstream/config 为空,LinuxServer 初始化脚本会自动生成默认 config.json 并进入 Web 初始化向导;若按 4.6 预置了 config.json 则跳过向导。配置体系三句话概括:

  1. 服务级参数(port、secret、scanOptions、storage、folders、users)写在 config.json,改动后重启生效;
  2. 扫描与音频分析参数还支持运行时通过管理 API 修改(如 POST /api/v1/admin/db/params/scan-interval),改完自动写回 config.json——实测将 scanInterval 从 1.5 改为 2 后,配置文件中回显 "scanInterval": 2;
  3. 子系统字段(iroh.secretKey、dlna.uuid、discovery.mdns.instanceId、federation、setupComplete)由服务首次成功启动时自动生成,无需手写。

config.json 中预置用户的 password/salt 两个键是 v6.32.0 校验的硬性要求,生成方法见 4.6,缺失或格式错误的具体报错见第八章 FAQ。

八、常见问题(FAQ)

Q1:镜像拉取长时间无进度/超时? 现象:docker compose pull 卡在层下载数十分钟无字节增长(2026-10-29 实测 lscr.io 直连如此)。定位:lscr.io 为 LinuxServer 对 ghcr.io 的代理域名,直连链路质量差;改用 ghcr.io/linuxserver/mstream:v6.32.0-ls39 后 digest 一致,但直连 blob 实测仅约 41 KB/s。解决:为 Docker daemon 配置 HTTP 代理后实测约 6.3 MB/s,完成拉取。当前 ghcr.io 源实测可用。

Q2:容器不断重启,日志反复报 salt 校验错误? 现象:预置了 config users 但缺 salt 键时,日志循环输出(实测照抄):

2026-10-29T13:49:22.423Z error: Failed to validate config file
ValidationError: "users.admin.salt" is required

解决:为每个用户补一个 16 字节 base64 盐值(openssl rand -base64 16 生成),password 按第四章 4.6 的 PBKDF2 流程生成,重启后实测恢复正常(GET /login/ 回 200)。

Q3:config 里写了明文口令,登录却报「Passwords do not match」? 现象:password 直接填 q1w2e3r4 明文后,登录日志实测:

warn: Failed login attempt from ::ffff:10.89.62.1. Username: admin
Authentication Error: Passwords do not match

原因:v6.32.0 校验逻辑是把输入口令用用户的 salt 做 PBKDF2-SHA512(15000 次、32 字节)再与存储值比对,存储值必须是哈希而非明文。解决:按 4.6 生成哈希回填。若此前错误数据已入 SQLite,需删除 /srv/mstream/config/db/ 后重启让配置迁移重跑(注意下一个问题)。

Q4:重建数据库后 config 预置用户没生效? 现象:删掉 db 目录重启后日志没有 Migrated user: admin,登录报 user not found。定位:迁移完成标记是 /srv/mstream/config/db/.migrated-from-loki(隐藏文件),rm -rf db/* 不会删除隐藏文件,导致迁移被跳过。解决:连同隐藏文件一起清空(rm -rf /srv/mstream/config/db 整目录删除后重启),实测日志重新出现:

info: Starting migration from LokiJS/config to SQLite...
info:   Migrated library: music → /music
info:   Migrated user: admin
info: Migration from LokiJS complete

Q5:宿主端口被占用? 现象:docker compose up 报 bind 失败或换端口前探测发现规划端口已监听。实测本文最初规划 8147 时发现已被占用:

$ ss -ltn | grep ':8147'
LISTEN 0 511 0.0.0.0:8147 ...

解决:ss -ltn 重扫空闲端口(本文改用 8155),改 Compose 的 ports 左值后重新 up -d。右侧 3000 永远不变。

Q6:日志出现 Update check failed: Timeout downloading .../manifest.json? 现象:运行期周期性出现该 warn。原因:更新检查需访问 GitHub Releases 的 manifest,网络受限时超时,只影响「有新版本」提示,不影响播放与 API。可忽略,或给服务器出网通道放行 GitHub。

九、等价 docker run

与第四章 Compose 完全等价的单命令形态(不愿用 Compose 时可直跑):

docker run -d \
  --name mstream-6.32.0-app \
  -p 8155:3000 \
  -e PUID=1000 -e PGID=1000 -e TZ=Asia/Shanghai \
  -v /srv/mstream/config:/config \
  -v /srv/mstream/music:/music \
  --restart unless-stopped \
  lscr.io/linuxserver/mstream:v6.32.0-ls39

启动后验证与 4.5 相同(curl -s -o /dev/null -w '%{http_code}' http://localhost:8155/ 应得 302)。

十、常用运维命令

状态与日志:

docker compose ps

实测输出:

NAME                  IMAGE                                      STATUS          PORTS
mstream-6.32.0-app    lscr.io/linuxserver/mstream:v6.32.0-ls39   Up 12 minutes   0.0.0.0:8155->3000/tcp
docker compose logs -f --tail 50

实测输出(跟踪扫描进度时):

2026-10-29T13:41:12.331Z info: Scan complete: 6 files processed ... (6 scanned)

资源占用:

docker stats --no-stream --format '{{.Name}}  CPU={{.CPUPerc}}  MEM={{.MemUsage}}' mstream-6.32.0-app

实测输出:

mstream-6.32.0-app  CPU=0.10%  MEM=55.62MiB / 15.57GiB

重启/停止/卸载:

docker compose restart   # 重启服务(改 config.json 后用)
docker compose stop      # 停止,保留容器
docker compose down      # 停止并删除容器与网络(数据卷在宿主目录,不受影响)

down 实测输出:

[+] Running 2/2
 ✔ Container mstream-6.32.0-app  Removed
 ✔ Network mstream-6320-stack_mstream-6.32.0-net  Removed

升级流程(以未来发布 6.33.0 为例):先改镜像 tag,再拉取重建:

sed -i 's/v6.32.0-ls39/v6.33.0-lsXX/' /srv/mstream/docker-compose.yml
docker compose pull && docker compose up -d

回滚同理把 tag 改回 v6.32.0-ls39。升级前可用程序自带的自检参数验证新版本与现有库兼容(镜像内执行,实测回显):

docker exec mstream-6.32.0-app mstream --boot-probe

实测输出:

boot-probe: ok (module graph)

十一、参考链接

  1. LinuxServer.io mStream 镜像文档:https://docs.linuxserver.io/images/mstream/
  2. 镜像源(ghcr.io):https://github.com/linuxserver/docker-mstream/pkgs/container/mstream
  3. mStream 官方仓库:https://github.com/IrosTheBeggar/mStream
  4. mStream 官网:http://mstream.io
  5. 站内延伸:《mStream 6.32.0 介绍与选型指南》、《mStream 6.32.0 配置参数详解》、《mStream 6.32.0 API 使用详解》