Deployment Overview
Campbooks deploys as a self-contained Docker Compose stack. docker compose up runs the whole app on a single server — there's no Node build, no Redis, and no external services to wire up beyond a TLS-terminating reverse proxy.
Want the step-by-step? This page is the lay of the land. The Self-Hosting guide walks through a real production server end to end.
The stack
docker compose up starts three containers:
| Service | What it is |
|---|---|
postgres | PostgreSQL with the pgvector extension (required — it holds app data, cache, jobs, Action Cable, and vector embeddings) |
web | The Rails app — Puma behind Thruster |
worker | Background jobs — email scanning, AI, indexing, calendar sync (Solid Queue) |
A fourth service, opensearch, is optional (enabled with the search profile) and only powers contact full-text autocomplete. Everything else — email search, document search, the Cmd+K palette — uses Postgres + pgvector.
Uploaded files (email attachments saved as Documents) live on a local Docker volume by default, or in S3-compatible object storage if you configure it.
What you need
- A server with Docker and the Docker Compose plugin (Docker Engine ≥ 24)
- About 2 GB RAM for the default stack (add ~1 GB if you enable OpenSearch)
- A domain name and a reverse proxy that terminates TLS (Caddy, Traefik, or nginx)
Configuration
Everything is configured through environment variables in .env. bin/generate-secrets fills the required secrets; the rest are optional and each unlocks one integration. The annotated list lives in .env.example.
| Variable | Required | Purpose |
|---|---|---|
SECRET_KEY_BASE | Yes | Rails session/signing key |
ACTIVE_RECORD_PRIMARY_KEY | Yes | Encrypts OAuth tokens & AI keys at rest |
ACTIVE_RECORD_DETERMINISTIC_KEY | Yes | As above |
ACTIVE_RECORD_KEY_DERIVATION_SALT | Yes | As above |
CAMPBOOKS_DATABASE_PASSWORD | Yes | Password for the bundled Postgres |
APP_HOST | Yes | The hostname you reach the app on (must match the Host header) |
FORCE_SSL | Yes | true behind a TLS proxy, false for plain-HTTP local use |
SELF_HOSTED | Yes | 1 — open registration + reads AI keys from env |
You don't need config/master.key. Self-hosting reads SECRET_KEY_BASE from the environment. Generate the required secrets with bin/generate-secrets.
Going to production
- Point a domain at the server and set
APP_HOST,FORCE_SSL=true, andWEB_PORTin.env. - Put a TLS-terminating reverse proxy in front (with Caddy it's a two-line config — automatic HTTPS).
docker compose up -d --build. The entrypoint runs database migrations on boot.
Never expose the app over plain HTTP on the internet. With FORCE_SSL=false sessions and cookies travel unencrypted. Always sit behind a TLS proxy in production.
Updates
git pull
docker compose up -d --build # the entrypoint runs migrations on boot
Back up the database first.
Health check
Campbooks exposes a health endpoint at /up (returns 200 when healthy). Use it for load-balancer probes and monitoring — it stays reachable over plain HTTP even with FORCE_SSL=true.
See the Self-Hosting guide for the full walkthrough, including backups and every integration.