# ── Domain ──────────────────────────────────────────────────────────────────── # Public domain Caddy will serve and obtain a TLS certificate for. DOMAIN=my-event.example.com # ── App server ──────────────────────────────────────────────────────────────── APP_PORT=3000 # Set to `production` in real deployments. This activates the secret guard that # refuses to boot with placeholder JWT_SECRET / ADMIN_PASSWORD_HASH values. # (docker-compose.yml already sets APP_ENV=production for the app service.) APP_ENV=production # ── Database ────────────────────────────────────────────────────────────────── # Set a strong password and keep it in sync between DATABASE_URL and # POSTGRES_PASSWORD. Generate one with: openssl rand -hex 24 DATABASE_URL=postgres://eventsnap:CHANGE_ME_use_a_strong_password@db:5432/eventsnap 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 JWT_SECRET=change_me_to_a_random_64_byte_hex_string SESSION_EXPIRY_DAYS=30 # Admin dashboard password (bcrypt hash). # Generate with: htpasswd -bnBC 12 "" yourpassword | tr -d ':\n' # IMPORTANT: keep the SINGLE QUOTES. A bcrypt hash is full of `$` (e.g. $2b$12$…$…), # and both Docker Compose's env_file interpolation and dotenvy's variable substitution # would otherwise eat the `$…` segments (reading them as unset vars) and corrupt the # hash — every admin login then 401s. Single quotes make both read it literally. ADMIN_PASSWORD_HASH='$2y$12$placeholder_replace_me' # ── Event ───────────────────────────────────────────────────────────────────── EVENT_NAME=Max & Maria's Wedding EVENT_SLUG=max-maria-2026 # ── Storage ─────────────────────────────────────────────────────────────────── MEDIA_PATH=/media # Export archives (Gallery.zip / Memories.zip). MUST be outside MEDIA_PATH — # /media is publicly served, so exports here would be downloadable without auth. EXPORT_PATH=/exports # ── 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