All articles
TutorialsJun 05, 2026 · 22 min read

Self-Host Audiobookshelf on a VPS for Audiobooks and Podcasts

Self-Host Audiobookshelf on a VPS for Audiobooks and Podcasts

If you own a pile of audiobooks - ripped from CDs, bought DRM-free from Libro.fm, or downloaded from your library - they probably live in a folder you forget about until you want one on a long drive. Audiobookshelf turns that folder into a proper server: a clean web player, per-user listening progress that syncs across devices, bookmarks, sleep timers, and native apps for Android and iOS. It also doubles as a podcast manager that auto-downloads new episodes, so you can quietly leave Spotify and Apple Podcasts behind.

This guide walks through a production-ready Audiobookshelf install on a VPS using Docker, with Caddy in front for automatic HTTPS and the WebSocket forwarding the app needs to sync progress in real time. It covers the parts that trip up first-timers: file ownership, library folder structure, and getting playback to resume on the right device.

Audiobookshelf tracks your exact listening position per user. That data lives in the config volume. Back it up. Losing it means re-finding your spot in a 40-hour book, which is exactly the misery you self-hosted to avoid.

TL;DR

  • Install Docker and Docker Compose on a fresh VPS
  • Point a subdomain like listen.example.com at your server
  • Run Audiobookshelf and Caddy from a single docker-compose.yml
  • Mount four volumes: config, metadata, audiobooks, and podcasts
  • Caddy handles HTTPS and the WebSocket upgrade automatically
  • Create the admin account on first run, then add libraries
  • Install the mobile app and point it at your domain

Total time: about 15 minutes.

