Deployment guide¶
How to run Bibliogon in production. Operator-side reference covering Docker Compose, environment variables, persistence, and common troubleshooting. For end-user installation use the installer scripts; this page is for the underlying setup.
What runs in production¶
Bibliogon ships as two Docker containers behind one port:
- backend — Python + FastAPI + SQLAlchemy + SQLite. Runs Uvicorn with 2 workers. Health endpoint at
/api/health. - frontend — Vite-built static files served by nginx. Proxies
/api/*to the backend on the internal Docker network.
The frontend container exposes port 7880 by default (override with BIBLIOGON_PORT). The backend container is internal only — you should not expose port 8000 to the public internet.
The compose file is docker-compose.prod.yml. Source: https://github.com/astrapi69/bibliogon/blob/main/docker-compose.prod.yml.
Quickstart¶
git clone https://github.com/astrapi69/bibliogon.git
cd bibliogon
./start.sh
start.sh generates a .env file with secrets if it doesn't exist, then runs docker compose -f docker-compose.prod.yml up -d. Open http://localhost:7880. Stop with ./stop.sh.
Environment variables¶
| Variable | Default | Purpose |
|---|---|---|
BIBLIOGON_PORT |
7880 |
Host port the frontend binds to. Change when 7880 is taken or when you front Bibliogon with a reverse proxy on a different port. |
BIBLIOGON_DEBUG |
false |
When true, enables /api/test/reset and the API docs at /api/docs and includes stacktraces in 5xx responses. Do not enable in production. |
BIBLIOGON_SECRET_KEY |
(auto-generated by start.sh) |
Used for license signing and CSRF protection. The startup script writes a random value to .env if not set. |
BIBLIOGON_CREDENTIALS_SECRET |
(auto-generated by start.sh) |
Fernet-encrypts API keys + service-account files at rest in the DB. The same auto-generation applies. |
BIBLIOGON_CORS_ORIGINS |
http://localhost:7880 |
Comma-separated list of allowed origins. Add your reverse-proxy hostname here when deploying behind a domain. |
BIBLIOGON_DATA_DIR |
/app/data (in container) |
Container-side root for runtime data: SQLite DB at <dir>/bibliogon.db and uploads at <dir>/uploads/. Mapped to a Docker named volume for persistence. |
BIBLIOGON_DB_PATH |
(no longer honoured) | Removed in v0.30.0 (DEP-DBPATH-01 step 3). The variable has no effect on path resolution; if still set in the environment, a single warning is logged at startup naming the ignored value. Set BIBLIOGON_DATA_DIR instead — the database resolves to <BIBLIOGON_DATA_DIR>/bibliogon.db. Deprecation timeline: warning v0.27.0, precedence flip v0.28.0, removal v0.30.0. |
Every variable that has a default is optional. The startup script auto-generates the two secrets if they are not already in .env, so a fresh ./start.sh works without any setup.
Persistence¶
Production data lives in a named Docker volume, bibliogon-data, mounted at /app/data inside the backend container.
- SQLite DB:
/app/data/bibliogon.db(+-wal,-shm). - Uploads (cover images, asset files):
/app/data/uploads/. - Audiobook persistence (post-export MP3s):
/app/data/uploads/{book_id}/audiobook/.
The volume survives container rebuilds. To verify it exists:
docker volume ls | grep bibliogon
To inspect contents (ephemeral container):
docker run --rm -v bibliogon-data:/data alpine ls -la /data
To back up the volume:
docker run --rm -v bibliogon-data:/data -v "$PWD":/backup alpine tar czf /backup/bibliogon-data.tar.gz -C /data .
To restore:
docker run --rm -v bibliogon-data:/data -v "$PWD":/backup alpine tar xzf /backup/bibliogon-data.tar.gz -C /data
The .bgb backup format the Bibliogon UI uses is a different, app-level concern — that's a per-book ZIP that survives Docker volume loss. The volume backup above covers everything: every book, every asset, every audiobook MP3, plus state like installed plugins.
Stop / restart / uninstall¶
./stop.sh # stop containers
./start.sh # start (or restart) containers
docker compose -f docker-compose.prod.yml restart # restart without rebuild
docker compose -f docker-compose.prod.yml down -v # stop + DELETE volume (uninstalls Bibliogon and ALL data)
The -v flag removes the named volume. Without it, the data persists across docker compose down and survives image rebuilds. Use -v only when you intend to delete all books, assets, and audiobook MP3s.
Logs¶
docker compose -f docker-compose.prod.yml logs -f # all services
docker compose -f docker-compose.prod.yml logs -f backend # backend only
docker compose -f docker-compose.prod.yml logs -f --tail=100 # last 100 lines, follow
The backend logs every API request (Uvicorn access log) and every plugin lifecycle event. Errors at WARNING/ERROR level include structured context (book_id, plugin name, etc.).
Reverse proxy¶
If you put Bibliogon behind nginx / Caddy / Traefik / Apache, two things to remember:
- Forward
/api/*to the same upstream — the frontend container already proxies/api/*internally, so you can either point your reverse proxy at port7880(treats Bibliogon as a single black box) or split it (route/to frontend container,/api/*to backend container directly). The single-port approach is simpler and matches the default deployment. - Add your hostname to
BIBLIOGON_CORS_ORIGINS. Without this, browsers block the frontend from reaching the backend with a CORS error. Comma-separated list, e.g.BIBLIOGON_CORS_ORIGINS=https://books.example.com,https://localhost:7880.
For HTTPS termination, do it at the reverse proxy. Bibliogon's containers do not handle TLS.
Backups¶
Two complementary mechanisms:
- App-level
.bgbper book — exported through the Bibliogon UI (Settings → Backup). Covers a single book, its chapters, its assets, optionally its audiobook MP3s. Portable across Bibliogon instances. - Volume-level backup — covers the entire Bibliogon state including every book + plugin state + installed plugins. The tar command above. Suitable for nightly cron jobs.
The optional git-sync plugin can also push each book to a separate git repo, giving you per-book version control + an off-site backup target. See the Git Backup help page.
Updates¶
To update to a new Bibliogon release:
cd ~/bibliogon
git pull origin main
git checkout vX.Y.Z # the new release tag
./stop.sh
./start.sh # rebuilds the images at the new tag
The launcher binary (Windows Launcher, macOS Launcher, Linux Launcher) wraps this lifecycle in a tray-icon UI with auto-update detection. For server deployments, the explicit shell flow above is more predictable.
The lock-step versioning model means there is never a partial upgrade — backend, frontend, and every plugin always ship at the same version. Pull a tag, restart, done.
Data migrations¶
Bibliogon uses Alembic for schema migrations. The FastAPI lifespan runs alembic upgrade head on startup, so a fresh ./start.sh after pulling a new tag applies any pending migrations. The migrations are idempotent and forward-only.
For the v0.25.0+ filesystem migration (data moved from the project tree to platformdirs / BIBLIOGON_DATA_DIR), the FastAPI lifespan auto-migrates legacy paths on first start and writes a .migrated-YYYY-MM-DD breadcrumb at each old location. Confirm the move before deleting the old files manually.
Troubleshooting¶
Containers do not start¶
docker compose -f docker-compose.prod.yml logs backend | tail -30
docker compose -f docker-compose.prod.yml logs frontend | tail -30
The most common causes are:
- Port 7880 already taken by another service. Set
BIBLIOGON_PORT=7881(or another free port) in.env. BIBLIOGON_SECRET_KEYmissing —start.shshould generate one; if it failed silently, check that.envhas bothBIBLIOGON_SECRET_KEY=andBIBLIOGON_CREDENTIALS_SECRET=lines with non-empty values.- Disk full — the bibliogon-data volume needs space for the SQLite DB and uploads.
df -handdocker system dfboth worth checking.
Backend health check fails¶
The /api/health endpoint returns 200 when the DB is reachable and the plugin manager has loaded successfully. If health stays unhealthy after 5+ minutes:
docker compose -f docker-compose.prod.yml exec backend python -c "from app.database import engine; print(engine.url)"
docker compose -f docker-compose.prod.yml exec backend ls -la /app/data/
If /app/data/bibliogon.db exists but the DB query fails, the SQLite file may be corrupted. Stop, restore from the most recent volume backup, restart.
Plugin not loading¶
docker compose -f docker-compose.prod.yml logs backend | grep -i plugin
Plugin discovery uses importlib.metadata.entry_points(). A plugin that fails to import is logged at ERROR with the import error, then skipped. Other plugins continue to load. Common cause: missing dependency in the plugin's pyproject.toml that wasn't bundled in the container.
CORS errors in the browser console¶
Add the hostname to BIBLIOGON_CORS_ORIGINS. The default http://localhost:7880 only allows the local-machine front-end. Behind a reverse proxy at books.example.com, set BIBLIOGON_CORS_ORIGINS=https://books.example.com.
Last verified for v0.29.0 (2026-05-07).