Audiobookshelf 部署:先算媒体空间,再保留账号与收听进度
固定 Audiobookshelf 版本,按音频与恢复副本核算空间,隔离管理员与收听账号,保存媒体、数据库和进度。
先用一本书确认用途
为了换设备继续听书,不必一开始就把几百 GB 音频搬到 VPS。先准备一部自己有权保存和播放的有声书,确认目录识别、普通账号访问和进度保存,再根据媒体大小、实际收听量与备份空间选机器。
Audiobookshelf 同时管理书库和用户收听状态。本文固定 2.37.2 版本,采用本地磁盘、只读有声书目录,不启用播客下载或在线匹配。主要核对媒体能否正确读取、谁能访问,以及换机以后能否连同进度一起恢复。
把媒体、传输和恢复副本一起算进费用
先统计已有音频文件总量。假设音频平均码率为 128 kbit/s,一小时约为 57.6 MB,30 小时约为 1.728 GB;收听 100 小时约传输 5.76 GB。这只是十进制单位的算术示例,不是对某本书或线路的测量,未包含封面、协议、重复下载和转码差异。实际预算以文件大小和真实传输为准。
系统盘除了原音频,还要容纳数据库、封面、缓存、日志和恢复材料。在同一机器生成一份完整冷备、再解出恢复副本,可先按约三份媒体预留空间,再核对实际归档大小和额外开销。示例只用于验证小库,大库应先安排备份存储和维护时间,不要等压缩中途磁盘满了才处理。
机房选择看主要收听设备的访问条件、出站带宽和流量计费方式。上传媒体、播放、离线下载、异地备份各自消耗通信量,报价时核对哪些方向计费,以及用完额度后的收费或限速条件。示例给应用设置 512 MiB 上限,不是转码并发或最低内存保证;扫描与转码还会消耗 CPU。
已有 NAS 或服务器能够满足访问和备份条件,可以继续使用。确实需要 VPS 时,再比较雨云 RCS 的地区、磁盘、月付与续费,以及数据传输和备份的总费用;听书服务不需要住宅 IP。
注册推荐码:KuZhuJi
应用数据与音频目录各有用途
Docker 镜像用 /config 保存 SQLite 数据库和迁移数据,用 /metadata 保存封面、元数据、缓存、日志及应用内备份。音频另放 /audiobooks。/config 必须使用应用所在主机的本地磁盘,不把它放在通过网络访问的共享目录上。
应用内备份不包含音频源文件。只复制 /config,会缺封面与其他元数据;只下载应用内备份,也不能在一台空机器上直接播放原书。因此本例分别保存三个互不嵌套的目录,并将媒体目录只读挂载给应用。
在已有 Docker Engine 与 Compose 插件的 Linux 服务器上,以有 Docker 权限的 root shell 建立新实例目录。已有同名部署时先核对配置与数据,不覆盖原库。
set -eu
umask 077
mkdir -p /srv/audiobookshelf/config
mkdir -p /srv/audiobookshelf/metadata
mkdir -p /srv/audiobookshelf/audiobooks
chmod 700 /srv/audiobookshelf
cd /srv/audiobookshelf
保存 compose.yaml,版本和多架构索引摘要同时固定。宿主机只绑定回环端口 13378,容器端口仍为 80。
services:
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:2.37.2@sha256:b897c33f05972fb1bf484f7487bc878fa91ec8691335c1db6e2929c3a7658efa
restart: unless-stopped
ports:
- "127.0.0.1:13378:80"
environment:
TZ: Asia/Shanghai
volumes:
- ./config:/config
- ./metadata:/metadata
- ./audiobooks:/audiobooks:ro
mem_limit: 512m
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
这里使用官方默认运行用户。若另行指定 user,需要按实际 UID/GID 设置目录权限,不能照搬其他媒体镜像的 PUID、PGID 环境变量。只读媒体目录适合扫描和播放既有文件,不用于网页上传、删除原音频或将元数据写回书籍目录。
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=80 audiobookshelf
curl -I http://127.0.0.1:13378/
首次管理员在私人入口创建
在自己的电脑另开终端,替换 SSH 用户与服务器地址:
ssh -N -L 13379:127.0.0.1:13378 user@your-server
打开 http://127.0.0.1:13379,在首次设置界面创建管理员并设置独立密码。没有一组可供长期沿用的默认管理员密码;完成后退出,再登录验证。日常收听另建普通账号,家庭成员各用自己的账号,避免互相覆盖收听位置。
新建普通账号时显式选择允许访问的书库,关闭不需要的下载、上传与修改权限。不能只因为账号叫“普通用户”,就认为它默认只能看少数书。用该账号检查授权书库可见、未授权书库不可读取,再安排外部访问。不创建公开 RSS 或分享入口来绕开账号检查。
宿主机已有 Caddy 时,可加入下面的命名片段,再在自己的真实域名站点块中添加 import audiobookshelf_backend。DNS、80/443 与证书申请条件需满足,验证配置后再重载,保留已有站点。
(audiobookshelf_backend) {
reverse_proxy 127.0.0.1:13378
}
Caddy 支持应用所需的 WebSocket 代理。若代理运行在另一个容器,127.0.0.1 指向代理自身,需要按已有网络连接应用。正式 HTTPS 入口除了登录,还应验证媒体请求、拖动播放位置和进度更新;首页返回成功不能代替这些检查。
先让一本书的章节与进度对得上
将有权使用的音频放进一个清楚的目录,例如 作者名/书名/01-开篇.mp3。同一本书的多段音频放在同一书籍目录中,编号顺序保持稳定,避免不同书混在一层。实际扫描结果还受音频标签和库的元数据优先级影响,不能只靠文件名判断。
在应用中创建有声书库,选择容器内的 /audiobooks,按需关闭文件监视并手动扫描。不要填写宿主机的 /srv/audiobookshelf/audiobooks。扫描完成后核对作者、书名、文件顺序、章节和总时长;本地扫描与向外部服务匹配资料是不同操作,本例不执行在线 Match。
保持“封面存入书籍目录”和“元数据写入书籍目录”等选项关闭,允许应用将数据保存在 /metadata。只读目录出现写入错误时先检查这些设置,不急着把整库改成可写。添加新书在宿主机完成,保留原始副本后再扫描。
用普通账号播放一段并暂停,从另一个浏览器或设备登录同一账号,核对当前位置;在第二端继续一小段,再回第一端确认。另一个普通账号应有自己的独立进度。看到两端书目相同,只证明它们能访问同一书库,不证明收听进度已同步。
移动客户端的后台播放、锁屏、耳机控制与离线下载要分别验证。本文没有据此给某款手机或客户端作兼容保证。先用短音频测试,不在首次遇到卡顿时就批量重编码整个库;记录客户端、文件格式、是否转码,再查 CPU、传输和代理连接。
冷备小库时,连同原媒体一起保存
先暂停收听、扫描和宿主机的媒体整理,安排一个维护窗口。即使应用以只读方式挂载媒体,宿主机上的其他工具仍可能修改文件;备份期间不要移动、改名或替换这些音频。下面停止应用后归档配置、元数据与媒体,只用于本例小库的完整恢复演练。
set -eu
cd /srv/audiobookshelf
umask 077
[ -e .env ] || : > .env
chmod 600 .env
mkdir -p /srv/audiobookshelf-backups
snapshot=$(mktemp -d /srv/audiobookshelf-backups/cold-XXXXXX)
cid=$(docker compose ps -q audiobookshelf)
test -n "$cid"
trap 'docker compose start audiobookshelf >/dev/null' EXIT
docker compose stop -t 60 audiobookshelf
state=$(docker inspect --format '{{.State.Status}} {{.State.OOMKilled}} {{.State.ExitCode}}' "$cid")
case "$state" in
"exited false 0"|"exited false 143") ;;
*) printf '未确认正常停止,拒绝归档:%s\n' "$state" >&2; exit 1 ;;
esac
tar --numeric-owner -czpf "$snapshot/instance.tar.gz.partial" \
-C /srv/audiobookshelf compose.yaml .env config metadata audiobooks
tar -tzf "$snapshot/instance.tar.gz.partial" >/dev/null
mv "$snapshot/instance.tar.gz.partial" "$snapshot/instance.tar.gz"
(cd "$snapshot" && sha256sum instance.tar.gz > SHA256SUMS)
docker compose start audiobookshelf
trap - EXIT
printf '备份目录:%s\n' "$snapshot"
脚本失败会尝试启动原服务,半成品不当作完成备份。媒体较大时,归档期间的停机和临时磁盘成本会明显增加,应先安排适合自己规模的媒体备份流程,并记录它与应用状态的对应关系。本地同盘副本不能应对整台主机丢失;完成包应加密保存到另一台设备。
在有额外空间与内存的测试主机,用相同版本从新空目录恢复。把 snapshot 换成实际目录,只解压自己创建的可信归档:
set -eu
snapshot=/srv/audiobookshelf-backups/cold-实际后缀
(cd "$snapshot" && sha256sum -c SHA256SUMS)
restore=$(mktemp -d /srv/audiobookshelf-restore.XXXXXX)
tar --numeric-owner -xzpf "$snapshot/instance.tar.gz" -C "$restore"
cd "$restore"
在这个新目录保存 compose.restore.yaml。容器内路径保持与原实例一致;副本不发布端口并禁止联网,避免误连正式客户端或访问元数据服务。
services:
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:2.37.2@sha256:b897c33f05972fb1bf484f7487bc878fa91ec8691335c1db6e2929c3a7658efa
network_mode: none
restart: "no"
environment:
TZ: Asia/Shanghai
volumes:
- ./config:/config
- ./metadata:/metadata
- ./audiobooks:/audiobooks:ro
mem_limit: 512m
保持在该目录启动副本,等服务就绪后,在容器内部检查首页:
project="abs-recovery-$(basename "$restore" | tr 'A-Z.' 'a-z-')"
docker compose -p "$project" -f compose.restore.yaml up -d
docker compose -p "$project" -f compose.restore.yaml logs --tail=80 audiobookshelf
docker compose -p "$project" -f compose.restore.yaml exec -T audiobookshelf node -e \
'fetch("http://127.0.0.1/",{signal:AbortSignal.timeout(10000)}).then(r=>{if(!r.ok)process.exit(1);console.log(r.status)}).catch(()=>process.exit(1))'
这个无网络副本不能直接从电脑打开。完整恢复还要通过容器内的应用 API 或受控的隔离客户端,核对原账号、授权书库、书名、媒体文件字节与原收听位置,并验证更新位置后重启仍保留。只有首页检查成功时,应记录为服务可启动、数据恢复待验。
验完在恢复目录执行 docker compose -p "$project" -f compose.restore.yaml stop,保留副本。若还需要浏览器或手机实际收听验收,另安排有访问限制、无外部集成出口的测试入口;不要把 API 返回的进度数字当成听感或锁屏验收。
更新时先保留可读取的旧数据
更新前阅读目标版本发布说明,先完成冷备与原版本恢复。保留旧镜像完整标识、三个数据目录及挂载关系,再修改正式 Compose 的版本和摘要,拉取并重建。检查登录、书目、媒体读取、权限和进度,而不只看容器处于 Running。
新版本迁移数据库后,回退需要升级前的数据与旧镜像配套恢复。以上配置仅用于有声书小库;以后启用播客下载,要另设可写目录、外网与保留规则,并把下载的媒体纳入备份。启用大量转码或扩大并发也应重新核算资源,不能沿用本例的 512 MiB 当作性能承诺。