What You Need

  • A VPS with 1 GB RAM (2 GB is comfortable; the app itself is light)
  • Disk space for your collection - audiobooks are smaller than video, but a large library plus downloaded podcasts adds up, so a storage VPS is worth it past a few hundred books
  • A domain you can add DNS records to
  • Ports 80 and 443 open to the internet (Let's Encrypt needs them)
  • Root or sudo access

Audiobookshelf barely touches the CPU. Streaming an audio file is cheap, there is no transcoding worth worrying about, and the database is tiny. This runs happily on the smallest plans. Disk for the media is the only real sizing question.

Step 1: Point a Subdomain at Your VPS

In your DNS provider, create an A record:

listen.example.com → YOUR_VPS_IPV4

Add an AAAA record too if your server has IPv6. Confirm it resolves:

dig +short listen.example.com

The output should be your VPS IP. Caddy cannot issue a certificate until DNS points at the box.

Step 2: Install Docker and Docker Compose

On a fresh Ubuntu 22.04 or 24.04 server:

sudo apt update sudo apt install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \ https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Check it works:

docker --version docker compose version

Step 3: Open the Firewall

sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable

Caddy needs 80 for the ACME HTTP challenge and 443 for HTTPS. Do not expose Audiobookshelf's internal port 13378 to the internet - Caddy is the only thing that should answer publicly.

Step 4: Create the Project Directory

sudo mkdir -p /opt/audiobookshelf cd /opt/audiobookshelf sudo mkdir -p config metadata audiobooks podcasts caddy-data caddy-config

The four data directories map to how Audiobookshelf splits state:

  • config/ holds the database, users, settings, and listening progress
  • metadata/ holds cached cover art, downloaded podcast episodes, and backups
  • audiobooks/ is your audiobook library
  • podcasts/ is where subscribed shows download to

If your VPS has a large data disk mounted elsewhere, point the media folders at it:

sudo mkdir -p /mnt/storage/audiobooks /mnt/storage/podcasts sudo ln -s /mnt/storage/audiobooks /opt/audiobookshelf/audiobooks sudo ln -s /mnt/storage/podcasts /opt/audiobookshelf/podcasts

Step 5: Lay Out Your Audiobook Files

Audiobookshelf scans folders and matches them to metadata providers, so the directory structure matters. The recommended layout is one folder per book, grouped by author:

audiobooks/ Andy Weir/ Project Hail Mary/ Project Hail Mary.m4b Brandon Sanderson/ Stormlight Archive/ 1 - The Way of Kings/ The Way of Kings.m4b 2 - Words of Radiance/ Words of Radiance.m4b

A single .m4b file per book is the cleanest format because it carries chapters and cover art internally. If a book is split into dozens of .mp3 files, keep them in one folder and Audiobookshelf will stitch them into a single playable item in scan order. Series get a sub-folder under the author with a numeric prefix so they sort correctly.

You do not have to get this perfect up front. The scanner is forgiving, and you can edit any book's metadata, cover, and series position from the web UI later.

Step 6: Write the Compose File

Create /opt/audiobookshelf/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: "Europe/Berlin" volumes: - ./config:/config - ./metadata:/metadata - ./audiobooks:/audiobooks - ./podcasts:/podcasts networks: - booknet caddy: image: caddy:2 container_name: audiobookshelf-caddy restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - ./caddy-data:/data - ./caddy-config:/config networks: - booknet networks: booknet:

Notes:

  • AUDIOBOOKSHELF_UID and AUDIOBOOKSHELF_GID set who the process runs as inside the container. Match these to the user that owns your media on the host. Run id -u and id -g to check - on Ubuntu the first non-root user is usually 1000:1000.
  • The internal port 13378 stays on the booknet Docker network. Caddy reaches it by container name.
  • Unlike a video server, there are no :ro read-only mounts here. Audiobookshelf writes downloaded podcast episodes into /podcasts and may rename or tag files, so it needs write access.

Step 7: Write the Caddyfile

Create /opt/audiobookshelf/Caddyfile:

listen.example.com { encode zstd gzip reverse_proxy audiobookshelf:13378 { header_up X-Real-IP {remote_host} header_up X-Forwarded-For {remote_host} header_up X-Forwarded-Proto {scheme} header_up Host {host} } }

Audiobookshelf uses Socket.IO (WebSockets) to push playback progress and library updates between your devices in real time. Caddy v2 upgrades WebSocket connections automatically for reverse_proxy, so there is nothing extra to configure - this is one of the nicest reasons to use Caddy over a hand-written Nginx config, where you have to add the Upgrade and Connection headers by hand or sync silently breaks.

The X-Forwarded-Proto header is the one that matters most. Without it, the app builds http:// URLs internally and the mobile apps refuse to connect over mixed-content rules.

Step 8: Start the Stack

cd /opt/audiobookshelf sudo docker compose up -d sudo docker compose logs -f

Wait until the Caddy logs show the Let's Encrypt certificate was issued, then open https://listen.example.com. You should land on the Audiobookshelf setup screen.

Step 9: Create the Admin Account and Libraries

The first screen asks you to create a root user. This is the all-powerful admin account - pick a strong password, because there is no recovery flow other than editing the database by hand.

Once you are in:

  1. Go to Settings -> Libraries -> Add Library.
  2. Name it Audiobooks, set the media type to Books, and add the folder /audiobooks.
  3. Add a second library named Podcasts, media type Podcasts, folder /podcasts.

Save, and Audiobookshelf kicks off a scan. It reads tags and folder names, then matches each book against metadata providers (Audible, Google Books, and others) to pull covers, descriptions, narrators, and series info. A few hundred books take a couple of minutes.

If a match is wrong, open the book, click the edit (pencil) icon, and use Match to search a provider manually. Covers and chapter markers can be fixed from the same screen.

Step 10: Add Users Without Giving Away the Keys

Audiobookshelf has real multi-user support with per-user progress, which is the whole point for a household. Add accounts under Settings -> Users -> Add User:

  • Set the account type to User, not Admin, for everyone who is not you. Regular users can listen and track progress but cannot change server settings or delete libraries.
  • Use Library access to scope what each person sees. Kids can get the children's library and nothing else.
  • There is no public signup form - accounts are created by an admin only. That is the correct default for a public-facing server, so leave it that way.

Step 11: Set Up Podcasts

This is the feature that quietly replaces a podcast app. In the Podcasts library, click Add Podcast, search by name or paste an RSS feed URL, and pick how many past episodes to download. Then:

  • Toggle Auto Download Episodes so new episodes pull automatically.
  • Set a check schedule (a cron expression - the default of every few hours is fine).
  • Set an episode limit so a daily news show does not slowly eat your disk. Keeping the latest 10 episodes is a sane default.

Downloaded episodes land in /podcasts and play through the same web and mobile apps as your books, with the same progress sync.

Step 12: Install the Mobile Apps

The official apps are where most listening actually happens:

On first launch, choose Custom Server, enter https://listen.example.com, and log in with your account. The apps support offline downloads, so you can pull a book over Wi-Fi before a flight, plus CarPlay and Android Auto, sleep timers, and variable playback speed. Progress syncs back to the server the moment you reconnect.

Do not skip HTTPS and try to connect over plain http:// or a raw IP. The mobile apps and modern browsers block insecure connections for media, and CarPlay flatly refuses them. The Caddy setup above gives you a valid certificate for free - use it.

Step 13: Turn On Backups

Audiobookshelf has a built-in backup feature that snapshots your database and settings - not the media, just the irreplaceable state. Go to Settings -> Backups:

  • Enable scheduled backups (daily is fine).
  • Set a retention count so old backups get pruned.

Backups write to the metadata/backups folder. That gets you point-in-time restore inside the app, but it is still on the same server. For real safety, push that folder off-box. Pair this with restic to object storage and you have an offsite copy of every account and listening position:

restic -r s3:s3.amazonaws.com/my-backup-bucket backup \ /opt/audiobookshelf/config \ /opt/audiobookshelf/metadata/backups

Optional: Keep It Private with a VPN

If the only people using your server live in your house, you do not need it on the public internet at all. Putting Audiobookshelf behind Tailscale removes every bot scan and login-bruteforce attempt in one move. Skip the firewall openings for 80 and 443, reach listen.example.com over your tailnet, and the mobile apps connect exactly the same way. WireGuard works just as well if you already run a WireGuard VPN on your VPS.

Troubleshooting

Caddy returns a 502 right after docker compose up. Audiobookshelf takes a few seconds to initialize its database on first boot, and Caddy can hit it first. Wait 30 seconds and reload. If it sticks, check docker compose logs audiobookshelf.

The library is empty after a scan. Almost always a path or permission issue. Confirm your files live under the folder you added (/audiobooks inside the container) and that the container user can read them: docker exec -it audiobookshelf ls -la /audiobooks. If you see permission denied, fix ownership on the host with sudo chown -R 1000:1000 /opt/audiobookshelf/audiobooks.

Progress does not sync between phone and browser. This is the WebSocket path. Confirm you are on HTTPS, that no extra proxy (Cloudflare, another reverse proxy) is buffering the connection, and that X-Forwarded-Proto is set. If you front Caddy with Cloudflare, enable WebSockets in the dashboard.

A book shows the wrong cover or is split into the wrong number of items. Open the item, hit the edit pencil, and use Match to re-pull metadata, or Re-Scan the single item. Multi-file books that scan in the wrong order usually have inconsistent track tags - a quick pass with a tag editor fixes the sort.

Podcast episodes are not downloading. Check that Auto Download is on for that show and that the check schedule has run. The activity log under Settings -> Logs shows feed-fetch errors, which are usually a dead or moved RSS URL.

Going Further

  • Convert messy books to .m4b. A single chaptered .m4b per book is the gold standard. Tools like m4b-tool merge a pile of .mp3 files into one tidy file with chapters and embedded art.
  • Add an OPDS-capable reader. Audiobookshelf serves ebooks too and exposes an OPDS feed, so you can browse your library from third-party reading apps.
  • Run it next to your video server. If you already self-host Jellyfin for video, Audiobookshelf rounds out your media stack on the same box behind the same Caddy instance - just add a second site block.
  • Automate backups offsite. Wire the config and metadata/backups folders into a nightly restic job so a dead disk never costs you your place in a book.

That's it. Self-hosted Audiobookshelf gives you a polished listening experience across every device, podcast subscriptions that download themselves, and listening progress that follows you - all on a server you own, with no subscription and no ads.


Need a VPS for your audiobook and podcast library? Our Linux plans ship with NVMe storage, generous bandwidth, and a storage tier for big collections. See the options.