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.
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.
listen.example.com at your serverdocker-compose.ymlconfig, metadata, audiobooks, and podcastsTotal time: about 15 minutes.
80 and 443 open to the internet (Let's Encrypt needs them)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.
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.
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
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.
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 progressmetadata/ holds cached cover art, downloaded podcast episodes, and backupsaudiobooks/ is your audiobook librarypodcasts/ is where subscribed shows download toIf 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
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.
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.13378 stays on the booknet Docker network. Caddy reaches it by container name.:ro read-only mounts here. Audiobookshelf writes downloaded podcast episodes into /podcasts and may rename or tag files, so it needs write access.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.
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.
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:
Audiobooks, set the media type to Books, and add the folder /audiobooks.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.
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:
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:
Downloaded episodes land in /podcasts and play through the same web and mobile apps as your books, with the same progress sync.
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.
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.
Audiobookshelf has a built-in backup feature that snapshots your database and settings - not the media, just the irreplaceable state. Go to Settings -> Backups:
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
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.
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.
.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.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.