Snapcast 0.35.0 Docker Compose 部署指南

基于官方 deb 本地构建镜像的 Snapcast 0.35.0 三节点 Compose 编排:镜像选型查证、端口与配置挂载、datadir 持久化实测、等价 docker run 与运维命令全回显。

SnapcastSnapcastDockerDocker Compose2026/10/30

Snapcast 0.35.0 Docker Compose 部署指南

目标:在一台 Linux 服务器上用 Docker Compose 编排「1 个 snapserver + 2 个 snapclient」的多房间同步音频栈,完成镜像构建、端口映射、配置挂载与持久化验证。已在 Docker 29.9.2 / Compose v5.6.1 环境实测 Snapcast 0.35.0 三节点拓扑部署与重启恢复通过。

一、环境信息

本文所有命令与回显均在以下实测环境采集:

  • 宿主系统:Debian GNU/Linux 12 (bookworm),x86_64
  • Docker:29.9.2(build a82e528),Docker Compose:v5.6.1
  • Snapcast:v0.35.0 (rev f1237347),服务器与客户端均基于官方 deb(bookworm amd64)构建本地镜像
  • 拓扑:snapcast-server(映射 8151→1704、8152→1705、8153→1780)+ client1 + client2,同一自定义网络 snapcast-0.35.0-net(10.89.61.0/24)
  • 音源:pipe:///serverdata/snapfifo(流名 SineTest,44100:16:2,codec=pcm),由宿主侧脚本持续写入
  • 客户端输出:无声卡环境,--soundcard null 丢弃采样验证同步链路
  • 实测数据日期:2026-10-30

二、镜像选型说明

选择镜像前先核对官方发行渠道,逐项结论如下(2026-10-30 实查):

渠道结论依据
Docker Hub 官方镜像无官方发布官方文档 doc/install.md、doc/build.md 通篇无 Docker 安装指引(raw.githubusercontent.com 实查无命中),官方推荐 deb/PPA/源码编译
GitHub Releases deb 包本文采用v0.35.0 提供 46 个资产,覆盖 bookworm/bullseye/trixie × amd64/arm64/armhf × 标准/with-pulse/with-pipewire 三种后端
第三方社区镜像不推荐无法保证版本与构建透明度,多房间音频栈组件间版本耦合(协议 v2、JSON-RPC v23),应自行从官方 deb 构建

💡 说明:snapserver 与 snapclient 的通信协议有版本号(实测 snapclient 协议版本 2、JSON-RPC 版本 23.0.0),服务器与客户端必须同源构建。从同一 v0.35.0 deb 构建两个本地镜像可彻底锁定版本一致性,这也是本文不使用任何现成镜像的原因。

三、前置条件

  1. Docker 24+ 与 Docker Compose v2+ 已安装(docker compose version 可执行);
  2. 以下端口在宿主机未被占用:8151、8152、8153(映射容器内 1704/1705/1780);
  3. 准备约 300MB 磁盘空间(两个镜像各约 100~200MB,含 debian:bookworm-slim 基础层);
  4. 已下载 v0.35.0 官方 deb:
mkdir -p /opt/snapcast/debs && cd /opt/snapcast/debs
wget https://github.com/snapcast/snapcast/releases/download/v0.35.0/snapserver_0.35.0-1_amd64_bookworm.deb
wget https://github.com/snapcast/snapcast/releases/download/v0.35.0/snapclient_0.35.0-1_amd64_bookworm.deb

