Files
hermes-config/skills/self-hosting/audiobookshelf-setup/SKILL.md
T
2026-07-12 10:17:17 -04:00

8.6 KiB
Raw Blame History

name, description, version, author, license, platforms, metadata, related_skills
name description version author license platforms metadata related_skills
audiobookshelf-setup Deploy Audiobookshelf on a self-hosted Linux server — Docker setup, reverse proxy with nginx + Let's Encrypt, DuckDNS for dynamic DNS, and DNS challenge for SSL when ISP blocks ports 80/443. 1.0.0 Hermes Agent MIT
linux
hermes
tags category
audiobookshelf
docker
nginx
lets-encrypt
duckdns
reverse-proxy
self-hosted
self-hosting
plex-setup
selfhosted-migration

Audiobookshelf Setup

Deploy Audiobookshelf via Docker with secure HTTPS access — including reverse proxy, dynamic DNS, and SSL via Let's Encrypt DNS challenge (for residential ISPs that block ports 80/443).

When to Use

  • User asks to install Audiobookshelf
  • User wants a self-hosted audiobook/podcast server
  • User needs HTTPS access to a self-hosted service behind a residential ISP
  • User needs a reverse proxy pattern for any Docker-hosted web service

Prerequisites

  • Docker + docker-compose-v2 installed
  • A domain or DuckDNS subdomain (for SSL)
  • Port forwarding in router (for external access)

Setup Steps

1. Create directory structure

mkdir -p ~/docker/audiobookshelf/{config,metadata}

2. Create docker-compose.yml

services:
  audiobookshelf:
    image: ghcr.io/advplyr/audiobookshelf:latest
    container_name: audiobookshelf
    network_mode: host
    environment:
      - TZ=America/New_York
      - PORT=13378
    volumes:
      - /home/ray/docker/audiobookshelf/config:/config
      - /home/ray/docker/audiobookshelf/metadata:/metadata
      - /path/to/audiobooks:/audiobooks:ro
    restart: unless-stopped

Key points:

  • network_mode: host — Audiobookshelf defaults to port 80 internally. Set PORT=13378 to move it off port 80 (needed for nginx later). With host networking, the port binds directly on the host.
  • Audiobook mount — Use :ro (read-only). Audiobookshelf only reads files; no write access needed.
  • The container needs ~5-10 seconds on first run to initialize its SQLite database and generate a JWT secret.

3. Start and verify

cd ~/docker/audiobookshelf && docker compose pull && docker compose up -d
sleep 10
curl -s -o /dev/null -w "HTTP %{http_code}" http://localhost:13378/
# Should return 200

4. Set up DuckDNS (free dynamic DNS)

If using a residential ISP without a static IP:

  1. Go to https://duckdns.org → sign in with GitHub/Google
  2. Create a subdomain (e.g., mybooks)
  3. Copy the token from the top of the page

Install the updater script and cron job:

sudo mkdir -p /opt/duckdns

sudo tee /opt/duckdns/update.sh << 'EOF' > /dev/null
#!/bin/bash
echo url="https://www.duckdns.org/update?domains=<YOUR_SUBDOMAIN>&token=<YOUR_TOKEN>&ip=" | curl -k -s -o /opt/duckdns/duck.log -K -
EOF

sudo chmod +x /opt/duckdns/update.sh

# Run once to register
sudo /opt/duckdns/update.sh && cat /opt/duckdns/duck.log
# Should output "OK"

# Auto-update every 5 minutes
(sudo crontab -l 2>/dev/null | grep -v duckdns; echo "*/5 * * * * /opt/duckdns/update.sh") | sudo crontab -

Verify the domain resolves: dig +short <subdomain>.duckdns.org

5. Install nginx + certbot

sudo apt-get update && sudo apt-get install -y nginx certbot python3-certbot-nginx

# Install DuckDNS plugin for Let's Encrypt DNS challenge
sudo pip3 install --break-system-packages certbot-dns-duckdns

6. Get SSL certificate via DNS challenge

Why DNS challenge: Residential ISPs often block incoming ports 80/443. Let's Encrypt HTTP challenge requires port 80. DNS challenge works without any open ports — it adds a TXT record to the domain.

# Create credentials file
sudo mkdir -p /etc/letsencrypt
sudo tee /etc/letsencrypt/duckdns.ini << 'EOF' > /dev/null
dns_duckdns_token=<YOUR_TOKEN>
EOF
sudo chmod 600 /etc/letsencrypt/duckdns.ini

# Get certificate
sudo certbot certonly \
  --authenticator dns-duckdns \
  --dns-duckdns-credentials /etc/letsencrypt/duckdns.ini \
  --dns-duckdns-propagation-seconds 30 \
  -d <domain>.duckdns.org \
  --non-interactive --agree-tos --email <your-email>

Cert will auto-renew via certbot's built-in systemd timer. No cron needed.

7. Configure nginx reverse proxy with SSL

Pick an available high port (since 80/443 are blocked by ISP). Check what's free:

sudo ss -tlnp | grep -E "3443|4443|5443"

Create the nginx config:

server {
    listen 3443 ssl;
    server_name <domain>.duckdns.org;

    ssl_certificate /etc/letsencrypt/live/<domain>.duckdns.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<domain>.duckdns.org/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    client_max_body_size 500M;

    location / {
        proxy_pass http://127.0.0.1:13378;
        proxy_http_version 1.1;
        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;
        proxy_read_timeout 86400;
    }
}

# Optional: redirect HTTP → HTTPS
server {
    listen 80;
    server_name <domain>.duckdns.org;
    return 301 https://$host:3443$request_uri;
}

Enable and reload:

sudo cp audiobookshelf.conf /etc/nginx/sites-available/audiobookshelf
sudo ln -sf /etc/nginx/sites-available/audiobookshelf /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

8. Port forwarding

Forward the chosen high port (e.g., 3443 TCP) in your router to the server's LAN IP.

9. Access and create library

  1. Visit https://<domain>.duckdns.org:3443
  2. Create an admin account on first visit
  3. Add a library → point it to /audiobooks (the container's internal mount path)
  4. Audiobookshelf will scan and populate automatically

10. Client apps

Connect the app to https://<domain>.duckdns.org:3443 with your account. Supports offline downloads, sleep timer, and playback speed controls.

Pitfalls

  • Port 80 conflicts with host networking — Audiobookshelf defaults to port 80 internally. With network_mode: host, this binds directly on the host, conflicting with nginx. Set PORT=13378 in the environment to move it before nginx setup.
  • Docker port conflicts — Other containers may already use ports you want for nginx (e.g., Heimdall on 8443). Check with docker ps --format '{{.Names}} {{.Ports}}' before picking an nginx port.
  • Shell mangling of tokens — If API tokens or Docker env vars contain special chars (underscores, dashes), bash may mangle them in variable expansion. Use Python for HTTP calls with tokens: urllib.request.Request(url) with headers works reliably. See plex-setup for the same pattern.
  • Hairpin NAT — Testing the public URL from inside the LAN often fails (connection refused) even when the setup is correct. Always test from a mobile device on cellular data, not WiFi.
  • certbot-dns-duckdns not in apt — On Ubuntu 26.04, this package isn't in apt. Use pip3 install --break-system-packages certbot-dns-duckdns instead.
  • Audiobookshelf first-run delay — The container needs ~10 seconds to initialize its SQLite database, generate a JWT secret, and start listening. Don't assume it's broken if port 13378 isn't immediately available.
  • docker compose restart vs up -drestart reuses the old container config (doesn't pick up new env vars like PORT). Use up -d when changing environment variables, then restart for simple process cycling.

Verification Checklist

  • curl http://localhost:13378/ returns 200
  • DuckDNS updater returns "OK" on manual run
  • dig +short <domain>.duckdns.org returns the correct public IP
  • SSL cert obtained (sudo certbot certificates)
  • nginx config passes sudo nginx -t
  • curl -sk https://localhost:3443/ returns 200 (SSL working locally)
  • Port forwarding rule added in router
  • External access confirmed from cellular data

Support Files

  • templates/docker-compose.yml — Starter Docker Compose file
  • templates/nginx-audiobookshelf.conf — nginx reverse proxy config with SSL
  • scripts/duckdns-update.sh — DuckDNS IP update script (set DOMAIN and TOKEN)
  • references/heimdall-dashboard.md — Add the service to Heimdall dashboard via SQLite