# Self-hosted deployment for mitbringsl. # # cp .env.example .env # fill in secrets + domains # docker compose up -d --build # # Services: # caddy reverse proxy (auto-HTTPS by default; see "Behind a Reverse Proxy" below) # backend mitbringsl Go API (image builds server + migrate binaries) # migrate one-shot migration runner, must finish before backend starts # db PostgreSQL 16 name: mitbringsl x-backend-image: &backend-image image: mitbringsl-backend build: context: ../backend dockerfile: Dockerfile x-app-env: &appenv APP_ENV: ${APP_ENV:-production} LOG_LEVEL: ${LOG_LEVEL:-info} DATABASE_URL: postgres://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-appdb}?sslmode=disable services: db: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_USER: ${POSTGRES_USER:-app} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} POSTGRES_DB: ${POSTGRES_DB:-appdb} volumes: - pgdata:/var/lib/postgresql/data - ./db/init:/docker-entrypoint-initdb.d:ro # runs on first init only healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-app} -d ${POSTGRES_DB:-appdb}"] interval: 10s timeout: 5s retries: 5 start_period: 30s networks: [appnet] # Only expose the DB to the host for local debugging; remove in prod. # ports: # - "5432:5432" migrate: <<: *backend-image restart: "no" command: ["/app/migrate", "up"] environment: <<: *appenv depends_on: db: condition: service_healthy networks: [appnet] backend: <<: *backend-image restart: unless-stopped environment: <<: *appenv HTTP_ADDR: ":8080" PUBLIC_BASE_URL: ${PUBLIC_BASE_URL:-http://localhost:8080} SESSION_TOKEN_TTL: ${SESSION_TOKEN_TTL:-720h} AUTH_PASSWORD_ENABLED: ${AUTH_PASSWORD_ENABLED:-true} # OIDC (all optional) OIDC_GOOGLE_ENABLED: ${OIDC_GOOGLE_ENABLED:-false} OIDC_GOOGLE_CLIENT_ID: ${OIDC_GOOGLE_CLIENT_ID:-} OIDC_GOOGLE_ISSUER: ${OIDC_GOOGLE_ISSUER:-https://accounts.google.com} OIDC_GENERIC_ENABLED: ${OIDC_GENERIC_ENABLED:-false} OIDC_GENERIC_ISSUER: ${OIDC_GENERIC_ISSUER:-} OIDC_GENERIC_CLIENT_ID: ${OIDC_GENERIC_CLIENT_ID:-} OIDC_GENERIC_DISPLAY_NAME: ${OIDC_GENERIC_DISPLAY_NAME:-OIDC} CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-} expose: ["8080"] depends_on: db: condition: service_healthy migrate: condition: service_completed_successfully networks: [appnet] caddy: image: caddy:2-alpine restart: unless-stopped # --- Standalone mode (default): Caddy handles TLS via Let's Encrypt ---- # Exposes ports 80 (ACME HTTP-01 challenge) and 443 (HTTPS + HTTP/3). ports: - "${CADDY_HTTP_PORT:-80}:80" - "${CADDY_HTTPS_PORT:-443}:443" - "${CADDY_HTTPS_PORT:-443}:443/udp" # HTTP/3 # --- Behind a Reverse Proxy mode ---------------------------------------- # If your own proxy handles TLS, set CADDY_HTTP_PORT to the port your proxy # forwards to (e.g. 8880), and mount Caddyfile.behind-proxy instead: # # CADDY_HTTP_PORT=8880 # volumes: # - ./Caddyfile.behind-proxy:/etc/caddy/Caddyfile:ro # # Also remove the CADDY_HTTPS_PORT ports above and comment out this note. # ------------------------------------------------------------------------- volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config depends_on: - backend networks: [appnet] volumes: pgdata: caddy_data: caddy_config: networks: appnet: