Self-Host Your Audiobook & Podcast Server: Audiobookshelf from Zero to Synced

Audible locks your purchases behind DRM. Spotify wraps podcasts in a walled garden. And Plex — while great for video — treats audiobooks as an afterthought. If you have a collection of MP3s, M4Bs, and OPDS-era rips sitting on a NAS, you deserve something that actually respects your files.

That’s where Audiobookshelf comes in. It’s a self-hosted server built specifically for audiobooks and podcasts — not bolted onto a general-purpose media center. It tracks your playback position per-user per-device, downloads podcast episodes automatically, serves an OPDS catalog, and has genuinely good mobile apps on both iOS and Android.

This guide covers the full journey: server deployment with Docker Compose, reverse proxy config, library setup, podcast management, and getting your phone to stay in sync. By the end you’ll have a production-grade setup, not a "works on localhost" toy.


Why Audiobookshelf and Not Something Else

Before spending time on this, it’s worth knowing what you’re getting.

Booksonic is the old-guard option. It works, but development has slowed to nearly nothing. The UI feels like it was designed when Blackberry was relevant.

Plex can handle audiobooks via the Plex Pass tier, but the metadata matching is unreliable for audiobooks, and podcasts are treated as a second-class citizen. You’re also feeding playback telemetry to a company that has repeatedly shifted pricing.

Audiobookshelf was written from scratch for this exact use case. The metadata scanner actually understands chapter files. The podcast manager handles feed parsing, automatic downloads, and retention policies. The mobile apps use the same codebase as the server API, so syncing is reliable — not a side feature.

It’s actively maintained, open source (GPL-3.0), and the Discord community is unusually responsive for a project of this size.


What You Need Before Starting

  • A Linux server or VPS (anything with Docker and 1+ GB RAM is fine)
  • Docker and Docker Compose v2 installed
  • A domain name pointing to your server (for HTTPS — highly recommended if you’ll use mobile apps outside your home network)
  • Your audiobook/podcast files already on the server, or a plan to get them there

The server itself is lightweight. A Raspberry Pi 4 handles it fine for personal use. CPU usage is nearly zero at rest; it spikes briefly during library scans or when transcoding is needed (and transcoding is only needed for formats the client can’t handle natively).


Docker Compose Deployment

Create a directory for the project and put your compose file there:

mkdir -p /opt/audiobookshelf && cd /opt/audiobookshelf

Here’s a production-ready docker-compose.yml:

services:
  audiobookshelf:
    image: ghcr.io/advplyr/audiobookshelf:latest
    container_name: audiobookshelf
    # Run as a non-root user — create this user beforehand
    # or remove these lines if you're okay with root inside the container
    user: "1000:1000"
    ports:
      # Only expose to localhost if you're running a reverse proxy
      - "127.0.0.1:13378:80"
    volumes:
      # Config and metadata database
      - /opt/audiobookshelf/config:/config
      # Audiobookshelf metadata cache (cover art, etc.)
      - /opt/audiobookshelf/metadata:/metadata
      # Your actual media — mount as read-only unless you need ABS to write
      - /mnt/media/audiobooks:/audiobooks:ro
      - /mnt/media/podcasts:/podcasts
      # Podcasts are NOT read-only — ABS writes episode files here
    environment:
      - TZ=Europe/Berlin
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

A few things worth calling out explicitly:

The :ro flag on the audiobooks volume is intentional. ABS doesn’t need write access to your audiobook files, and this prevents an accidental metadata write operation from mangling filenames you carefully organized. The podcasts volume is different — ABS writes downloaded episodes there, so it needs write access.

The user field matters if your media files are owned by a specific UID on the host. If ABS can’t read the files, it won’t scan them — it won’t throw a loud error, it’ll just silently skip them.

Start it up:

docker compose up -d
docker compose logs -f

The first startup generates a config and starts an HTTP server on port 13378 (internally mapped to port 80 in the container). Hit http://your-server-ip:13378 and you’ll land on the initial setup wizard.


Reverse Proxy: nginx

Running ABS on a raw port over HTTP is fine for home network use. For anything outside — mobile apps over LTE, friends you’ve shared access with — you need HTTPS. Here’s a minimal nginx config:

