Installing bookwyrm using docker and ubuntu
Why?
Because we want to use a federated book review system :)
What do I need to install?
You need a Linux Server with Docker, and Docker-compose installed. creating directories
What's my setup?
- ProxMox
- KVM
- Ubuntu 20.04
- Docker
- Docker-compose
- External Nginx
- Reverse Proxy Configuration
- LetsEncrypt Certificate
- CloudFlare
- DNS Manager
Where I can find out more about the project?
How I can install it?
Let's start with it
creating directories
mkdir -p /opt/bookwyrm
mkdir -p /opt/bookwyrm/nginx/conf
mkdir -p /opt/bookwyrm/pgsql/data
mkdir -p /opt/bookwyrm/pgsql/backup
mkdir -p /opt/bookwyrm/data/app/static
mkdir -p /opt/bookwyrm/data/app/media
mkdir -p /opt/bookwyrm/data/redis/config
mkdir -p /opt/bookwyrm/data/redis/activity_data
mkdir -p /opt/bookwyrm/data/redis/broker_data
cloning the project
cd /opt/bookwyrm
git clone https://github.com/bookwyrm-social/bookwyrm.git source
cd source
git checkout production
creating the redis config
copying redis.conf
cd /opt/bookwyrm/source
cp redis.conf /opt/bookwyrm/data/redis/config
creating nginx.conf
creting production.conf
cd /opt/bookwyrm/data/nginx/conf
vim production.conf
content
include /etc/nginx/conf.d/server_config;
upstream web {
server web:8000;
}
server {
access_log /var/log/nginx/access.log cache_log;
listen 80;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
#include /etc/nginx/mime.types;
#default_type application/octet-stream;
gzip on;
gzip_disable "msie6";
proxy_read_timeout 1800s;
chunked_transfer_encoding on;
# store responses to anonymous users for up to 1 minute
proxy_cache bookwyrm_cache;
proxy_cache_valid any 1m;
add_header X-Cache-Status $upstream_cache_status;
# ignore the set cookie header when deciding to
# store a response in the cache
proxy_ignore_headers Cache-Control Set-Cookie Expires;
# PUT requests always bypass the cache
# logged in sessions also do not populate the cache
# to avoid serving personal data to anonymous users
proxy_cache_methods GET HEAD;
proxy_no_cache $cookie_sessionid;
proxy_cache_bypass $cookie_sessionid;
# tell the web container the address of the outside client
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
# rate limit the login or password reset pages
location ~ ^/(login[^-/]|password-reset|resend-link|2fa-check) {
limit_req zone=loginlimit;
proxy_pass http://web;
}
# do not log periodic polling requests from logged in users
location /api/updates/ {
access_log off;
proxy_pass http://web;
}
# forward any cache misses or bypass to the web container
location / {
proxy_pass http://web;
}
# directly serve images and static files from the
# bookwyrm filesystem using sendfile.
# make the logs quieter by not reporting these requests
location ~ ^/(images|static)/ {
root /app/;
try_files $uri =404;
add_header X-Cache-Status STATIC;
access_log off;
}
# monitor the celery queues with flower, no caching enabled
#location /flower/ {
# proxy_pass http://flower:8888;
# proxy_cache_bypass 1;
#}
}
creting server_config file
cd /opt/bookwyrm/data/nginx/conf
vim server_config
content
client_max_body_size 10m;
limit_req_zone $binary_remote_addr zone=loginlimit:10m rate=1r/s;
# include the cache status in the log message
log_format cache_log '$upstream_cache_status - '
'$remote_addr [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$upstream_response_time $request_time';
# Create a cache for responses from the web app
proxy_cache_path
/var/cache/nginx/bookwyrm_cache
keys_zone=bookwyrm_cache:20m
loader_threshold=400
loader_files=400
max_size=400m;
# use the accept header as part of the cache key
# since activitypub endpoints have both HTML and JSON
# on the same URI.
proxy_cache_key $scheme$proxy_host$uri$is_args$args$http_accept;
docker env config
creating the env config
cd /opt/bookwyrm/source
cp .env.example .env
vim .env
content
SECRET_KEY="a-very-good-secret-here-25-chars-letter-numbers-symbols"
DEBUG=false
USE_HTTPS=true
DOMAIN=domain.tld
EMAIL=help@domain.tld
LANGUAGE_CODE="en-us"
DEFAULT_LANGUAGE="English"
ALLOWED_HOSTS="localhost,127.0.0.1,[::1],domain.tld"
MEDIA_ROOT=images/
# PostgreSQL
PGPORT=5432
POSTGRES_PASSWORD=a-very-good-password-here
POSTGRES_USER=bookwyrm
POSTGRES_DB=bookwyrm
POSTGRES_HOST=db
# Redis activity stream manager
MAX_STREAM_LENGTH=200
REDIS_ACTIVITY_HOST=redis_activity
REDIS_ACTIVITY_PORT=6379
REDIS_ACTIVITY_PASSWORD=a-very-good-password-here
# Redis as celery broker
REDIS_BROKER_HOST=redis_broker
REDIS_BROKER_PORT=6379
REDIS_BROKER_PASSWORD=a-very-good-password-here
# Monitoring for celery
FLOWER_PORT=8888
FLOWER_USER=admin
FLOWER_PASSWORD=a-very-good-password-here
# Email config
EMAIL_HOST=mail.domain.tld
EMAIL_PORT=587
EMAIL_HOST_USER=user@domain.tld
EMAIL_HOST_PASSWORD=a-very-good-password-here
EMAIL_USE_TLS=true
EMAIL_USE_SSL=false
EMAIL_SENDER_NAME=no-reply
EMAIL_SENDER_DOMAIN=domain.tld
# Query timeouts
SEARCH_TIMEOUT=5
QUERY_TIMEOUT=5
# Thumbnails Generation
ENABLE_THUMBNAIL_GENERATION=true
# S3 configuration
USE_S3=false
AWS_ACCESS_KEY_ID=your-access-key-here
AWS_SECRET_ACCESS_KEY=your-secret-access-key-here
AWS_STORAGE_BUCKET_NAME=your-bucket-name-here
AWS_S3_REGION_NAME=your-bucket-region-here
AWS_S3_CUSTOM_DOMAIN=https://[your-bucket-name].[your-endpoint_url]
AWS_S3_ENDPOINT_URL=https://your-endpoint-url
# Preview image generation can be computing and storage intensive
ENABLE_PREVIEW_IMAGES=true
# Specify RGB tuple or RGB hex strings,
PREVIEW_TEXT_COLOR=#363636
PREVIEW_IMG_WIDTH=1200
PREVIEW_IMG_HEIGHT=630
PREVIEW_DEFAULT_COVER_COLOR=#002549
# Set HTTP_X_FORWARDED_PROTO ONLY to true if you know what you are doing.
# Only use it if your proxy is "swallowing" if the original request was made
# via https. Please refer to the Django-Documentation and assess the risks
# for your instance:
# https://docs.djangoproject.com/en/3.2/ref/settings/#secure-proxy-ssl-header
HTTP_X_FORWARDED_PROTO=false
# TOTP settings
TWO_FACTOR_LOGIN_VALIDITY_WINDOW=2
TWO_FACTOR_LOGIN_MAX_SECONDS=60
# Additional hosts to allow in the Content-Security-Policy, "self" (should be DOMAIN)
# and AWS_S3_CUSTOM_DOMAIN (if used) are added by default.
# Value should be a comma-separated list of host names.
#CSP_ADDITIONAL_HOSTS=
docker-compose config
creating a new docker-compose file
cd /opt/bookwyrm/source
mv docker-compose.yml docker-compose.yml.original
vim docker-compose.yml
content
version: '3'
services:
nginx:
image: nginx:latest
container_name: bookwyrm_nginx
restart: unless-stopped
ports:
- "8001:80"
depends_on:
- web
networks:
- main
volumes:
- .:/app
- app_static:/app/static
- app_media:/app/images
- nginx_conf:/etc/nginx/conf.d
db:
build: postgres-docker
env_file: .env
container_name: bookwyrm_pgsql
entrypoint: /bookwyrm-entrypoint.sh
command: cron postgres
volumes:
- pgdata:/var/lib/postgresql/data
- pgbackup:/backups
networks:
- main
web:
build: .
container_name: bookwyrm_web
env_file: .env
command: gunicorn bookwyrm.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/app
- app_static:/app/static
- app_media:/app/images
depends_on:
- db
- celery_worker
- redis_activity
networks:
- main
ports:
- "8000:8000"
redis_activity:
image: redis
container_name: bookwyrm_redis_activity
command: redis-server --requirepass ${REDIS_ACTIVITY_PASSWORD} --appendonly yes --port ${REDIS_ACTIVITY_PORT}
volumes:
- /opt/bookwyrm/data/redis/config/redis.conf:/etc/redis/redis.conf
- redis_activity_data:/data
env_file: .env
networks:
- main
restart: on-failure
redis_broker:
container_name: bookwyrm_redis_broker
image: redis
command: redis-server --requirepass ${REDIS_BROKER_PASSWORD} --appendonly yes --port ${REDIS_BROKER_PORT}
volumes:
- /opt/bookwyrm/data/redis/config/redis.conf:/etc/redis/redis.conf
- redis_broker_data:/data
env_file: .env
networks:
- main
restart: on-failure
celery_worker:
container_name: bookwyrm_celery_worker
env_file: .env
build: .
networks:
- main
command: celery -A celerywyrm worker -l info -Q high_priority,medium_priority,low_priority,imports,broadcast
volumes:
- .:/app
- app_static:/app/static
- app_media:/app/images
depends_on:
- db
- redis_broker
restart: on-failure
celery_beat:
container_name: bookwyrm_celery_beat
env_file: .env
build: .
networks:
- main
command: celery -A celerywyrm beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler
volumes:
- .:/app
- app_static:/app/static
- app_media:/app/images
depends_on:
- celery_worker
restart: on-failure
flower:
container_name: bookwyrm_flower
build: .
command: celery -A celerywyrm flower --basic_auth=${FLOWER_USER}:${FLOWER_PASSWORD} --url_prefix=flower
env_file: .env
volumes:
- .:/app
networks:
- main
depends_on:
- db
- redis_broker
restart: on-failure
dev-tools:
container_name: bookwyrm_devtools
build: dev-tools
env_file: .env
volumes:
- .:/app
volumes:
nginx_conf:
driver_opts:
type: none
device: /opt/bookwyrm/data/nginx/conf
o: bind
pgdata:
driver_opts:
type: none
device: /opt/bookwyrm/data/pgsql/data
o: bind
pgbackup:
driver_opts:
type: none
device: /opt/bookwyrm/data/pgsql/backup
o: bind
app_static:
driver_opts:
type: none
device: /opt/bookwyrm/data/app/static
o: bind
app_media:
driver_opts:
type: none
device: /opt/bookwyrm/data/app/media
o: bind
redis_activity_data:
driver_opts:
type: none
device: /opt/bookwyrm/data/redis/activity_data
o: bind
redis_broker_data:
driver_opts:
type: none
device: /opt/bookwyrm/data/redis/broker_data
o: bind
networks:
main:
initializing bookwyrm
database
cd /opt/bookwyrm/source
./bw-dev migrate
containers
cd /opt/bookwyrm/source
docker-compose up -d
initial setup
cd /opt/bookwyrm/source
./bw-dev setup
expect output
...
...
...
*******************************************
Use this code to create your admin account:
c6c35779-BOLHA-IS-COOL-c026610920d6
*******************************************
reverse proxy config
here we are using an external nginx as our reverse proxy, here follows our config, this is just an example
server {
listen your_listen_ip_here:80;
server_name domain.tld;
location / {
return 301 https://domain.tld$request_uri;
}
}
server {
listen your_listen_ip_here:443 ssl http2;
server_name domain.tld;
ssl_certificate /etc/letsencrypt/live/domain.tld/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.tld/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_dhparam /etc/letsencrypt/dh-param.pem;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_ecdh_curve prime256v1;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
gzip on;
gzip_types text/css application/javascript image/svg+xml;
gzip_vary on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://your_docker_instance_ip_here:8000;
proxy_set_header Host $host;
}
location /images/ {
proxy_pass http://your_docker_instance_ip_here:8001;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
location /static/ {
proxy_pass http://your_docker_instance_ip_here:8001;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
restart your nginx
finish the bookwyrm config via UI
now, go to your site and finish the configuration using the admin CODE
That's it, we're done, enjoy your Bookwyrm instance!
enabling object storage (s3)
If you want to enable the Object Storage, edit your env config file
cd /opt/bookwyrm/source
vim .env
Adjust the object storage configuration
USE_S3=true
AWS_ACCESS_KEY_ID=your-access-key-here
AWS_SECRET_ACCESS_KEY=your-secret-access-key-here
AWS_STORAGE_BUCKET_NAME=your-bucket-name-here
AWS_S3_REGION_NAME=your-bucket-region-here
AWS_S3_CUSTOM_DOMAIN=https://[your-bucket-name].[your-endpoint_url]
AWS_S3_ENDPOINT_URL=https://your-endpoint-url
Sync the files
./bw-dev copy_media_to_s3
Recreate all containers
cd /opt/bookwyrm/source
docker-compose up -d
That's it!
:)