diff --git a/.env.example b/.env.example index ba56666..392f746 100644 --- a/.env.example +++ b/.env.example @@ -16,6 +16,9 @@ DATABASE_URL=postgres://eventsnap:CHANGE_ME_use_a_strong_password@db:5432/events POSTGRES_USER=eventsnap POSTGRES_PASSWORD=CHANGE_ME_use_a_strong_password POSTGRES_DB=eventsnap +# Connection pool size. Default 10. For a busy event (~100 guests polling the feed +# + SSE + uploads at once) raise to ~30 so requests don't queue on a pool permit. +DATABASE_MAX_CONNECTIONS=30 # ── Authentication ──────────────────────────────────────────────────────────── # Generate with: openssl rand -hex 64 @@ -40,19 +43,25 @@ MEDIA_PATH=/media # /media is publicly served, so exports here would be downloadable without auth. EXPORT_PATH=/exports -# ── Upload limits ───────────────────────────────────────────────────────────── -DEFAULT_MAX_IMAGE_SIZE_MB=20 -DEFAULT_MAX_VIDEO_SIZE_MB=500 - -# ── Rate limiting ───────────────────────────────────────────────────────────── -DEFAULT_UPLOAD_RATE_PER_HOUR=10 -DEFAULT_FEED_RATE_PER_MIN=60 -DEFAULT_EXPORT_RATE_PER_DAY=3 - -# ── Capacity ────────────────────────────────────────────────────────────────── -DEFAULT_ESTIMATED_GUEST_COUNT=100 -# Fraction of total storage that triggers the "low storage" warning (0.0–1.0) -DEFAULT_QUOTA_TOLERANCE=0.75 +# ── Runtime settings (upload limits, rate limits, capacity) ─────────────────── +# NOTE: These are NOT environment variables. Upload size caps, rate limits, guest +# count and quota tolerance are stored in the database `config` table (seeded once +# at first boot) and changed at runtime from the ADMIN DASHBOARD — the backend does +# not read them from .env. Setting them here has no effect. Current seeded defaults: +# upload rate 100 / hour / guest (raised from 10 by migration 015) +# feed rate 60 / minute +# export rate 3 / day +# max image size 20 MB +# max video size 500 MB +# estimated guests 100 +# quota tolerance 0.75 (fraction of disk that triggers the low-storage warning) +# Adjust these in the admin UI before the event if needed. # ── Workers ─────────────────────────────────────────────────────────────────── +# Number of parallel image/video compression workers. Default 2. This is the main +# throughput bottleneck: with 2 workers a burst of uploads can take ~5s to appear. +# For a large event (100+ guests) 4 is a good target — but each worker can run an +# ffmpeg transcode, so if you raise this ALSO raise the app container's memory limit +# in docker-compose.yml (`app.deploy.resources.limits.memory`) from 1G to ~2G, or a +# burst of large videos can OOM the box and take Postgres down with it. COMPRESSION_WORKER_CONCURRENCY=2 diff --git a/docker-compose.yml b/docker-compose.yml index 8cb810a..7e26de9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,7 +46,11 @@ services: expose: - "3000" healthcheck: - test: ["CMD-SHELL", "wget -q -O- http://localhost:3000/health || exit 1"] + # Use 127.0.0.1, NOT localhost: the app binds IPv4 (0.0.0.0) but `localhost` + # resolves to ::1 (IPv6) first inside the container, so a localhost probe gets + # "connection refused" and the container never turns healthy — which would leave + # Caddy (gated on `condition: service_healthy` below) blocked forever on boot. + test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:3000/health || exit 1"] interval: 10s timeout: 5s retries: 5 @@ -73,7 +77,9 @@ services: expose: - "3001" healthcheck: - test: ["CMD-SHELL", "wget -q -O- http://localhost:3001/ >/dev/null 2>&1 || exit 1"] + # 127.0.0.1, not localhost — see the app healthcheck note above (IPv4 bind vs + # ::1 resolution would leave this container permanently unhealthy). + test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:3001/ >/dev/null 2>&1 || exit 1"] interval: 10s timeout: 5s retries: 5 @@ -86,6 +92,12 @@ services: caddy: image: caddy:2-alpine restart: unless-stopped + environment: + # The Caddyfile's site address is `{$DOMAIN}`, read from THIS container's env. + # Without it, `{$DOMAIN}` expands to empty, the site block collapses, and Caddy + # serves nothing / fails to obtain a TLS cert. `env_file` alone wouldn't help — + # Caddy needs it in `environment`, and this keeps the Caddyfile the single source. + DOMAIN: ${DOMAIN} ports: - "80:80" - "443:443"