アプローチの比較:専用オーディオブックサーバー vs 汎用メディアスタック
総再生時間30時間の長編ファンタジー大作を12時間聴き進めたところで、車に乗り込んでオーディオプレーヤーを開いたら、再生位置が0分0秒にリセットされていた——そんな経験はないでしょうか。PlexやJellyfinに無理やりオーディオブックライブラリを組み込もうとしたことがある方なら、一度はこの頭の痛い問題に直面したはずです。
汎用メディアサーバーは、オーディオブックを通常の音楽トラックと同じように扱います。デスクトップのブラウザから地下鉄内のスマートフォンへと切り替えると、再生タイムスタンプが消失したり、チャプター分割が認識されなかったり、複数ディスクのファイル順がシャッフルされたりといった問題が発生します。HomeLabユーザーが音声コンテンツを運用する場合、通常は次の3つの構成のいずれかを採用します。
- 音楽サーバーの転用(Navidrome / Jellyfin): FLACやMP3の音楽アルバムには最適です。しかし、ネイティブの
.m4bチャプター解析、ピッチ補正付きの可変速再生(1.25倍速や1.75倍速など)、詳細なブックマーク機能が不足しています。また、不安定な4G回線経由でのレジューム再生もしばしば失敗します。 - Plex+サードパーティ製コンパニオンアプリ構成(Plex + Prologue/Chronicle): iOSではPrologueを使うことで快適に動作します。しかし、壊れやすいサードパーティ製スクレイパーや厳格なファイル命名規則に依存します。さらに、Androidユーザーには同等の専用クライアントが存在せず、選択肢から取り残されてしまいます。
- 専用音声コンテンツサーバー(Audiobookshelf): オーディオブックとポッドキャストに特化してゼロから構築されています。ローカルのSQLiteキャッシュとリアルタイムWebSocket同期を組み合わせ、Audible、Google Books、OpenLibraryから直接メタデータを取得できます。AndroidとiOSの両方向けに完成度の高いアプリも提供されています。
DockerでAudiobookshelfを運用するメリット・デメリット
Audiobookshelfは非常に軽量で、アイドル時のRAM消費量は約120MB〜200MB程度です。ただし、イメージをプルする前に把握しておくべき運用上の注意点もあります。
メリット
- 秒単位の高精度な進捗同期: 常時接続のWebSocketにより、Webプレーヤー、Android、iOS間で秒単位の再生進捗を正確に同期します。
- チャプターの自動抽出: 外部のサイドカーファイルを必要とせず、
.m4b、.mp3、.flacファイルから埋め込みキューポイントやチャプタータイトルを直接読み取ります。 - メタデータストレージの分離: 著者の経歴情報、シリーズ順序、高解像度カバー画像などはローカルキャッシュに保存されるため、大容量の音声ライブラリ本体には一切変更が加わりません。
- ポッドキャストダウンローダーの内蔵: 統合されたRSSパーサーがスケジュールに従って新着エピソードを自動ダウンロードするため、TubeArchivistやPoduramaなどの別ツールを導入する必要がありません。
デメリット
- パーミッションの厳格さ: コンテナプロセスはユーザーID(PUID)とグループID(PGID)の一致を厳密に要求します。ホスト側の権限設定が食い違うと、SQLiteが
SQLITE_BUSYエラーでロックされたり、スケジュールされたライブラリスキャンが警告なくサイレントに失敗したりします。 - リバースプロキシのWebSocket要件: リアルタイム同期は双方向通信に依存しているため、Nginx、Traefik、Cloudflareなどのリバースプロキシで適切なアップグレードヘッダーを明示的に転送する必要があります。ヘッダーの設定漏れがあると、モバイルセッションが不安定なポーリングループにフォールバックしてしまいます。
推奨アーキテクチャ構成
データベースファイルをネットワーク共有(NASなど)上に直接配置するのは絶対に避けてください。ファイルロックの競合やスキャン速度の低下を防ぐため、設定データとメディアファイルは分離して配置します。
設定ファイルとSQLiteデータベースは高速なローカルSSD(/opt/audiobookshelfなど)に保存し、大容量の音声ファイル群はローカルマウントまたはNFS経由のHDDアレイやZFSプールに配置します。
/opt/audiobookshelf/ # ローカルNVMe/SATA SSD
├── config/ # アプリ設定、ユーザー情報、セッショントークン
└── metadata/ # SQLiteデータベース、キャッシュされたカバー画像、ストリームキャッシュ
/mnt/storage/media/ # 大容量ストレージ(ZFS、Unraidアレイ、mergerfsなど)
├── audiobooks/ # 著者/シリーズ/作品名/作品.m4b
└── podcasts/ # 番組名/エピソード - タイトル.mp3
この構成によりトラブルを未然に防ぐことができます。HDDが20分間のアイドル後にスピンダウン(スリープ)しても、あるいはZFSプールで月次の高負荷なスクラブが実行されていても、Webインターフェースは高速なレスポンスを維持します。すべてのI/Oクエリがローカルフラッシュメモリ上で実行されるため、SQLiteデータベースの軽快な動作が保たれます。
導入ガイド
ホスト側ディレクトリの準備、Docker Composeファイルの作成、WebSocket対応リバースプロキシのルーティング設定、そしてデバイス間の再生同期テストの手順を解説します。
ステップ1:ホストディレクトリの作成とUID/GID権限の設定
Dockerホスト上で以下のコマンドを実行し、必要なディレクトリを作成して所有権を設定します。ルートレス運用や非rootシステムユーザーへの権限一致により、永続マウント時のパーミッションデッドロックを防ぎます。
# 現在のユーザーIDとグループIDを確認
id
# 出力例: uid=1000(ubuntu) gid=1000(ubuntu)
# ランタイム、設定、メディア用ディレクトリを作成
sudo mkdir -p /opt/audiobookshelf/config /opt/audiobookshelf/metadata
sudo mkdir -p /mnt/storage/media/audiobooks /mnt/storage/media/podcasts
# 非特権ユーザー(UID/GID 1000)に所有権を割り当て
sudo chown -R 1000:1000 /opt/audiobookshelf
sudo chmod -R 755 /opt/audiobookshelf
ステップ2:Docker Composeマニフェストの作成
スタック用の専用ディレクトリを作成し、Composeファイルを新規作成して開きます。
mkdir -p ~/stacks/audiobookshelf
cd ~/stacks/audiobookshelf
nano docker-compose.yml
以下の本番用設定を貼り付けます。
services:
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:latest
container_name: audiobookshelf
restart: unless-stopped
environment:
- AUDIOBOOKSHELF_UID=1000
- AUDIOBOOKSHELF_GID=1000
- TZ=Etc/UTC
- PORT=13378
ports:
- "13378:13378"
volumes:
- /opt/audiobookshelf/config:/config
- /opt/audiobookshelf/metadata:/metadata
- /mnt/storage/media/audiobooks:/audiobooks:ro
- /mnt/storage/media/podcasts:/podcasts
healthcheck:
test: ["CMD", "nc", "-z", "127.0.0.1", "13378"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
セキュリティのヒント:/mnt/storage/media/audiobooksを:ro(読み取り専用)フラグ付きでマウントすることで、コンテナによる原本ファイルの誤書き換えや削除を防ぐことができます。Audiobookshelfから直接埋め込みID3タグを編集したい場合のみ、:roを外してください。
ステップ3:デプロイとランタイムログの確認
デタッチモードでコンテナを起動し、初期起動シークエンスのログを確認します。
docker compose up -d
docker compose logs -f audiobookshelf
データベースの初期化とソケットリスナーのバインドが正常に完了したことを確認します。
[INFO] DB初期化: SQLite接続完了
[INFO] サーバーがポート13378でリッスン中
[INFO] WebSocketサーバーが初期化されました
もしEACCESやSQLITE_BUSYの警告が表示された場合は、ls -la /opt/audiobookshelfを実行し、/opt/audiobookshelf/configと/opt/audiobookshelf/metadataの所有者がUID 1000になっているかを再確認してください。
ステップ4:WebSocketヘッダーを含むリバースプロキシの設定
Nginxを経由してAudiobookshelfをHTTPSで公開する場合、明示的なWebSocket処理の設定が不可欠です。Upgradeヘッダーがないと、モバイルクライアントとの接続が切断され、再生タイムスタンプの送信に失敗します。
server {
listen 443 ssl http2;
server_name audio.homelab.local;
# SSL証明書のパス
ssl_certificate /etc/letsencrypt/live/audio.homelab.local/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/audio.homelab.local/privkey.pem;
location / {
proxy_pass http://127.0.0.1:13378;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 必須のWebSocketアップグレードヘッダー
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
ステップ5:ライブラリのセットアップとクライアント同期の検証
- ブラウザを開き、
http://<HOST-IP>:13378にアクセスします。 - プライマリの管理者アカウント(root)を作成し、強固なパスフレーズを設定します。
- Add Libraryを選択し、Audiobooksを指定して、パスに
/audiobooksを設定します。スキャン処理を高速化するため、ディレクトリ構成パターンとしてAuthor/Title(著者/タイトル)を選択します。 - Google Play、F-Droid、またはApple TestFlight / App StoreからAudiobookshelfクライアントアプリをダウンロードします。
- モバイル端末からサーバーインスタンスにログインし、任意の作品を再生してタイムスタンプ
00:18:45までシークします。 - デスクトップのブラウザで同じ作品を開きます。1〜2秒以内に、Webプレーヤー側の再生位置がモバイル側の再生位置と自動的に同期されることを確認します。
ステップ6:自動バックアップの設定
リスニングの統計情報やブックマークを保護しましょう。Settings > Backupsに移動し、毎日午前3:00に実行される自動バックアップを有効にします。Audiobookshelfは、SQLiteの状態、ユーザーセッション、カスタムブックマークをまとめた軽量な.tarアーカイブを/opt/audiobookshelf/metadata/backups配下に生成します。完全な復旧性を確保するために、このフォルダーをResticやRcloneを使ってオフサイト(外部ストレージ)に同期しておくことを推奨します。