server {
    listen 80;
    server_name abs.yourdomain.com;
    # Redirect all HTTP to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name abs.yourdomain.com;

    ssl_certificate     /etc/letsencrypt/live/abs.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/abs.yourdomain.com/privkey.pem;

    # Increase max body size for file uploads (cover art, etc.)
    client_max_body_size 100M;

    location / {
        proxy_pass         http://127.0.0.1:13378;
        proxy_http_version 1.1;

        # WebSocket support — required for real-time sync
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection "upgrade";

        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;

        # Increase timeouts for large file streaming
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }
}

The WebSocket headers are not optional. ABS uses WebSocket connections to push playback progress from clients to the server in real time. Without those headers, the initial page will load, but progress sync will silently fail and you’ll wonder why your position isn’t saving.

If you’re on Traefik, the labels equivalent:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.abs.rule=Host(`abs.yourdomain.com`)"
  - "traefik.http.routers.abs.entrypoints=websecure"
  - "traefik.http.routers.abs.tls.certresolver=letsencrypt"
  - "traefik.http.services.abs.loadbalancer.server.port=80"

Traefik handles WebSocket passthrough automatically.


Setting Up Libraries

After logging in, the first thing to do is create libraries. ABS separates audiobooks and podcasts into distinct library types — don’t mix them into a single library, you’ll lose functionality specific to each type.

Go to Settings → Libraries → Add Library.

For audiobooks: name it whatever you want, set the type to "Book", and point it at /audiobooks (the container path from your volume mount).

For podcasts: create a separate library with type "Podcast", pointing at /podcasts.

Trigger a scan manually from the library view. The first scan on a large collection can take a few minutes. ABS reads embedded ID3/M4B metadata, chapter markers, and cover art embedded in the files. It handles multi-file audiobooks (where each chapter is a separate MP3) as well as single-file M4Bs.

Metadata Matching

ABS can pull metadata from Audible, Open Library, and Google Books. Go to Settings → Item Metadata Utils to configure your preferred sources. For audiobooks with good embedded metadata, you often don’t need this. For rips from CDs or obscure collections with bare filenames, the matcher saves a lot of manual work.


Podcasts: Beyond Basic Feed Management

The podcast side of ABS is genuinely underrated. Add a podcast by pasting an RSS feed URL into + Add Podcast on your podcast library. ABS parses the feed, shows you the episode list, and lets you pick what to auto-download.

Auto-download configuration lives on each podcast’s settings page. You can set:

  • How many recent episodes to keep (great for high-frequency daily shows)
  • Whether to download new episodes automatically on a schedule
  • The schedule itself (cron expression or a simple interval)

For a news podcast you listen to daily, set "keep last 5 episodes, download on schedule". ABS handles retention automatically — it deletes older episodes once the limit is hit. You never need to think about disk space for podcasts again.

One thing ABS doesn’t do: it won’t fetch a full back-catalog of a 10-year-old podcast on its own initiative. You bulk-download by selecting episodes manually in the UI and hitting download. This is probably the right call — no one wants to wake up to 800 episode files and a full disk.


Users and Permissions

ABS has a proper multi-user system. Go to Settings → Users to create accounts.

Each user gets their own playback position tracking, finished/unfinished status, and bookmarks. This is the core feature that separates ABS from a basic file server — your partner can be 40% through a book while you’re at 75%, and neither position interferes with the other.

User roles:

  • Root: the initial admin account, full access
  • Admin: can manage libraries and users
  • User: can listen, manage their own progress, and optionally download

The "Allow Download" permission is granular per-user. If you’re running a family server and you’re worried about someone accidentally downloading the entire library to their phone, you can restrict it.

For anything public-facing, set a strong password on the admin account and don’t expose ABS directly — keep it behind the reverse proxy and consider adding fail2ban rules on the nginx access log.


Mobile Clients

The official apps are free on both platforms:

Setup is the same on both: open the app, enter your server URL (https://abs.yourdomain.com), log in. The app connects to your server, pulls your library, and you’re reading.

Offline listening works via the in-app download button on any item. Downloaded content plays without a network connection, and progress syncs back to the server when you reconnect. The sync is not push-based on iOS (Apple’s background limitations), so if you finish something offline, open the app once when connected to let it sync.

The Android app is more capable in the background — it can receive push notifications when new podcast episodes download, and it syncs more aggressively.

For audiobooks specifically: the chapter navigation is excellent. M4B files with embedded chapter metadata show a chapter list in the player. You can jump directly to chapter 12 without scrubbing a 12-hour file.


OPDS Catalog for External Clients

ABS exposes an OPDS catalog at /opds/v1.2/catalog. OPDS is a standard used by apps like Kybook 3, Moon+ Reader, PocketBook, and others.

To connect an OPDS client, point it at:

https://abs.yourdomain.com/opds/v1.2/catalog

Use your ABS username and password for authentication. This lets you browse and stream/download your audiobook library from any OPDS-compatible reader without using the native ABS app — useful for users with specific app preferences or on platforms the native app doesn’t support.


Gotchas

File permissions are the #1 problem. If your library scan finishes instantly and shows 0 items, the container user can’t read your files. Fix it with:

# Check what UID owns your media
ls -la /mnt/media/audiobooks/

# Option 1: Change the compose user to match your media owner
# Option 2: Add the container user to the group that owns the media
# Option 3: chmod/chown the media directory (least preferred)

Running the container as root (removing the user: line) is the quick fix but not the right answer for production.

Duplicate episode downloads. If you run multiple ABS instances pointing at the same podcast folder (e.g., during a migration), you can end up with duplicate files. ABS deduplication works by filename, not by content. Rename carefully during migrations.

The metadata folder grows. ABS caches cover art, extracted chapter images, and other metadata in /metadata. On a library of a few hundred books, this can reach several GB over time. Back it up — regenerating all covers from scratch on a rescan is slow. But also don’t put it on a slow network share; metadata reads happen constantly during browsing.

WebSocket timeouts behind load balancers. If you’re behind AWS ALB, Cloudflare, or any proxy with an aggressive WebSocket idle timeout, you’ll see progress sync fail silently after a few minutes of inactivity. Fix it at the proxy level by increasing the WebSocket timeout, or in ABS by enabling the "heartbeat" option in settings.

Token-based podcast feeds. Some podcast apps auto-discover a "secure" version of the feed. ABS generates per-user feed tokens that include credentials in the URL — don’t share these publicly, they’re equivalent to your password for feed access.


Production Best Practices

Backups: the only things you need to back up are /config (contains the SQLite database with all user data and library metadata) and /metadata (covers and caches). Your actual media files are the source of truth — ABS can re-scan those. The database is what holds playback progress, user accounts, and podcast subscriptions.

# Simple nightly backup with restic or rsync
rsync -av /opt/audiobookshelf/config/ /backup/audiobookshelf/config/
rsync -av /opt/audiobookshelf/metadata/ /backup/audiobookshelf/metadata/

Pin your image version in production. latest is convenient until an update breaks something on a Friday evening:

image: ghcr.io/advplyr/audiobookshelf:2.19.1

Check the releases page before updating and read the changelog — ABS occasionally has breaking changes to the database schema that require migration.

Don’t run the container as root if you can avoid it. It’s not ABS-specific advice, but worth repeating.

Resource limits are optional but sensible on a shared server:

deploy:
  resources:
    limits:
      memory: 512M
    reservations:
      memory: 128M

ABS is light on memory in normal operation. The limit mainly protects against runaway transcoding jobs or a scanning loop on a malformed audio file.


Wrapping Up

Audiobookshelf is one of those self-hosted projects that’s hard to go back from once you set it up. Progress sync across devices actually works. Podcast management is genuinely better than most dedicated apps. The mobile clients don’t feel like an afterthought.

The setup takes maybe 30 minutes end to end on a server you already have running. After that, the only maintenance is the occasional version update and making sure your media files stay organized the way ABS expects them.

If you’re coming from Plex for audiobooks, the metadata quality difference alone is worth the migration. If you’re coming from nothing — just a pile of M4Bs on a hard drive — this is the cleanest way to turn that pile into a proper library you’ll actually use.

👁 Views: 202,852 · Unique visitors: 57,408