name: provenance # One stack stands up the whole system. Configuration is entirely env-driven # (see .env.example). Run from this directory: `docker compose up -d`. # # backend/frontend are PULLED from the public registry (git.jpaul.io); CI pushes # them to the LAN endpoint (192.168.0.2:1234). For local building instead of # pulling, layer the dev override: # docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build services: postgres: # pgvector image = Postgres + pgvector; pg_trgm ships in contrib. image: pgvector/pgvector:pg17 environment: POSTGRES_USER: ${POSTGRES_USER:-provenance} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-provenance} POSTGRES_DB: ${POSTGRES_DB:-provenance} volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-provenance} -d ${POSTGRES_DB:-provenance}"] interval: 5s timeout: 5s retries: 10 restart: unless-stopped minio: image: minio/minio:latest command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:-provenance} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-change-me-too} volumes: - miniodata:/data healthcheck: test: ["CMD-SHELL", "mc ready local || exit 1"] interval: 10s timeout: 5s retries: 10 restart: unless-stopped backend: image: git.jpaul.io/justin/provenance-backend:${IMAGE_TAG:-test-main} labels: com.centurylinklabs.watchtower.enable: "true" environment: APP_ENV: ${APP_ENV:-development} DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://provenance:provenance@postgres:5432/provenance} depends_on: postgres: condition: service_healthy healthcheck: test: - CMD-SHELL - >- python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8000/health').status==200 else 1)" interval: 10s timeout: 5s retries: 5 start_period: 20s restart: unless-stopped frontend: image: git.jpaul.io/justin/provenance-frontend:${IMAGE_TAG:-test-main} labels: com.centurylinklabs.watchtower.enable: "true" environment: NODE_ENV: production depends_on: - backend restart: unless-stopped caddy: image: caddy:2 ports: - "80:80" - "443:443" environment: # Local default ':80' -> http://localhost. Set to a domain in production # for automatic HTTPS (or run plain HTTP behind a Cloudflare Tunnel). PROVENANCE_SITE_ADDRESS: ${PROVENANCE_SITE_ADDRESS:-:80} volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddydata:/data - caddyconfig:/config depends_on: - backend - frontend restart: unless-stopped # Cloudflare Tunnel connector. The tunnel/ingress is configured in the # Cloudflare dashboard; this container just connects. One public hostname # (e.g. provenance.paul.farm) -> http://caddy:80 is enough, because Caddy # does the internal path routing (/ -> frontend, /api + /health -> backend). # # Opt-in via the "tunnel" profile so local dev doesn't start it. On the lab # host set COMPOSE_PROFILES=tunnel so `docker compose up -d` includes it. cloudflared: image: cloudflare/cloudflared:latest restart: unless-stopped command: tunnel --no-autoupdate run environment: TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN:-} depends_on: - caddy profiles: - tunnel # Auto-deploy: watch the label-enabled app containers (backend, frontend), # poll the registry every 2 minutes, and recreate on a new :test-main digest. # Scoped by label so it never touches Postgres/MinIO/Caddy. Registry creds come # from the host docker config (the `docker login git.jpaul.io` on the host). # Opt-in via the "watchtower" profile. watchtower: image: containrrr/watchtower:latest restart: unless-stopped command: --label-enable --cleanup --interval 120 volumes: - /var/run/docker.sock:/var/run/docker.sock - ${HOME:-/root}/.docker/config.json:/config.json:ro profiles: - watchtower volumes: pgdata: miniodata: caddydata: caddyconfig: