Self-Hosting BookWyrm with Docker Compose

Want to Track Books Without Feeding Amazon’s Data Machine?

BookWyrm is a federated social platform for tracking reading, writing reviews, and discovering books — think Goodreads, but decentralized and community-owned. It speaks ActivityPub, so your BookWyrm instance connects to Mastodon, Lemmy, and the rest of the fediverse. Users can follow readers on other instances, share reviews across servers, and build reading lists without any corporate platform owning the data. Official site

FeatureBookWyrmGoodreads
Book trackingShelves, reading progress, datesShelves, reading progress
ReviewsFull reviews + status updatesReviews + ratings
Social featuresFollows, groups, lists, ActivityPub federationFriends, groups, Amazon integration
Data ownershipYou own everythingAmazon owns everything
Book data sourceOpenLibrary, Inventaire, manual entryAmazon catalog
CostFree (self-hosted)Free (ad-supported)
PrivacyFull controlAmazon tracks reading habits

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose v2+ installed (guide)
  • 2 GB RAM minimum (4 GB recommended)
  • 20 GB of free disk space (book covers and media grow over time)
  • A domain name with DNS configured
  • SMTP credentials for email (required for account verification)
RequirementMinimumRecommended
CPU2 cores4 cores
RAM2 GB4 GB
Disk20 GB50 GB+
EmailRequired (SMTP)Required (SMTP)

Docker Compose Configuration

BookWyrm runs seven services: the Django web app, a Celery worker for background tasks, Celery Beat for scheduled jobs, PostgreSQL, two Redis instances (activity stream + task broker), and Flower for queue monitoring.

Create a .env file first:

# BookWyrm environment configuration
SECRET_KEY=generate-a-long-random-string-here-minimum-50-chars
DOMAIN=bookwyrm.yourdomain.com
EMAIL=[email protected]

# Set to false for production
DEBUG=false

# Database
POSTGRES_PASSWORD=change-this-strong-password
POSTGRES_USER=bookwyrm
POSTGRES_DB=bookwyrm
POSTGRES_HOST=db
PGPORT=5432

# Redis (activity stream)
REDIS_ACTIVITY_HOST=redis_activity
REDIS_ACTIVITY_PORT=6379
REDIS_ACTIVITY_PASSWORD=change-this-redis-activity-password

# Redis (Celery broker)
REDIS_BROKER_HOST=redis_broker
REDIS_BROKER_PORT=6379
REDIS_BROKER_PASSWORD=change-this-redis-broker-password

# Celery monitoring
FLOWER_PORT=8888
FLOWER_USER=admin
FLOWER_PASSWORD=change-this-flower-password

# SMTP — required for account verification emails
EMAIL_HOST=smtp.yourdomain.com
EMAIL_PORT=587
EMAIL_HOST_USER=[email protected]
EMAIL_HOST_PASSWORD=your-smtp-password
EMAIL_USE_TLS=true
EMAIL_USE_SSL=false
EMAIL_SENDER_NAME=BookWyrm
EMAIL_SENDER_DOMAIN=yourdomain.com

# Media
MEDIA_ROOT=images/
ENABLE_THUMBNAIL_GENERATION=true
ENABLE_PREVIEW_IMAGES=true

Now create docker-compose.yml:

