# Pablan customer deployment: postgres + backend + frontend + reverse proxy. # Copy .env.example to .env, set POSTGRES_PASSWORD, PABLAN_DOMAIN and the LLM # endpoints, then: docker compose up -d --build name: pablan services: postgres: image: pgvector/pgvector:pg18 environment: POSTGRES_USER: ${POSTGRES_USER:-pablan} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env} POSTGRES_DB: ${POSTGRES_DB:-pablan} volumes: # PG18 images expect the mount at /var/lib/postgresql (not .../data) - postgres_data:/var/lib/postgresql healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 5s timeout: 5s retries: 10 restart: unless-stopped backend: build: ./backend # Exactly ONE worker — the in-process job queue and metrics registry # assume a single process (docs/architecture.md). Do not raise this; # scaling is the Variant B worker split. command: [ "uv", "run", "--no-sync", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1", ] env_file: .env environment: PABLAN_DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-pablan}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-pablan} # Production posture by default, even if .env carries dev values. PABLAN_ENV: ${PABLAN_ENV:-production} PABLAN_COOKIE_SECURE: ${PABLAN_COOKIE_SECURE:-true} PABLAN_TEMPLATES_DIR: /app/templates volumes: # Built-in interview templates (product content, outside the image). - ./templates:/app/templates:ro depends_on: postgres: condition: service_healthy restart: unless-stopped frontend: build: ./frontend environment: ORIGIN: https://${PABLAN_DOMAIN:?set PABLAN_DOMAIN in .env} # SvelteKit server-side auth check goes straight to the backend # container instead of looping through the public proxy. PABLAN_INTERNAL_API_BASE: http://backend:8000 restart: unless-stopped caddy: image: caddy:2 ports: - "80:80" - "443:443" - "443:443/udp" environment: PABLAN_DOMAIN: ${PABLAN_DOMAIN} volumes: - ./deploy/caddy/Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config depends_on: - backend - frontend restart: unless-stopped volumes: postgres_data: caddy_data: caddy_config: