Adds bookwyrm app.

This commit is contained in:
2026-06-19 21:32:56 +00:00
parent 78ac8d0cbb
commit 7b41ed5148
11 changed files with 493 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
# BookWyrm
BookWyrm is a federated social reading platform — a self-hosted alternative to Goodreads, connected to the ActivityPub network.
## Dependencies
- **PostgreSQL** - Database for storing books, reviews, and user data
- **Redis** - Used for caching and background job queuing
- **SMTP** - For account verification and notifications
## Configuration
Key settings in `config.yaml`:
- **domain** - Where BookWyrm will be accessible
- **storage** - Persistent volume size (default: `2Gi`)
- **smtp** - Email settings inherited from your Wild Cloud SMTP service
## First-Time Setup
1. Add and deploy the app:
```bash
wild app add bookwyrm
wild app deploy bookwyrm
```
2. Visit the app URL — on first load, a setup wizard will guide you through creating the admin account and configuring the instance.
3. After setup, log in and configure the instance settings (name, description, registration policy) from the admin panel at `/settings/`.
## Notes
- BookWyrm federates with other BookWyrm instances and Mastodon via ActivityPub — users on other instances can follow and interact with your users
- Book metadata is imported from OpenLibrary and Inventaire — internet access is required for book search to work
- The `secretKey` is auto-generated in `secrets.yaml`

View File

@@ -0,0 +1,83 @@
apiVersion: batch/v1
kind: Job
metadata:
name: bookwyrm-db-init
namespace: bookwyrm
spec:
template:
metadata:
labels:
component: db-init
spec:
securityContext:
runAsNonRoot: true
runAsUser: 999
runAsGroup: 999
seccompProfile:
type: RuntimeDefault
restartPolicy: OnFailure
containers:
- name: db-init
image: postgres:16-alpine
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
env:
- name: PGHOST
value: "{{ .db.host }}"
- name: PGPORT
value: "{{ .db.port }}"
- name: PGUSER
value: postgres
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: postgres.password
- name: BOOKWYRM_DB_USER
value: "{{ .db.user }}"
- name: BOOKWYRM_DB_NAME
value: "{{ .db.name }}"
- name: BOOKWYRM_DB_PASSWORD
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: dbPassword
command:
- /bin/sh
- -c
- |
echo "Initializing BookWyrm database..."
# Create user if it doesn't exist
psql -c "
DO \$\$
BEGIN
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '$BOOKWYRM_DB_USER') THEN
CREATE USER \"$BOOKWYRM_DB_USER\" WITH PASSWORD '$BOOKWYRM_DB_PASSWORD';
ELSE
ALTER USER \"$BOOKWYRM_DB_USER\" WITH PASSWORD '$BOOKWYRM_DB_PASSWORD';
END IF;
END
\$\$;
"
# Create database if it doesn't exist
if ! psql -lqt | cut -d \| -f 1 | grep -qw "$BOOKWYRM_DB_NAME"; then
echo "Creating database $BOOKWYRM_DB_NAME..."
createdb -O "$BOOKWYRM_DB_USER" "$BOOKWYRM_DB_NAME"
else
echo "Database $BOOKWYRM_DB_NAME already exists."
fi
# Grant privileges
psql -d "$BOOKWYRM_DB_NAME" -c "
GRANT ALL PRIVILEGES ON DATABASE \"$BOOKWYRM_DB_NAME\" TO \"$BOOKWYRM_DB_USER\";
GRANT ALL ON SCHEMA public TO \"$BOOKWYRM_DB_USER\";
GRANT USAGE ON SCHEMA public TO \"$BOOKWYRM_DB_USER\";
"
echo "Database initialization completed."

View File

@@ -0,0 +1,120 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: bookwyrm-worker
namespace: bookwyrm
spec:
replicas: 1
strategy:
type: RollingUpdate
selector:
matchLabels:
component: worker
template:
metadata:
labels:
component: worker
spec:
securityContext:
runAsNonRoot: false
runAsUser: 0
seccompProfile:
type: RuntimeDefault
containers:
- name: celery-worker
image: ghcr.io/bookwyrm-social/bookwyrm:v0.8.6
command:
- celery
- -A
- celerywyrm
- worker
- --pool=threads
- --concurrency=10
- -l
- info
- -Q
- high_priority,medium_priority,low_priority,streams,images,suggested_users,email,connectors,lists,inbox,imports,import_triggered,broadcast,misc
env:
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: secretKey
- name: DEBUG
value: "false"
- name: DOMAIN
value: {{ .domain }}
- name: ALLOWED_HOSTS
value: {{ .domain }}
- name: EMAIL
value: {{ .smtp.from }}
- name: POSTGRES_HOST
value: {{ .db.host }}
- name: PGPORT
value: "{{ .db.port }}"
- name: POSTGRES_DB
value: {{ .db.name }}
- name: POSTGRES_USER
value: {{ .db.user }}
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: dbPassword
- name: REDIS_ACTIVITY_HOST
value: {{ .redis.host }}
- name: REDIS_ACTIVITY_PORT
value: "6379"
- name: REDIS_ACTIVITY_PASSWORD
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: redis.password
- name: REDIS_ACTIVITY_URL
value: redis://:$(REDIS_ACTIVITY_PASSWORD)@{{ .redis.host }}:6379/0
- name: REDIS_BROKER_HOST
value: {{ .redis.host }}
- name: REDIS_BROKER_PORT
value: "6379"
- name: REDIS_BROKER_PASSWORD
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: redis.password
- name: REDIS_BROKER_URL
value: redis://:$(REDIS_BROKER_PASSWORD)@{{ .redis.host }}:6379/1
- name: EMAIL_HOST
value: {{ .smtp.host }}
- name: EMAIL_PORT
value: "{{ .smtp.port }}"
- name: EMAIL_HOST_USER
value: {{ .smtp.user }}
- name: EMAIL_HOST_PASSWORD
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: smtpPassword
- name: EMAIL_USE_TLS
value: "true"
- name: EMAIL_USE_SSL
value: "false"
- name: MEDIA_ROOT
value: /app/images
- name: STATIC_ROOT
value: /app/static
resources:
limits:
cpu: 1000m
ephemeral-storage: 1Gi
memory: 1Gi
requests:
cpu: 50m
ephemeral-storage: 50Mi
memory: 256Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
restartPolicy: Always

View File

@@ -0,0 +1,149 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: bookwyrm
namespace: bookwyrm
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
component: web
template:
metadata:
labels:
component: web
spec:
securityContext:
runAsNonRoot: false
runAsUser: 0
seccompProfile:
type: RuntimeDefault
containers:
- name: bookwyrm
image: ghcr.io/bookwyrm-social/bookwyrm:v0.8.6
command: ["gunicorn", "bookwyrm.wsgi:application"]
ports:
- name: http
containerPort: 8000
protocol: TCP
env:
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: secretKey
- name: DEBUG
value: "false"
- name: DOMAIN
value: {{ .domain }}
- name: ALLOWED_HOSTS
value: {{ .domain }}
- name: EMAIL
value: {{ .smtp.from }}
- name: POSTGRES_HOST
value: {{ .db.host }}
- name: PGPORT
value: "{{ .db.port }}"
- name: POSTGRES_DB
value: {{ .db.name }}
- name: POSTGRES_USER
value: {{ .db.user }}
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: dbPassword
- name: REDIS_ACTIVITY_HOST
value: {{ .redis.host }}
- name: REDIS_ACTIVITY_PORT
value: "6379"
- name: REDIS_ACTIVITY_PASSWORD
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: redis.password
- name: REDIS_ACTIVITY_URL
value: redis://:$(REDIS_ACTIVITY_PASSWORD)@{{ .redis.host }}:6379/0
- name: REDIS_BROKER_HOST
value: {{ .redis.host }}
- name: REDIS_BROKER_PORT
value: "6379"
- name: REDIS_BROKER_PASSWORD
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: redis.password
- name: REDIS_BROKER_URL
value: redis://:$(REDIS_BROKER_PASSWORD)@{{ .redis.host }}:6379/1
- name: EMAIL_HOST
value: {{ .smtp.host }}
- name: EMAIL_PORT
value: "{{ .smtp.port }}"
- name: EMAIL_HOST_USER
value: {{ .smtp.user }}
- name: EMAIL_HOST_PASSWORD
valueFrom:
secretKeyRef:
name: bookwyrm-secrets
key: smtpPassword
- name: EMAIL_USE_TLS
value: "true"
- name: EMAIL_USE_SSL
value: "false"
- name: EMAIL_SENDER_NAME
value: BookWyrm
- name: EMAIL_SENDER_DOMAIN
value: {{ .domain }}
- name: MEDIA_ROOT
value: /app/images
- name: STATIC_ROOT
value: /app/static
- name: ENABLE_THUMBNAIL_GENERATION
value: "true"
resources:
limits:
cpu: 1000m
ephemeral-storage: 2Gi
memory: 1Gi
requests:
cpu: 50m
ephemeral-storage: 50Mi
memory: 256Mi
volumeMounts:
- name: bookwyrm-images
mountPath: /app/images
- name: bookwyrm-static
mountPath: /app/static
- name: bookwyrm-exports
mountPath: /app/exports
livenessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 120
timeoutSeconds: 10
periodSeconds: 30
failureThreshold: 6
readinessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 60
timeoutSeconds: 5
periodSeconds: 15
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
volumes:
- name: bookwyrm-images
persistentVolumeClaim:
claimName: bookwyrm-images
- name: bookwyrm-static
emptyDir: {}
- name: bookwyrm-exports
emptyDir: {}
restartPolicy: Always

View File

@@ -0,0 +1,26 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bookwyrm
namespace: bookwyrm
annotations:
external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }}
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
external-dns.alpha.kubernetes.io/ttl: "60"
spec:
ingressClassName: traefik
rules:
- host: {{ .domain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: bookwyrm
port:
number: 80
tls:
- hosts:
- {{ .domain }}
secretName: {{ .tlsSecretName }}

View File

@@ -0,0 +1,17 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: {{ .namespace }}
labels:
- includeSelectors: true
pairs:
app: bookwyrm
managedBy: kustomize
partOf: wild-cloud
resources:
- namespace.yaml
- deployment.yaml
- deployment-worker.yaml
- service.yaml
- ingress.yaml
- pvc.yaml
- db-init-job.yaml

View File

@@ -0,0 +1,30 @@
version: 0.8.7-1
requires:
- name: postgres
- name: redis
- name: smtp
defaultConfig:
namespace: bookwyrm
externalDnsDomain: '{{ .cloud.domain }}'
domain: bookwyrm.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls
storage: 2Gi
db:
host: '{{ .apps.postgres.host }}'
port: '{{ .apps.postgres.port }}'
name: bookwyrm
user: bookwyrm
redis:
host: '{{ .apps.redis.host }}'
smtp:
host: '{{ .apps.smtp.host }}'
port: '{{ .apps.smtp.port }}'
from: '{{ .apps.smtp.from }}'
user: '{{ .apps.smtp.user }}'
defaultSecrets:
- key: secretKey
- key: dbPassword
- key: smtpPassword
requiredSecrets:
- postgres.password
- redis.password

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: {{ .namespace }}

View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: bookwyrm-images
namespace: bookwyrm
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .storage }}

View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: bookwyrm
namespace: bookwyrm
spec:
selector:
component: web
ports:
- name: http
port: 80
targetPort: 8000
protocol: TCP