# Production stack: PostgreSQL + API + Frontend + Caddy (TLS-ready) # # Quick start: # cp .env.prod.example .env.prod # # edit .env.prod — set PROMPTLENS_API_KEY and PROMPTLENS_SITE_ADDRESS # docker compose --env-file .env.prod -f docker-compose.prod.yml up --build -d # # Local HTTP (no TLS): # PROMPTLENS_SITE_ADDRESS=:80 docker compose --env-file .env.prod -f docker-compose.prod.yml up --build # # Public HTTPS: # PROMPTLENS_SITE_ADDRESS=dashboard.example.com ACME_EMAIL=you@example.com ... services: db: image: postgres:16-alpine container_name: promptlens-db environment: POSTGRES_USER: ${POSTGRES_USER:-promptlens} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env.prod} POSTGRES_DB: ${POSTGRES_DB:-promptlens} volumes: - promptlens_pg:/var/lib/postgresql/data restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-promptlens}"] interval: 10s timeout: 5s retries: 5 api: build: context: ./backend dockerfile: Dockerfile container_name: promptlens-api depends_on: db: condition: service_healthy environment: PROMPTLENS_ENVIRONMENT: production PROMPTLENS_API_KEY: ${PROMPTLENS_API_KEY:?Set PROMPTLENS_API_KEY in .env.prod} PROMPTLENS_DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-promptlens}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-promptlens} PROMPTLENS_RUN_MIGRATIONS_ON_STARTUP: "true" PROMPTLENS_LOG_FORMAT: json PROMPTLENS_CORS_ORIGINS: ${PROMPTLENS_CORS_ORIGINS} restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/api/v1/health"] interval: 15s timeout: 5s retries: 5 start_period: 20s frontend: build: context: ./Frontend dockerfile: Dockerfile args: VITE_PROMPTLENS_API_URL: ${VITE_PROMPTLENS_API_URL:-/api/v1} VITE_PROMPTLENS_API_KEY: ${VITE_PROMPTLENS_API_KEY:-} container_name: promptlens-frontend depends_on: api: condition: service_healthy environment: NODE_ENV: production HOST: 0.0.0.0 PORT: 3000 PROMPTLENS_INTERNAL_API_URL: http://api:8000/api/v1 restart: unless-stopped healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:3000/"] interval: 15s timeout: 5s retries: 5 start_period: 30s caddy: image: caddy:2-alpine container_name: promptlens-caddy depends_on: api: condition: service_healthy frontend: condition: service_healthy ports: - "${PROMPTLENS_HTTP_PORT:-80}:80" - "${PROMPTLENS_HTTPS_PORT:-443}:443" environment: PROMPTLENS_SITE_ADDRESS: ${PROMPTLENS_SITE_ADDRESS:-:80} ACME_EMAIL: ${ACME_EMAIL:-} volumes: - ./docker/Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config restart: unless-stopped volumes: promptlens_pg: caddy_data: caddy_config: