Kuzu to PostgreSQL migration
MyBibliotheca 3.0.0 uses PostgreSQL as its primary database. A fresh install starts directly on PostgreSQL. An existing installation with a Kuzu database continues using Kuzu until an administrator finishes the in-app migration wizard.
Updating the app does not migrate your library. Keep the Kuzu database unchanged throughout migration.
Update your Compose setup
Required before migrating: update your Compose setup
The 3.0.0 production Compose file is different from the Kuzu-only setup: it adds a PostgreSQL service and the postgres_data volume, and pins the app to pickles4evaaaa/mybibliotheca:3.0.0. You must use the 3.0.0 Compose file before running the migration.
- If you installed from a Git checkout and did not replace
docker-compose.yml, update the checkout; the new file will be included. - If you use a copied or customized Compose file, replace it with the 3.0.0 file or manually add the PostgreSQL service, app image tag, and PostgreSQL connection settings from the repository Compose file. Keep your custom ports and proxy settings. Preserve the
./data:/app/datamount. - Keep your existing
SECRET_KEYandSECURITY_PASSWORD_SALTvalues. Runpython3 scripts/setup_docker.pyto fill any missing required settings; it preserves non-empty values. If you already have an initialized PostgreSQL data volume, restore its originalPOSTGRES_PASSWORDbecause changing.envdoes not change the password stored in that database. - Do not run
docker compose down -v, removedata/, or delete thepostgres_datavolume. The upgrade createspostgres_datawhile retaining your existing Kuzu files.
3.0.0 Compose file
Use this as docker-compose.yml for the standard deployment. If you have custom ports, networks, or proxy labels, carry those settings into this file while keeping the PostgreSQL service, app image tag, and persistent data mounts.
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB:-bibliotheca}
POSTGRES_USER: ${POSTGRES_USER:-bibliotheca}
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?Required setting is missing. Run: python3 scripts/setup_docker.py}"
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
interval: 10s
timeout: 5s
retries: 10
start_period: 20s
restart: unless-stopped
redis:
image: redis:7.4-alpine
command: ["redis-server", "--save", "", "--appendonly", "no", "--maxmemory", "256mb", "--maxmemory-policy", "allkeys-lru"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
restart: unless-stopped
bibliotheca:
image: pickles4evaaaa/mybibliotheca:3.0.0
ports:
- "5054:5054"
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
volumes:
# Mount local data directory for migration support, Kuzu database, and backups
- ./data:/app/data
# NOTE: Do NOT bind-mount the static directory on macOS.
# Docker Desktop's osxfs can trigger EDEADLK (Resource deadlock avoided)
environment:
# Security - MUST be set via .env file or environment
SECRET_KEY: "${SECRET_KEY:?Required setting is missing. Run: python3 scripts/setup_docker.py}"
SECURITY_PASSWORD_SALT: "${SECURITY_PASSWORD_SALT:?Required setting is missing. Run: python3 scripts/setup_docker.py}"
# New installs initialize on PostgreSQL. Existing Kuzu installs remain
# on Kuzu until the administrator completes the migration wizard.
KUZU_DB_PATH: /app/data/kuzu
GRAPH_DATABASE_ENABLED: "true"
# Application settings
SITE_NAME: ${SITE_NAME:-MyBibliotheca}
TIMEZONE: ${TIMEZONE:-UTC}
# PostgreSQL defaults to six workers; the entrypoint keeps Kuzu at one.
WORKERS: ${WORKERS:-6}
# Central log level (controls Gunicorn and Flask loggers)
LOG_LEVEL: ${LOG_LEVEL}
# Logging settings - disable verbose initialization to prevent duplicate messages
MYBIBLIOTHECA_VERBOSE_INIT: ${MYBIBLIOTHECA_VERBOSE_INIT:-false}
# Optional: use your own Google Books quota for metadata lookups/imports.
# OpenLibrary remains available when this is unset.
GOOGLE_BOOKS_API_KEY: ${GOOGLE_BOOKS_API_KEY:-}
OPENLIBRARY_CONTACT_EMAIL: ${OPENLIBRARY_CONTACT_EMAIL:-}
# Shared Redis cache. The same cache backend remains enabled after the
# PostgreSQL cutover; Kuzu query caching is transitional.
CACHE_REDIS_URL: redis://redis:6379/1
CACHE_REDIS_PREFIX: ${CACHE_REDIS_PREFIX:-mybibliotheca}
KUZU_REDIS_CACHE_ENABLED: "true"
KUZU_REDIS_CACHE_TTL_SECONDS: ${KUZU_REDIS_CACHE_TTL_SECONDS:-10}
# Primary database for new installs and migration target for Kuzu upgrades.
POSTGRES_DSN: ${POSTGRES_DSN:-}
POSTGRES_HOST: ${POSTGRES_HOST:-postgres}
POSTGRES_PORT: ${POSTGRES_PORT:-5432}
POSTGRES_DB: ${POSTGRES_DB:-bibliotheca}
POSTGRES_USER: ${POSTGRES_USER:-bibliotheca}
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?Required setting is missing. Run: python3 scripts/setup_docker.py}"
# Migration settings (DISABLED for manual migration)
AUTO_MIGRATE: "false"
# - MIGRATION_DEFAULT_USER=admin
restart: unless-stopped
volumes:
postgres_data:
Prepare
- Schedule a maintenance window and ask users to stop making changes.
- Copy the complete application
data/directory to a safe location. Back up any existing PostgreSQL storage too. - Update the checkout/Compose file as described above, then pull and start the new services:
git pull
docker compose pull
docker compose up -d
docker compose ps
- Confirm the
postgresservice is healthy and the app is running. Sign in as an administrator and open Admin → Database Migration.
Complete the wizard
- Create and verify Kuzu backup. The app pauses Kuzu access, archives the database and related application files, and checks the archive. Download the backup and keep it with your independent copy.
- Check PostgreSQL. The wizard verifies connectivity and inventories the source. The target must not contain application data. If it is not empty, configure an empty target; do not truncate it manually.
- Migrate and verify. Start the migration during the maintenance window. The app copies the graph and relational data and checks both. Leave the page open while the migration runs.
- Confirm cutover. When verification passes, the app activates PostgreSQL. Confirm the wizard reports PostgreSQL as active. If running outside the supplied Gunicorn container, restart the application when prompted.
- Check the library. Verify user accounts, books, reading history, covers, and integrations before normal use resumes.
If a check fails, follow the blocker reported by the wizard. Kuzu remains active until a verified cutover. Do not edit the persistence state files by hand.
After migration
Keep the original Kuzu files and backups until you are satisfied with the PostgreSQL library. PostgreSQL becomes authoritative after cutover. The old Kuzu copy will not receive later writes, so use a verified PostgreSQL backup for recovery after normal use resumes.
Keep backing up both the PostgreSQL volume and data/. See Backups & restore and Install & upgrade.