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
| Feature | BookWyrm | Goodreads |
|---|---|---|
| Book tracking | Shelves, reading progress, dates | Shelves, reading progress |
| Reviews | Full reviews + status updates | Reviews + ratings |
| Social features | Follows, groups, lists, ActivityPub federation | Friends, groups, Amazon integration |
| Data ownership | You own everything | Amazon owns everything |
| Book data source | OpenLibrary, Inventaire, manual entry | Amazon catalog |
| Cost | Free (self-hosted) | Free (ad-supported) |
| Privacy | Full control | Amazon 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)
| Requirement | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4 cores |
| RAM | 2 GB | 4 GB |
| Disk | 20 GB | 50 GB+ |
| Required (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:
| Data | Location | Priority |
|---|---|---|
| PostgreSQL database | pgdata volume | Critical — all books, reviews, users |
| Media files | media_volume | Important — book covers, uploaded images |
| Static files | static_volume | Low — regenerated by collectstatic |
| Environment file | .env | Critical — 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.
Books not found during search
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
| Component | RAM (idle) | RAM (active) | CPU | Disk |
|---|---|---|---|---|
| Web (Django/Gunicorn) | ~200 MB | ~400 MB | Low | Minimal |
| Celery Worker | ~150 MB | ~300 MB | Medium (during imports) | Minimal |
| Celery Beat | ~80 MB | ~100 MB | Minimal | Minimal |
| PostgreSQL | ~100 MB | ~300 MB | Low-Medium | Grows with content |
| Redis (x2) | ~50 MB total | ~100 MB | Minimal | Minimal |
| Flower | ~80 MB | ~100 MB | Minimal | Minimal |
| Total | ~660 MB | ~1.3 GB | Low-Medium | 20 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.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments