Deploying Provenance
This directory is the self-host stack: docker-compose.yml (Postgres + MinIO +
Caddy + backend + worker + frontend, with a one-shot Alembic migrate job),
Caddyfile for the edge, .env.example for configuration, and backup.sh /
BACKUP.md for backup and restore.
Run it
cp .env.example .env # fill in secrets, OWNER_EMAIL, APP_ENV=production
docker compose up -d # pulls backend/frontend images from git.jpaul.io
Images are pulled from the public git.jpaul.io registry (CI pushes them to
the LAN endpoint 192.168.0.2:1234; see CLAUDE.md). To build locally instead,
layer the dev override:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
Caddy's site address is env-driven (PROVENANCE_SITE_ADDRESS): :80 for plain
HTTP behind a tunnel or for http://localhost, or a domain for automatic HTTPS.
Nothing in this repo hard-codes a hostname.
Note that a Watchtower image swap recreates only the long-running containers,
not the one-shot migrate job — pair auto-deploys with a docker compose up
so migrations re-run.
The maintainer's instance
Justin's own deployment — the one holding the Paul/Reier family tree:
| URL | https://provenance.paul.farm |
| Host | 192.168.0.2 (the fleet host; also the LAN registry endpoint) |
| Ingress | Cloudflare Tunnel → caddy:80; Cloudflare terminates TLS, so PROVENANCE_SITE_ADDRESS stays :80 |
| Owner account | [email protected] |
| Deploys | CI builds on merge to main; the host's global Watchtower swaps the test-main images |
Health check (no auth): curl https://provenance.paul.farm/health.
It reports "env":"production" — the host's .env sets APP_ENV=production
(corrected 2026-09-20; it had been left at the .env.example default). app_env
is informational: /health and /api/v1/admin report it, nothing branches on it.