Environment Files for Dev, Stage, and Prod¶
This guide explains how to create and manage your .env file for local development (dev), stage, and production (prod) using the files and Make targets in this repo. The app will pick up these settings via Docker Compose/containers.
Quick Start¶
Local development (dev)¶
Use env.dev.example as a safe default for local work. You only need to change a few values (e.g., Google Maps keys, docs repos/token) if you plan to exercise those features locally.
# Copy the dev example and open it for editing (overwrites existing .env)
make env
Minimum you might want to customize locally:
GOOGLE_MAPS_BROWSER_API_KEY,GOOGLE_MAPS_SERVER_API_KEY– see google_maps_api_key_guide.mdGH_REPOS,GH_PAT,GH_WEBHOOK_SECRET,DOCS_BUILD_TOKEN– if you want local MkDocs builds pulling from your GitHub repos
For local dev, keeping the remaining
change-me-in-productionvalues is acceptable; these are not used outside your machine.
Stage / Prod¶
For stage and prod you should use real values. Start from env.prod.example and fill in the necessary secrets and hostnames.
# Copy the prod example and open it for editing (overwrites existing .env)
make prod-env
Rule of thumb: Only values explicitly shown as
change-me-in-productionor marked as# secret=truemust be replaced. Common defaults likePOSTGRES_PORT=5432,POSTGRES_DB=ok_db, etc., can stay as-is unless you deliberately changed your infra.
Generating Secrets with Make¶
Use the provided Make targets to generate cryptographic secrets inside the containers (no host dependencies needed):
# 50-char Django SECRET_KEY
make dj-secret
# 32-byte hex token (e.g., HEALTH_CHECK_TOKEN)
make token-32
# 64-byte hex token (e.g., OTEL_WRITE_TOKEN)
make token-64
# Bcrypt hash for passwords used by Caddy/Prometheus basic auth
make caddy-hash-password
Paste the outputs into the corresponding .env variables.
What to Change (by section)¶
Below is a checklist covering stage and prod (both use real values). For dev, you can usually keep the defaults unless noted.
Postgres¶
POSTGRES_USER(prod/stage: real value)POSTGRES_PASSWORD(prod/stage: real secret)- ✅ OK to keep defaults:
POSTGRES_HOST=postgres,POSTGRES_PORT=5432,POSTGRES_DB=ok_db,POSTGRES_LOCALE=en_US.UTF-8
Caddy (Prod only)¶
TLS_EMAIL(prod: real email for ACME/Let’s Encrypt)
Django / Gunicorn¶
BUILD_TARGET=development(dev) /production(stage & prod)DJANGO_SETTINGS_MODULE– dev:mow.settings.dev, prod/stage:mow.settings.prodSECRET_KEY(prod/stage: generate viamake dj-secret)JWT_SIGNING_KEY(prod/stage: generate viamake dj-secretor another secret)DEBUG=False(prod/stage)ALLOWED_HOSTS(prod/stage: set your domains)HEALTH_CHECK_TOKEN(prod/stage:make token-32)- ✅ Usually fine unchanged:
GUNICORN_WORKERS,GUNICORN_THREADS,GUNICORN_MAX_REQUESTS*
CORS / CSRF / Domains¶
CORS_ALLOW_ALL_ORIGINS=False(already safe default)CORS_ALLOWED_ORIGINS(prod/stage: comma-separated https URLs)CSRF_TRUSTED_ORIGINS(prod/stage: comma-separated https URLs)DOMAIN_NAME,ADMIN_DOMAIN,PORTAL_DOMAIN(prod/stage: real domains)
Initial Admin User¶
ADMIN_USERNAME,ADMIN_EMAIL(prod/stage: your real bootstrap admin)ADMIN_ROLE=ADMIN(default is fine)
Google Maps API Keys¶
GOOGLE_MAPS_BROWSER_API_KEY,GOOGLE_MAPS_SERVER_API_KEY(prod/stage: real keys)- For how to create/secure keys, see: google_maps_api_key_guide.md
Outbound Email (Prod only if used)¶
EMAIL_HOST_USER,EMAIL_HOST_PASSWORD(real creds)DEFAULT_FROM_EMAIL,SERVER_EMAIL(valid addresses)
Timezone (Supercronic)¶
TZ(defaultUTCis fine)
Observability / Telemetry¶
OTEL_WRITE_TOKEN(prod/stage:make token-64)
Prometheus / Caddy Basic Auth¶
PROMETHEUS_USER(prod/stage: pick a user)PROMETHEUS_PASSWORD_HASH(prod/stage:make caddy-hash-password)
Grafana¶
GRAFANA_USER(prod/stage: pick a user)GRAFANA_PASSWORD(prod/stage: strong password)GRAFANA_FROM_EMAIL(valid email if sending mail)GF_DATABASE_PASSWORD(prod/stage: real secret)
MkDocs / Docs Service¶
GH_REPOS(prod/stage: comma-separated list of org/repo[@branch])GH_PAT(prod/stage: fine‑grained token with Contents: Read‑only)GH_WEBHOOK_SECRET(prod/stage: random secret)DOCS_BUILD_TOKEN(optional manual trigger; random if used)OPENAPI_REPO,OPENAPI_PATH(point to your OpenAPI file)DOCS_SITE_NAME(branding)DOCS_SITE_URL,DOCS_API_URL(prod/stage: public URLs)DOCS_INITIAL_BUILD(true/falseas desired)
Deploy (Prod and/or Stage if you use the deploy container)¶
GH_DEPLOY_KEY(prod/stage: real deploy key if used)
Recommended Workflows¶
Dev (local)¶
- Run
make envand accept the overwrite prompt. -
Optionally set:
-
Google Maps keys (see guide)
- Docs-related vars (if you’ll build docs locally)
- Start your dev stack as usual via Docker Compose/Make.
Stage¶
- Run
make prod-envto copyenv.prod.exampleto.envand edit. - Replace everything marked
change-me-in-productionand any# secret=trueitems using the Make targets above. - Set stage hostnames in
ALLOWED_HOSTS,CORS_ALLOWED_ORIGINS,CSRF_TRUSTED_ORIGINS, and the*_DOMAINvars. - Provide real Google Maps keys (see guide), GitHub repos/PAT, and any OpenAPI settings for docs.
- Commit
.envonly to your secret manager/CI secrets – never to Git.
Prod¶
Repeat the Stage steps with production hostnames and credentials. Ensure:
DEBUG=False- Valid TLS email (
TLS_EMAIL) - All secrets are strong and unique
Safe Defaults You Don’t Need to Change¶
POSTGRES_HOST=postgresPOSTGRES_PORT=5432POSTGRES_DB=ok_dbPOSTGRES_LOCALE=en_US.UTF-8- Gunicorn worker/thread counts (tune later if needed)
TZ=UTCADMIN_ROLE=ADMINOPENAPI_PATH=api/openapi.jsonDOCS_SITE_NAME=Developer DocsDOCS_INITIAL_BUILD=true
Prod Settings that MUST be correct¶
BUILD_TARGET=productionDJANGO_SETTINGS_MODULE=mow.settings.prodDEBUG=FalseCORS_ALLOW_ALL_ORIGINS=False
Tips¶
- Keep
.envout of Git (already covered by.gitignore). - Store real secrets in a secure secrets manager (e.g., AWS SSM Parameter Store – see
aws/scripts/ssm-*). - To regenerate a single secret, re-run the relevant
maketarget and update.env. - After editing
.env, restart containers that read it (often easiest: bring the stack down/up).
Reference¶
- Dev example:
env.dev.example - Prod example:
env.prod.example - Google Maps keys: google_maps_api_key_guide.md
< Backend Development - Google Maps API Key Guide
Next: Backend Development - Django Python Dependency Management >