services:
  web:
    image: ghcr.io/bookwyrm-social/bookwyrm:v0.8.5
    restart: unless-stopped
    env_file: .env
    ports:
      - "8001:8000"
    volumes:
      - static_volume:/app/static
      - media_volume:/app/images
      - exports_volume:/app/exports
    depends_on:
      db:
        condition: service_healthy
      redis_activity:
        condition: service_healthy
      redis_broker:
        condition: service_healthy

  celery_worker:
    image: ghcr.io/bookwyrm-social/bookwyrm:v0.8.5
    restart: unless-stopped
    env_file: .env
    command: celery -A celerywyrm worker -l info -Q high_priority,medium_priority,low_priority,streams,images,suggested_users,broadcasts,misc
    volumes:
      - static_volume:/app/static
      - media_volume:/app/images
      - exports_volume:/app/exports
    depends_on:
      - web

  celery_beat:
    image: ghcr.io/bookwyrm-social/bookwyrm:v0.8.5
    restart: unless-stopped
    env_file: .env
    command: celery -A celerywyrm beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
    volumes:
      - static_volume:/app/static
      - media_volume:/app/images
    depends_on:
      - celery_worker

  flower:
    image: ghcr.io/bookwyrm-social/bookwyrm:v0.8.5
    restart: unless-stopped
    env_file: .env
    command: celery -A celerywyrm flower --basic_auth=${FLOWER_USER}:${FLOWER_PASSWORD}
    ports:
      - "8888:8888"
    depends_on:
      - celery_worker

  db:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis_activity:
    image: redis:7.2.1
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_ACTIVITY_PASSWORD} --appendonly yes
    volumes:
      - redis_activity_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_ACTIVITY_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis_broker:
    image: redis:7.2.1
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_BROKER_PASSWORD} --appendonly yes
    volumes:
      - redis_broker_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_BROKER_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  pgdata:
  static_volume:
  media_volume:
  exports_volume:
  redis_activity_data:
  redis_broker_data:

Start the stack:

docker compose up -d

Initial Setup

Once all containers are running, initialize the database and create an admin account:

# Run database migrations
docker compose exec web python manage.py migrate

# Initialize BookWyrm data (connectors, book sources)
docker compose exec web python manage.py initdb

# Collect static files
docker compose exec web python manage.py collectstatic --noinput

# Create the admin/superuser account
docker compose exec web python manage.py createsuperuser

Follow the prompts to set your admin username, email, and password. Then visit http://your-server-ip:8001 and log in. Navigate to Settings > Site Settings to configure your instance name, description, registration policy, and code of conduct.

Configuration

Federation

BookWyrm federates automatically once your instance is reachable over HTTPS with a valid domain. Other BookWyrm instances and Mastodon users can find your instance through the fediverse. To manage federation:

  • Settings > Federation — view connected instances
  • Block domains — prevent federation with specific instances
  • Allow list mode — restrict federation to approved instances only

Book Data Sources

BookWyrm fetches book metadata from external sources. Configure connectors in Settings > External Integrations:

  • OpenLibrary — enabled by default, largest open book database
  • Inventaire — community-maintained, Wikidata-backed
  • Manual entry — always available for books not in any database

Registration

Control who can join your instance:

  • Open registration — anyone can create an account
  • Approval required — users request access, admins approve
  • Invite only — existing users generate invite links
  • Closed — no new registrations

Advanced Configuration

S3-Compatible Storage

For instances with many users, offload media to S3-compatible storage (MinIO, Backblaze B2, Wasabi):

Add to your .env:

USE_S3=true
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_STORAGE_BUCKET_NAME=bookwyrm-media
AWS_S3_REGION_NAME=us-east-1
AWS_S3_ENDPOINT_URL=https://s3.yourstorage.com

Preview Image Generation

BookWyrm can generate OpenGraph preview images for books and statuses. This helps when links are shared on social media. Enable in .env:

ENABLE_PREVIEW_IMAGES=true

This uses more CPU during image generation but improves social sharing appearance.

Reverse Proxy

Behind a reverse proxy like Nginx Proxy Manager, Caddy, or Traefik, set NGINX_SETUP=reverse_proxy in your .env and map port 8001 to your domain. Ensure the proxy passes X-Forwarded-For and X-Forwarded-Proto headers — BookWyrm needs these for correct federation URLs.

For detailed reverse proxy setup, see Reverse Proxy Setup.

Backup

Critical data to back up:

DataLocationPriority
PostgreSQL databasepgdata volumeCritical — all books, reviews, users
Media filesmedia_volumeImportant — book covers, uploaded images
Static filesstatic_volumeLow — regenerated by collectstatic
Environment file.envCritical — contains secrets

Back up the database with:

docker compose exec db pg_dump -U bookwyrm bookwyrm > bookwyrm-backup-$(date +%Y%m%d).sql

For automated backup strategies, see Backup Strategy.

Troubleshooting

Federation not working

Symptom: Other instances can’t find your BookWyrm server, follows fail with timeout errors.

Fix: BookWyrm requires HTTPS with a valid certificate. Verify your domain resolves correctly and your reverse proxy handles SSL termination. Check that DOMAIN in .env matches exactly (no trailing slash, no https:// prefix — just the bare domain).

Celery worker not processing tasks

Symptom: Book imports hang, emails don’t send, federation queue grows.

Fix: Check worker logs with docker compose logs celery_worker. Common causes: incorrect Redis password in .env, Redis container not healthy, or out of memory. Restart the worker with docker compose restart celery_worker.

Symptom: Searching for books returns no results even for popular titles.

Fix: Verify OpenLibrary connector is enabled in Settings > External Integrations. Check if your server can reach openlibrary.org — some VPS providers block outbound HTTPS. Test with docker compose exec web python -c "import urllib.request; urllib.request.urlopen('https://openlibrary.org')".

Preview images not generating

Symptom: Shared links show no preview image on social media.

Fix: Ensure ENABLE_PREVIEW_IMAGES=true in .env and restart the web container. Preview generation requires the celery_worker to be running. Check worker logs for image generation errors — missing fonts are a common cause.

High memory usage

Symptom: Server becomes unresponsive, OOM killer terminates containers.

Fix: BookWyrm with all services uses ~1.5 GB idle. If memory is tight, remove the flower service (monitoring only) and reduce PostgreSQL shared memory: set POSTGRES_SHM_SIZE=64mb in .env. Consider limiting Celery worker concurrency by adding -c 2 to the worker command.

Resource Requirements

ComponentRAM (idle)RAM (active)CPUDisk
Web (Django/Gunicorn)~200 MB~400 MBLowMinimal
Celery Worker~150 MB~300 MBMedium (during imports)Minimal
Celery Beat~80 MB~100 MBMinimalMinimal
PostgreSQL~100 MB~300 MBLow-MediumGrows with content
Redis (x2)~50 MB total~100 MBMinimalMinimal
Flower~80 MB~100 MBMinimalMinimal
Total~660 MB~1.3 GBLow-Medium20 GB+

Verdict

If you’re looking for a Goodreads replacement that respects your privacy, BookWyrm is the right tool. It’s the only mature, self-hostable book tracking platform with social features and ActivityPub federation. The reading experience is genuinely good — shelves, reading progress, annual goals, and lists all work well.

The trade-off is complexity. Seven containers for a book tracker is a lot, and SMTP is a hard requirement (not optional). For solo use, something like a simple spreadsheet might be more practical. But for a book club, small community, or anyone who wants to own their reading data while still being social about it, BookWyrm delivers.

FAQ

Can I import my Goodreads data?

Yes. BookWyrm supports importing from Goodreads CSV exports, LibraryThing, OpenLibrary, and StoryGraph. Go to Settings > Import and upload your export file. Large imports (1,000+ books) may take several hours to process via Celery.

How does federation work for book reviews?

When you post a review, it federates as an ActivityPub Note to your followers — including followers on Mastodon, Pleroma, and other BookWyrm instances. The review appears in their timeline. Replies from any platform federate back to your instance.

Is there a mobile app?

No native mobile app exists. BookWyrm’s web interface is responsive and works well on mobile browsers. Some users access BookWyrm through Mastodon mobile apps (since reviews federate as posts), but the full reading-tracking UI requires the web interface.

Can multiple people use the same instance?

Yes. BookWyrm is designed as a multi-user platform. Configure registration policy in Settings to control who can join. Each user gets their own shelves, reviews, and reading lists.

How much disk space do book covers use?

Roughly 200-500 KB per book cover. A library of 1,000 books uses approximately 200-500 MB for covers alone. Preview images (if enabled) add ~100 KB per book. Plan for growth if your instance has many users.

Comments