Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions .env.example.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# SourceBans++ production environment template (#1381 deliverable 4).
#
# Copy to `.env` next to `docker-compose.prod.yml`, fill in the
# REQUIRED values, then run:
#
# docker compose -f docker-compose.prod.yml up -d
#
# `.env` is read automatically by `docker compose`. Do NOT commit it
# to git — it carries the panel's JWT signing key and the DB password.
#
# All values below are EXAMPLES. Production values must be unique to
# your deployment.

# =============================================================================
# REQUIRED — the stack will refuse to start without these
# =============================================================================

# JWT signing key. Persists across container recreations so admin
# sessions survive redeploys. Generate once:
#
# openssl rand -base64 47
#
# If you regenerate this value, every admin's session cookie becomes
# invalid and they have to log in again. NEVER commit this value.
SB_SECRET_KEY=replace-with-output-of-openssl-rand-base64-47

# Database root password (used by mariadb's init step for the first
# boot of the `db` container only — the panel itself uses DB_USER/
# DB_PASS). Generate with `openssl rand -base64 24`.
DB_ROOT_PASS=replace-with-strong-random-password

# Database password for the panel user. Generate with
# `openssl rand -base64 24`. Must NOT equal DB_ROOT_PASS.
DB_PASS=replace-with-different-strong-random-password

# =============================================================================
# RECOMMENDED — the panel runs without these, but features degrade
# =============================================================================

# Steam Web API key. Required for SteamID name resolution and the
# Steam OpenID login path. Get one from:
# https://steamcommunity.com/dev/apikey
#
# The all-zero value below is the dev-stack placeholder; production
# panels must use a real key.
STEAMAPIKEY=00000000000000000000000000000000

# Default "From" address for outgoing panel email (password resets,
# protest notifications, etc.). Defaults to noreply@$(hostname) inside
# the container if unset.
SB_EMAIL=noreply@yourdomain.example

# =============================================================================
# FIRST-BOOT INSTALL — initial admin account seed
# =============================================================================
#
# When the entrypoint runs against an empty DB on first boot, it seeds
# an Owner-flagged admin from these four values. Set ALL four — the
# entrypoint skips the seed (and logs a warning) if any is missing,
# leaving you unable to log in until you create an admin by hand.
#
# After the first successful boot, these values are no-ops (the
# entrypoint only runs the seed when the DB is empty).

# Display name shown in the panel sidebar.
INITIAL_ADMIN_NAME=Admin

# SteamID2 of the initial admin. Looks like `STEAM_0:1:12345678`.
# Required because the panel's primary identity is the SteamID, not
# the display name. Convert SteamID64 → SteamID2 with
# https://steamid.io or https://steamidfinder.com.
INITIAL_ADMIN_STEAM=STEAM_0:0:0

# Email address used for password-reset flows.
INITIAL_ADMIN_EMAIL=admin@yourdomain.example

# Login password. Minimum 8 characters per the panel's bcrypt floor.
# Change this immediately after first login via the "Your account"
# page; the env var is the SEED, not a persistent source of truth.
INITIAL_ADMIN_PASSWORD=replace-with-strong-temporary-password

# =============================================================================
# OPTIONAL — knobs most operators leave at defaults
# =============================================================================

# Host port the panel binds to. Default 8080 to avoid colliding with
# anything already on port 80 (e.g. a host nginx). Behind a reverse
# proxy, you'd typically point the proxy at this port.
SBPP_HOST_PORT=8080

# Host bind. Empty = bind to all interfaces (the panel is reachable
# from the public internet on SBPP_HOST_PORT). For "reverse-proxy
# only" deployments, set to `127.0.0.1:` (note the trailing colon)
# so only the proxy can talk to the panel.
SBPP_BIND=

# Image tag to pull from GHCR. Defaults to `latest` (newest
# released stable). Pin to a specific version (e.g. `1.7.0`) for
# reproducible deployments; pin to `main` to ride the bleeding
# edge (NOT recommended for production).
SBPP_IMAGE_TAG=latest

# Trusted reverse-proxy CIDR ranges. Space-separated. When set,
# Apache trusts X-Forwarded-For / X-Forwarded-Proto from these
# addresses ONLY. Empty default = no proxy trust (so a misconfigured
# deployment doesn't accidentally trust arbitrary clients).
#
# For Caddy / Traefik / nginx on the same compose network, the
# default Docker bridge ranges cover everything:
#
# SBPP_TRUSTED_PROXIES=172.16.0.0/12 10.0.0.0/8
#
# For Cloudflare's network in front of the panel:
#
# SBPP_TRUSTED_PROXIES=173.245.48.0/20 103.21.244.0/22 ...
# (see https://www.cloudflare.com/ips/ for the current list)
SBPP_TRUSTED_PROXIES=

# Database name / user / port / prefix / charset. Defaults match
# the dev stack; only change if pointing at an existing DB.
DB_NAME=sourcebans
DB_USER=sourcebans
DB_HOST=db
DB_PORT=3306
DB_PREFIX=sb
DB_CHARSET=utf8mb4

# DATABASE_URL — alternative to the split DB_* vars above. When set,
# the entrypoint parses the URL into DB_HOST/DB_PORT/DB_USER/DB_PASS/
# DB_NAME and that value wins over the split vars. Useful when the
# DB is provisioned by an app platform (Render / Fly / Railway)
# that emits a single connection URL.
#
# DATABASE_URL=mysql://user:pass@host:port/dbname?charset=utf8mb4
DATABASE_URL=

# SBPP_AUTO_INSTALL — when "1" (default), the entrypoint runs the
# first-boot install + migrations on every container start (idempotent
# after the first run). Set to "0" if you provisioned the DB by hand
# (managed RDS, restored backup, etc.) and want the entrypoint to
# only run migrations.
SBPP_AUTO_INSTALL=1

# PORT (advanced — do NOT set on a docker-compose deploy)
#
# Inside the container, the entrypoint rewrites Apache's `Listen 80`
# to `Listen $PORT` when this env var is set. The mechanism exists
# so the image runs unmodified on app-platform deploys
# (Render / Fly / Railway / Heroku) that inject `PORT` and expect
# the app to bind to it.
#
# On a docker-compose deploy (this file), `docker-compose.prod.yml`
# hardcodes `${SBPP_HOST_PORT}:80` — host port → container port 80.
# Setting `PORT=8000` here would rewrite the container's listen to
# 8000 BUT compose would still map the host port to the container's
# port 80 (which nothing is listening on), making the panel
# unreachable. SBPP_HOST_PORT above is the right knob for the
# compose host-side; leave PORT unset on compose.
#
# LOW-3 of the #1381 review documented this trap.
# PORT=8080

# SBPP_CONFIG_PATH — where the panel reads/writes config.php. Default
# (empty) = the image's writable layer at
# /var/www/html/web/config.php. Set this to a path on a volume or a
# Docker-secret mount when you want config.php to survive container
# image rebuilds (the alternative is setting SB_SECRET_KEY explicitly
# in this file, which the entrypoint will then bake into config.php).
SBPP_CONFIG_PATH=

# =============================================================================
# DOCKER SECRETS / *_FILE PATTERN (advanced)
# =============================================================================
#
# Every secret-shaped env var above (SB_SECRET_KEY, DB_PASS, DB_ROOT_PASS,
# STEAMAPIKEY, INITIAL_ADMIN_PASSWORD) accepts a sibling `_FILE` form
# that points at a file path inside the container. When the sibling is
# set AND the file exists, the entrypoint reads the file's contents
# and uses them as the secret value, overriding the plain env var.
#
# This is the standard Docker Swarm / Kubernetes secret-injection
# idiom. Example:
#
# # docker-compose.prod.override.yml
# services:
# web:
# environment:
# SB_SECRET_KEY_FILE: /run/secrets/sbpp_jwt_key
# DB_PASS_FILE: /run/secrets/sbpp_db_pass
# secrets:
# - sbpp_jwt_key
# - sbpp_db_pass
# secrets:
# sbpp_jwt_key:
# file: ./secrets/jwt_key
# sbpp_db_pass:
# file: ./secrets/db_pass
#
# Plain `docker compose` deploys typically use the env-var form above
# (simpler); the `_FILE` form is for Swarm / k8s where secrets are
# managed by the orchestrator.

# =============================================================================
# REVERSE PROXY (only needed if you uncomment the caddy: service)
# =============================================================================

# When you uncomment the `caddy:` block in docker-compose.prod.yml,
# set your panel's public domain here. Caddy automatically provisions
# Let's Encrypt TLS for the value.
SBPP_DOMAIN=panel.yourdomain.example
Loading
Loading