Skip to main content
The repository ships a production-ready docker-compose.yml that runs three services on one box:
This single-box setup is suitable for most early-stage and growth-stage deployments. For high-availability requirements, move Postgres to a managed provider (Neon, RDS, Cloud SQL) and deploy the API container to a managed compute platform.

Dockerfile

The image uses a two-stage build to minimize the final image size. The runtime stage runs as a non-root user (appuser) for security.
Build and run the image standalone:

docker-compose.yml

Caddyfile

Edit Caddyfile and replace api.yourdomain.com with your real subdomain before starting:
Caddy handles certificate provisioning and renewal automatically — no certbot, no cron jobs.

Deploying the stack

1

Provision your server and point DNS

Create an Ubuntu 22.04 / Debian 12 VM (Hetzner CX22 is ~€5/month). Add an A record pointing api.yourdomain.com to the server’s public IP. Let the record propagate before starting Caddy.
2

Install Docker

3

Clone the repository

4

Configure secrets

Edit .env — at minimum set:
Generate secrets:
5

Edit the Caddyfile

Replace api.yourdomain.com in Caddyfile with your actual subdomain.
6

Start the stack

On first start, db initialises, then api starts once the database health check passes, then caddy begins serving HTTPS.
7

Load the database schema

After the stack is running, load the schema into the Postgres container:
Then apply migrations:
See Database setup for the full guide, including the required causeloop_app role.
8

Verify

The db service in docker-compose.yml runs as POSTGRES_USER=causeloop, which is the Postgres superuser for that database. Row-Level Security is bypassed by superusers. Before using the database with real tenant data, create a dedicated causeloop_app role with NOSUPERUSER / NOBYPASSRLS and set DATABASE_URL to connect as that role. See Database setup — RLS two-role model.

Kubernetes

The Docker image works on any Kubernetes cluster. Use the /health and /ready endpoints as probes:
Push the image to your registry:
Supply environment variables via a Secret (for JWT_SECRET, CAUSELOOP_MASTER_KEY, DATABASE_URL) and a ConfigMap (for PORT, CORS_ORIGINS, LLM_PROVIDER_ORDER).

Updating

Caddy and Postgres do not need to be restarted for application updates.