⚠️ 注意:GitHub Releases 下载慢或不通时,可走代理(curl -x http://192.168.1.62:7897 -LO <URL>)或从国内镜像加速站获取同版本 deb;deb 完整性以 Release 页 sha256 为准。

四、部署步骤

4.1 目录与文件准备

规划目录结构(配置、数据、构建上下文分离):

/opt/snapcast/
├── compose.yml
├── debs/
│   ├── snapserver_0.35.0-1_amd64_bookworm.deb
│   └── snapclient_0.35.0-1_amd64_bookworm.deb
├── docker/
│   ├── Dockerfile.snapserver
│   └── Dockerfile.snapclient
└── serverdata/
    ├── snapserver.conf
    └── snapfifo        # 命名管道,首次启动后由 snapserver 自动创建

4.2 构建服务器与客户端镜像

两个 Dockerfile 均以 debian:bookworm-slim 为基座,直接安装官方 deb,保证与裸机 deb 安装行为完全一致(含 snapserver/snapclient 运行账号与 audio 组)。

docker/Dockerfile.snapserver:

FROM debian:bookworm-slim

COPY debs/snapserver_0.35.0-1_amd64_bookworm.deb /tmp/snapserver.deb

RUN apt-get update \
    && apt-get install -y --no-install-recommends /tmp/snapserver.deb \
    && rm -rf /var/lib/apt/lists/* /tmp/*.deb

EXPOSE 1704 1705 1780

ENTRYPOINT ["/usr/bin/snapserver"]
CMD ["-c", "/etc/snapserver.conf"]

docker/Dockerfile.snapclient:

FROM debian:bookworm-slim

COPY debs/snapclient_0.35.0-1_amd64_bookworm.deb /tmp/snapclient.deb

RUN apt-get update \
    && apt-get install -y --no-install-recommends /tmp/snapclient.deb \
    && rm -rf /var/lib/apt/lists/* /tmp/*.deb

ENTRYPOINT ["/usr/bin/snapclient"]

在 /opt/snapcast 下构建(输出节选,实测):

docker build -f docker/Dockerfile.snapserver -t snapcast-server-0.35.0:local .
docker build -f docker/Dockerfile.snapclient -t snapcast-client-0.35.0:local .
#8 exporting layers done
#8 exporting manifest sha256:cb20ac9f71c5b791abdffd99403fd1f67cb9b5cf0d4ab87e8ed7b2eb3cab805b done
#8 exporting config sha256:0b002a78c811f24cb48fe51abe7859dabac3edbd1fb755d01ba53c90e579b747 done
#8 naming to docker.io/library/snapcast-server-0.35.0:local done
#8 unpacking to docker.io/library/snapcast-server-0.35.0:local done
#8 DONE 0.1s
#8 naming to docker.io/library/snapcast-client-0.35.0:local done
#8 unpacking to docker.io/library/snapcast-client-0.35.0:local done
#8 DONE 0.1s

验证镜像已生成:

docker images | grep snapcast
snapcast-client-0.35.0   local    a8f4fa7c9325   5 minutes ago   122MB
snapcast-server-0.35.0   local    ecf9cf84d95b   6 minutes ago   152MB

4.3 编写 compose.yml

在 /opt/snapcast/compose.yml 写入以下编排(本文实测原样使用):

name: snapcast-0.35.0-stack

services:
  server:
    image: snapcast-server-0.35.0:local
    hostname: snapserver
    ports:
      - "8151:1704"
      - "8152:1705"
      - "8153:1780"
    volumes:
      - ./serverdata/snapserver.conf:/etc/snapserver.conf
      - ./serverdata:/serverdata
    networks: [snapnet]
    restart: unless-stopped

  client1:
    image: snapcast-client-0.35.0:local
    command: ["tcp://server:1704", "--soundcard", "null", "--latency", "0"]
    depends_on: [server]
    networks: [snapnet]
    restart: unless-stopped

  client2:
    image: snapcast-client-0.35.0:local
    command: ["tcp://server:1704", "--soundcard", "null", "--latency", "0"]
    depends_on: [server]
    networks: [snapnet]
    restart: unless-stopped

networks:
  snapnet:
    name: snapcast-0.35.0-net
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.61.0/24

编排要点逐项说明:

  • hostname: snapserver:容器网络内 DNS 名,客户端命令里的 tcp://server:1704 依赖 Compose 服务名解析,两套机制都能解析到 server;
  • 8151/8152/8153 → 1704/1705/1780:1704 音频流、1705 TCP 控制台、1780 HTTP 控制台;宿主侧端口按空闲段自定,改左侧即可;
  • ./serverdata/snapserver.conf:/etc/snapserver.conf:配置文件单文件挂载,改配置无需重建镜像;
  • ./serverdata:/serverdata:目录挂载,pipe 源的 snapfifo 与服务器持久化数据经此与宿主交换;
  • command: [... "--soundcard", "null" ...]:无声卡环境的关键参数,丢弃采样但完整走同步链路;
  • restart: unless-stopped:服务器与客户端进程崩溃自动拉起(与官方 systemd 单元 Restart=on-failure 行为对齐);
  • 固定 subnet 10.89.61.0/24:固定网段便于防火墙按源放行,非必需。

4.4 启动与验证

cd /opt/snapcast
docker compose up -d

实测回显(首次启动):

[+] Running 4/4
 ✔ Network snapcast-0.35.0-net          Created
 ✔ Container snapcast-0.35.0-server     Started
 ✔ Container snapcast-0.35.0-client1    Started
 ✔ Container snapcast-0.35.0-client2    Started

确认运行状态:

docker compose ps
NAME                      IMAGE                          COMMAND                  SERVICE   CREATED          STATUS          PORTS
snapcast-0.35.0-client1   snapcast-client-0.35.0:local   "/usr/bin/snapclient…"   client1   26 minutes ago   Up 26 minutes
snapcast-0.35.0-client2   snapcast-client-0.35.0:local   "/usr/bin/snapclient…"   client2   26 minutes ago   Up 26 minutes
snapcast-0.35.0-server    snapcast-server-0.35.0:local   "/usr/bin/snapserver…"   server    26 minutes ago   Up 21 minutes   0.0.0.0:8151->1704/tcp, [::]:8151->1704/tcp, 0.0.0.0:8152->1705/tcp, [::]:8152->1705/tcp, 0.0.0.0:8153->1780/tcp, [::]:8153->1780/tcp

查看服务器启动日志,确认三个端口监听与 pipe 源创建(输出节选,实测):

docker compose logs server | head -12
snapcast-0.35.0-server  | 2026-10-30 00-51-59.023 [Info] (Snapserver) Version 0.35.0, revision f1237347
snapcast-0.35.0-server  | 2026-10-30 00-51-59.023 [Info] (Snapserver) Adding source: pipe:///serverdata/snapfifo?name=SineTest&sampleformat=44100:16:2
snapcast-0.35.0-server  | 2026-10-30 00-51-59.025 [Info] (PipeStream) PipeStream mode: create
snapcast-0.35.0-server  | 2026-10-30 00-51-59.025 [Info] (ControlServer) Creating TCP control acceptor for address: ::, port: 1705
snapcast-0.35.0-server  | 2026-10-30 00-51-59.025 [Info] (ControlServer) Creating HTTP acceptor for address: ::, port: 1780
snapcast-0.35.0-server  | 2026-10-30 00-51-59.025 [Info] (StreamServer) Creating TCP stream acceptor for address: ::, port: 1704

通过 HTTP 控制台验证客户端接入(无声卡客户端以 null 设备完成同步链路验证):

curl -s http://127.0.0.1:8153/jsonrpc -H 'Content-Type: application/json' \
  -d '{"id":2,"jsonrpc":"2.0","method":"Server.GetStatus"}'

实测回显(节选,两客户端 connected: true,流 playing):

{
    "id": 2,
    "jsonrpc": "2.0",
    "result": {
        "server": {
            "groups": [
                {
                    "clients": [
                        {
                            "connected": true,
                            "id": "86:4c:86:eb:df:d7",
                            "snapclient": {
                                "protocolVersion": 2,
                                "version": "0.35.0"
                            }
                        },
                        {
                            "connected": true,
                            "id": "1e:66:50:a2:56:b7"
                        }
                    ],
                    "muted": false,
                    "stream_id": "SineTest"
                }
            ],
            "streams": [
                {
                    "id": "SineTest",
                    "status": "playing",
                    "uri": {
                        "raw": "pipe:///serverdata/snapfifo?chunk_ms=20&codec=pcm&name=SineTest&sampleformat=44100%3A16%3A2",
                        "scheme": "pipe"
                    }
                }
            ]
        }
    }
}

最后喂入音源完成端到端验证——往命名管道持续写 PCM(示例为 440Hz 正弦波生成器,也可以是 MPD 等上游的 pipe 输出):

# 宿主机执行;fifo 经目录挂载与容器内 /serverdata/snapfifo 为同一文件
python3 -c "
import math, struct, sys
fifo = sys.argv[1]; phase = 0.0; RATE = 44100
f = open(fifo, 'wb', buffering=0)
while True:
    frames = bytearray()
    for i in range(4410):
        v = int(20000 * math.sin(2 * math.pi * 440.0 * (phase + i) / RATE))
        frames += struct.pack('<hh', v, v)
    phase = (phase + 4410) % RATE
    f.write(bytes(frames))
" /opt/snapcast/serverdata/snapfifo

五、数据持久化说明

5.1 需要持久化的数据

Snapcast 服务器有两类状态需要区分:

数据位置内容不持久化的后果
客户端拓扑配置/var/lib/snapserver/server.json([server] datadir)客户端名字、音量、静音、分组、延迟服务器重启后全部回到默认
音频管道与本地文件pipe:// 源路径(本文 /serverdata/snapfifo)上游写入的实时 PCM管道本身无需持久化,重启后按 mode=create 自动重建

5.2 持久化实测结论

本文按 4.3 的 compose(未挂载 /var/lib/snapserver)实测一次完整的重建验证:

# 重启前给客户端命名
curl -s http://127.0.0.1:8153/jsonrpc -H 'Content-Type: application/json' \
  -d '{"id":1,"jsonrpc":"2.0","method":"Client.SetName","params":{"id":"66:5f:2e:b8:5e:7f","name":"TestPersist"}}'
{"id":1,"jsonrpc":"2.0","result":{"name":"TestPersist"}}

# 一键重建
docker compose down && docker compose up -d
 Container snapcast-0.35.0-server Removed
 Network snapcast-0.35.0-net Removing
 Network snapcast-0.35.0-net Removed
 Container snapcast-0.35.0-client2 Started
 Container snapcast-0.35.0-client1 Started

重启后查询状态(节选,实测):

streams: [('SineTest', 'playing')]
client: 86:4c:86:eb:df:d7 | connected: True | name: '' | vol: 100
client: 1e:66:50:a2:56:b7 | connected: True | name: '' | vol: 100

两条实测结论:

  1. 未挂载 datadir 时客户端配置不持久化:重启前命名的 TestPersist 重启后消失(name: ''),因为 snapserver 重启后没有从 server.json 恢复;
  2. 客户端身份随容器重建而变:snapclient 以本机 MAC 作为唯一 ID,容器重建生成新随机 MAC(旧 66:5f:2e:... 变为 86:4c:86:...),旧客户端记录残留为 connected: false(可用 Server.DeleteClient 清理)。生产中若需稳定身份,为每个 client 服务显式指定 --hostID(如 --hostID client1)。

5.3 生产持久化 compose 片段

生产环境给 server 增加两条挂载(client 不需要):

  server:
    volumes:
      - ./serverdata/snapserver.conf:/etc/snapserver.conf
      - ./serverdata:/serverdata
      - ./serverdata/lib:/var/lib/snapserver

💡 说明:./serverdata/lib 对应容器内 datadir 默认值 /var/lib/snapserver/(daemon 模式),server.json 会落在宿主机 serverdata/lib/ 下,重建容器后名字/音量/分组原样恢复。[server] datadir 也可显式指定其他路径,与挂载点保持一致即可。

六、环境变量表

snapserver/snapclient 官方 deb 无约定的环境变量配置面(配置走 /etc/snapserver.conf 与命令行),Compose 编排下常用注入点如下:

变量/注入点适用服务作用示例
command(数组)client1/client2传递 snapclient 命令行参数["tcp://server:1704", "--soundcard", "null"]
command(数组)server覆盖默认 ["-c", "/etc/snapserver.conf"],可追加任意 --server.* 选项["-c", "/etc/snapserver.conf", "--server.mdns_enabled=false"]
TZ全部时区(影响日志时间戳显示)Asia/Shanghai
http_proxy/https_proxy构建期apt 拉取依赖走代理构建时 --build-arg 或 Docker 全局代理

💡 说明:snapclient 的所有命令行选项见 snapclient --help(全文已沉淀于站内《Snapcast 0.35.0 常用 CLI 命令与工具详解》);snapserver 的 -hh 选项与 /etc/snapserver.conf 配置段的对应关系见站内《Snapcast 0.35.0 配置参数详解》——每个配置文件选项都有等价命令行形式 --<section>.<name>=<value>。

七、配置参数说明

本文编排中三个服务涉及的配置分三层:

  1. 服务器运行配置(serverdata/snapserver.conf,单文件挂载到 /etc/snapserver.conf):
[stream]
source = pipe:///serverdata/snapfifo?name=SineTest&sampleformat=44100:16:2
codec = pcm
chunk_ms = 20
buffer = 1000
send_to_muted = false

[tcp-streaming]
enabled = true
port = 1704

[tcp-control]
enabled = true
port = 1705

[http]
enabled = true
port = 1780

逐项说明:

  • source:音源 URI。pipe:///serverdata/snapfifo 指向挂载目录内的命名管道(mode 缺省即 create,服务器启动时自动创建);name=SineTest 是流在控制接口里的 ID;sampleformat=44100:16:2 固定采样格式(采样率:位深:声道);
  • codec = pcm:不编码原样透传,CPU 零开销;家庭内网建议保持官方默认 flac(codec 四选对比见站内《Snapcast 0.35.0 配置参数详解》5.2 节);
  • chunk_ms = 20:每次从源读取并编码的时间片(毫秒),默认 20;
  • buffer = 1000:端到端同步缓冲(毫秒),越大越抗网络抖动、操作时延越高;
  • send_to_muted = false:静音客户端不再推流(省带宽),改为 true 可实现「静音后取消静音即出声」的零延迟切换;
  • [tcp-streaming]/[tcp-control]/[http]:三个监听面开关与端口,与 compose 端口映射一一对应。
  1. 客户端运行参数(compose command):tcp://server:1704 服务器地址、--soundcard null 输出设备、--latency 0 声卡固有延迟补偿(毫秒)。

  2. 编排层参数(compose.yml):端口映射、网络、重启策略,见 4.3 的逐项说明。

八、配置文件挂载

Compose 配置挂载的完整对应关系(宿主机路径以 /opt/snapcast 为前缀):

宿主机路径容器内路径模式用途
./serverdata/snapserver.conf/etc/snapserver.conf单文件 ro 建议改为 rw服务器主配置(deb 默认读取路径)
./serverdata//serverdatarwpipe 源命名管道 + 自定义 datadir 等数据
./serverdata/lib//var/lib/snapserver(5.3 增配)rwserver.json 客户端拓扑持久化

修改配置后的生效方式:snapserver.conf 变更需重启 server 服务(docker compose restart server),客户端 command 变更需要重建(docker compose up -d --force-recreate client1);命名管道文件与配置文件均在宿主机,docker compose down/up 不会丢失。

九、等价 docker run

不经 Compose 时,以下三条命令与本文编排完全等价(先建网络,再逐个启动):

docker network create --driver bridge --subnet 10.89.61.0/24 snapcast-0.35.0-net

docker run -d --name snapcast-0.35.0-server \
  --hostname snapserver \
  --network snapcast-0.35.0-net \
  -p 8151:1704 -p 8152:1705 -p 8153:1780 \
  -v /opt/snapcast/serverdata/snapserver.conf:/etc/snapserver.conf \
  -v /opt/snapcast/serverdata:/serverdata \
  --restart unless-stopped \
  snapcast-server-0.35.0:local

docker run -d --name snapcast-0.35.0-client1 \
  --network snapcast-0.35.0-net \
  --restart unless-stopped \
  snapcast-client-0.35.0:local \
  tcp://server:1704 --soundcard null --latency 0

docker run -d --name snapcast-0.35.0-client2 \
  --network snapcast-0.35.0-net \
  --restart unless-stopped \
  snapcast-client-0.35.0:local \
  tcp://server:1704 --soundcard null --latency 0

💡 说明:client 依赖 server 的服务名解析,故三条命令需按序执行且位于同一网络;生产建议仍用 Compose 编排(depends_on 保证顺序、up -d 一键重建)。

十、常用运维命令

以下命令均在 /opt/snapcast(compose.yml 所在目录)执行,全部实测:

docker compose ps                     # 三节点状态与端口映射
docker compose logs -f server         # 跟踪服务器日志(Ctrl+C 退出)
docker compose logs --tail 20 client1 # 客户端最近 20 行日志
docker compose restart server         # 改配置后重启服务器
docker compose up -d --force-recreate client1   # 重建单个客户端
docker compose down                   # 停止并移除容器与网络(宿主机数据保留)
docker compose up -d                  # 重新拉起全栈

docker compose down 实测回显:

 Container snapcast-0.35.0-server Removed
 Container snapcast-0.35.0-client1 Removed
 Container snapcast-0.35.0-client2 Removed
 Network snapcast-0.35.0-net Removing
 Network snapcast-0.35.0-net Removed

远程控制验证(HTTP JSON-RPC,从宿主机发):

curl -s http://127.0.0.1:8153/jsonrpc -H 'Content-Type: application/json' \
  -d '{"id":3,"jsonrpc":"2.0","method":"Server.GetRPCVersion"}'
{"id":3,"jsonrpc":"2.0","result":{"major":23,"minor":0,"patch":0}}

十一、常见问题

Q1:客户端日志报 Failed to create player 或声卡相关错误? 无声卡/虚拟机环境未指定输出设备导致。把 compose 的 client command 保持 --soundcard null(本文实测方式),或用文件输出 --player file --soundcard /path/out.wav;有真实声卡的树莓派则无需该参数(默认 default 设备)。

Q2:服务器日志出现 [Error] (Avahi) Failed to create client: Daemon not running? 容器内没有 avahi 守护进程,mDNS 发布不可用(实测日志原文)。不影响 TCP 直连播放;要消除该报错,在 snapserver.conf 的 [server] 段加 mdns_enabled = false(等价命令行 --server.mdns_enabled=false)。裸机 deb 安装不受影响——systemd 单元自带 Wants=avahi-daemon.service。

Q3:docker compose up 报端口占用? 宿主机 8151/8152/8153 已被占用。ss -lntp | grep -E '8151|8152|8153' 找到占用方,或直接改 compose.yml 端口映射左侧。

Q4:重启后客户端名字/音量丢了? 未挂载 datadir(见第五章实测结论)。按 5.3 增加 ./serverdata/lib:/var/lib/snapserver 挂载。

Q5:流一直 idle,客户端日志反复 No chunks available? pipe 源没有数据写入。确认宿主机侧生成器/播放器仍在写 serverdata/snapfifo(管道写端断开时 snapserver 会持续等待;写端进程退出需重新拉起)。客户端该实测日志形态:

2026-10-30 00-51-59.287 [Info] (Stream) No chunks available
2026-10-30 00-51-59.287 [Info] (Alsa) Failed to get chunk

Q6:往 /tmp/snapfifo 写数据但服务器没反应? 注意路径一致性:本文 compose 的源 URI 是 pipe:///serverdata/snapfifo(挂载目录内),宿主机写入路径是 serverdata/snapfifo。若用官方默认 pipe:///tmp/snapfifo,则对应容器内 /tmp,宿主机路径不互通——二选一后保证「源 URI、容器内路径、宿主机写入路径」三者一致。

十二、参考链接

  1. Snapcast 官方仓库:https://github.com/snapcast/snapcast
  2. v0.35.0 Release(deb 资产来源):https://github.com/snapcast/snapcast/releases/tag/v0.35.0
  3. 官方安装文档(无 Docker 官方镜像的判断依据):https://raw.githubusercontent.com/snapcast/snapcast/develop/doc/install.md
  4. 站内相关篇目:《Snapcast 0.35.0 介绍与选型指南》、《Lyrion Music Server 9.1.1 Docker Compose 部署指南》