Complete reference for every .env variable in Changemaker Lite.
material/file-cog
Environment Variables
Changemaker Lite uses a single .env file at the project root to configure all services. Copy the example file to get started:
cp .env.example .env
!!! danger "Security Essentials"
- Change everyREQUIRED_STRONG_PASSWORD_CHANGE_THIS value before starting services
- Generate secrets with openssl rand -hex 32 (or -hex 16 where noted)
- Never commit .env to version control
- Use unique values for each secret — do not reuse JWT secrets as encryption keys
Quick Reference
Variables are grouped by service. Each table marks whether a variable is required for a basic deployment or optional (has a sensible default or only needed for specific features).
Symbol
Meaning
:material-alert-circle:{ .text-red }
Must be set before first run
:material-tune-variant:
Has a working default; change for production
:material-flask:
Feature flag — opt-in
General
Variable
Default
Description
NODE_ENV
development
Set to production for production deployments. Controls logging, error detail, and security checks.
DOMAIN
cmlite.org
Root domain. Used for nginx subdomain routing (app.DOMAIN, api.DOMAIN, etc.). The root domain serves the MkDocs documentation site; all application routes live under app.DOMAIN.
USER_ID
1000
UID for container file ownership. Match your host user's UID (id -u).
GROUP_ID
1000
GID for container file ownership. Match your host user's GID (id -g).
DOCKER_GROUP_ID
984
GID of the docker group on the host. Needed for containers that access the Docker socket. Find with getent group docker.
The primary database for both the Express API and the Fastify Media API (shared).
Variable
Default
Description
V2_POSTGRES_USER
changemaker
Database username.
V2_POSTGRES_PASSWORD
—
:material-alert-circle:{ .text-red } Must change. Database password.
V2_POSTGRES_DB
changemaker_v2
Database name.
V2_POSTGRES_PORT
5433
Host port mapping. The container listens on 5432 internally.
!!! tip "Connection string"
The DATABASE_URL is constructed automatically inside Docker. If running locally, set:
DATABASE_URL=postgresql://changemaker:YOUR_PASSWORD@localhost:5433/changemaker_v2
JWT Authentication :material-alert-circle:
Variable
Default
Description
JWT_ACCESS_SECRET
—
:material-alert-circle:{ .text-red } Secret for signing access tokens. Generate with openssl rand -hex 32.
JWT_REFRESH_SECRET
—
:material-alert-circle:{ .text-red } Secret for signing refresh tokens. Must differ from the access secret.
JWT_ACCESS_EXPIRY
15m
Access token lifetime. Short-lived by design.
JWT_REFRESH_EXPIRY
7d
Refresh token lifetime. Tokens are rotated atomically on each refresh.
Encryption Key :material-alert-circle:
Variable
Default
Description
ENCRYPTION_KEY
—
:material-alert-circle:{ .text-red } AES key for encrypting secrets stored in the database (SMTP passwords, API keys, etc.). Generate with openssl rand -hex 32. Must not reuse a JWT secret. Required in production (NODE_ENV=production).
Initial Admin Account :material-alert-circle:
These credentials create the first super-admin user during database seeding (npx prisma db seed).
Variable
Default
Description
INITIAL_ADMIN_EMAIL
admin@cmlite.org
Email address for the initial admin.
INITIAL_ADMIN_PASSWORD
—
:material-alert-circle:{ .text-red } Must change. Must be 12+ characters with uppercase, lowercase, and a digit. Change this password after first login.
API Server
Variable
Default
Description
API_PORT
4000
Host port for the Express API.
API_URL
http://localhost:4000
Public URL of the API. Used for generating links in emails and QR codes.
CORS_ORIGINS
http://localhost:3000,http://localhost
Comma-separated list of allowed CORS origins. Add your production domain (e.g., https://app.yourdomain.org) for production.
!!! warning "Production CORS"
If you deploy behind a tunnel (Pangolin, Cloudflare) and API requests fail with CORS errors, add your production app. subdomain here:
CORS_ORIGINS=https://app.betteredmonton.org,http://localhost:3000,http://localhost
Then restart the API: docker compose restart api
Admin GUI
Variable
Default
Description
ADMIN_PORT
3000
Host port for the React admin dashboard.
ADMIN_URL
http://localhost:3000
Public URL of the admin GUI.
Nginx Reverse Proxy
Variable
Default
Description
NGINX_HTTP_PORT
80
HTTP port. All subdomains route through nginx.
NGINX_HTTPS_PORT
443
HTTPS port. SSL is typically handled by the tunnel provider (Pangolin/Cloudflare).
Redis :material-alert-circle:
Shared by rate limiting, BullMQ job queues, geocoding cache, and session data.
Variable
Default
Description
REDIS_PASSWORD
—
:material-alert-circle:{ .text-red } Must change. Redis requires authentication.
REDIS_URL
redis://:${REDIS_PASSWORD}@redis-changemaker:6379
Full connection URL. Uses the password variable automatically.
Payments (Stripe) :material-flask:
Variable
Default
Description
ENABLE_PAYMENTS
false
:material-flask: Set to true to enable the payments feature (memberships, products, donations). Stripe API keys are stored encrypted in the database via the admin settings page.
Email / SMTP :material-tune-variant:
Variable
Default
Description
SMTP_HOST
mailhog-changemaker
SMTP server. Default points to the MailHog dev container.
SMTP_PORT
1025
SMTP port. 1025 for MailHog, 587 for most production SMTP.
SMTP_USER
(empty)
SMTP username. Not needed for MailHog.
SMTP_PASS
(empty)
SMTP password.
SMTP_FROM
noreply@cmlite.org
"From" address on outgoing emails.
SMTP_FROM_NAME
Changemaker Lite
Display name for the "From" header.
EMAIL_TEST_MODE
true
When true, all emails go to MailHog instead of real SMTP. Set to false in production.
TEST_EMAIL_RECIPIENT
admin@cmlite.org
Catch-all recipient when test mode is on.
!!! info "Development email"
With EMAIL_TEST_MODE=true, all outgoing email is captured in MailHog at http://localhost:8025. No real emails are sent.
Listmonk (Newsletters) :material-flask:
Listmonk handles newsletter/marketing campaigns. Sync with the main platform is opt-in.
Variable
Default
Description
LISTMONK_PORT
9001
Listmonk web UI port.
LISTMONK_DB_PORT
5434
Listmonk's own PostgreSQL port (separate from the main DB). Uses 5434 to avoid conflict with the main PostgreSQL (5432 internal / 5433 host).
Admin panel token (access at /admin). Generate with openssl rand -hex 32.
VAULTWARDEN_DOMAIN
https://vault.cmlite.org
Public-facing URL. Must use HTTPS — Bitwarden web vault enforces HTTPS for account creation. Set to your Pangolin tunnel URL.
VAULTWARDEN_SIGNUPS_ALLOWED
false
Allow new user self-registration. Keep false and use admin panel invites.
VAULTWARDEN_WEBSOCKET_ENABLED
true
Enable WebSocket notifications for real-time sync.
VAULTWARDEN_SMTP_SECURITY
off
SMTP security mode: off for MailHog, starttls or force_tls for production. Uses the main SMTP_* variables for host/credentials.
!!! info "Initial setup"
The vaultwarden-init container automatically invites the INITIAL_ADMIN_EMAIL user when starting. Check MailHog (or your SMTP) for the invitation email.
Rocket.Chat (Team Chat) :material-flask:
Self-hosted team chat for volunteer coordination. Requires MongoDB (auto-configured).
Variable
Default
Description
ENABLE_CHAT
false
:material-flask: Set to true to enable the Rocket.Chat integration. The initial default; once saved in admin Settings, the DB value is authoritative.
Canadian address data import for geographic canvassing.
Variable
Default
Description
NAR_DATA_DIR
/data
Path to extracted NAR data inside the container. Expects YYYYMM/Addresses/ and YYYYMM/Locations/ subdirectories. Mount via ./data:/data:ro in Docker Compose.
Multi-provider geocoding for address resolution. Works out of the box with free providers; optional paid providers improve accuracy.
Variable
Default
Description
MAPBOX_API_KEY
(empty)
Mapbox API key for improved geocoding accuracy. Free tier: 100k requests/month. Sign up.
GEOCODING_RATE_LIMIT_MS
1100
Delay between requests to free providers (ms). Respects rate limits.
GEOCODING_CACHE_ENABLED
true
Enable Redis-backed geocoding cache.
GEOCODING_CACHE_TTL_HOURS
24
Cache lifetime in hours.
GOOGLE_MAPS_API_KEY
(empty)
Google Maps API key. Most accurate but $0.005/request after free tier.
GOOGLE_MAPS_ENABLED
false
Enable Google Maps as a geocoding provider.
GEOCODING_PARALLEL_ENABLED
true
Enable parallel geocoding for bulk imports (~10x speedup).
GEOCODING_BATCH_SIZE
10
Number of concurrent geocoding requests during bulk operations.
BULK_GEOCODE_ENABLED
true
Enable bulk re-geocoding from the admin UI.
BULK_GEOCODE_MAX_BATCH
5000
Maximum locations per bulk geocoding run.
Overpass / Area Import :material-tune-variant:
OpenStreetMap data import for map enrichment.
Variable
Default
Description
OVERPASS_API_URL
https://overpass-api.de/api/interpreter
Overpass API endpoint. Use a private instance for heavy usage.
OVERPASS_MIN_DELAY_MS
30000
Minimum delay between requests (ms). The public API requires 30 seconds.
AREA_IMPORT_MAX_GRID_POINTS
500
Maximum reverse-geocode grid points per area import.
Pangolin Tunnel :material-tune-variant:
Expose services to the internet without port forwarding, using a self-hosted Pangolin instance.
Variable
Default
Description
PANGOLIN_API_URL
https://api.bnkserve.org/v1
Pangolin server API endpoint.
PANGOLIN_API_KEY
(empty)
API key for Pangolin management.
PANGOLIN_ORG_ID
(empty)
Organization ID in Pangolin.
PANGOLIN_SITE_ID
(empty)
Site ID (populated after setup via admin GUI).
PANGOLIN_ENDPOINT
https://pangolin.bnkserve.org
Pangolin tunnel endpoint.
PANGOLIN_NEWT_ID
(empty)
Newt client ID (populated after setup).
PANGOLIN_NEWT_SECRET
(empty)
Newt client secret (populated after setup).
!!! tip "Setup flow"
Configure the tunnel from Admin → Settings → Pangolin. The setup wizard walks you through creating a site, copying credentials, and connecting the Newt container. See Deployment for the full guide.
Monitoring :material-flask:
These services are behind the monitoring Docker Compose profile. Start them with:
docker compose --profile monitoring up -d
Variable
Default
Description
PROMETHEUS_PORT
9090
Prometheus web UI / query port.
GRAFANA_PORT
3005
Grafana dashboard port.
GRAFANA_ADMIN_PASSWORD
admin
:material-tune-variant: Change in production.
GRAFANA_ROOT_URL
http://localhost:3005
Public URL for Grafana (used in links).
CADVISOR_PORT
8086
cAdvisor container metrics port.
NODE_EXPORTER_PORT
9100
Prometheus node exporter port.
REDIS_EXPORTER_PORT
9121
Redis metrics exporter port.
ALERTMANAGER_PORT
9093
Alertmanager web UI port.
GOTIFY_PORT
8889
Gotify push notification port.
GOTIFY_ADMIN_USER
admin
Gotify admin username.
GOTIFY_ADMIN_PASSWORD
admin
:material-tune-variant: Change in production.
Generating Secrets
Use these commands to generate all required secrets at once:
!!! tip
Copy the output and paste the values into your .env file. The INITIAL_ADMIN_PASSWORD uses base64 encoding to ensure it contains uppercase, lowercase, and digits (meeting the password policy).
Minimal vs Full Deployment
=== "Minimal (Core Only)"
For a basic deployment with campaigns, map, and admin:
```bash title="Required variables"
V2_POSTGRES_PASSWORD=...
REDIS_PASSWORD=...
JWT_ACCESS_SECRET=...
JWT_REFRESH_SECRET=...
ENCRYPTION_KEY=...
INITIAL_ADMIN_PASSWORD=...
```
```bash title="Start services"
docker compose up -d v2-postgres redis api admin
```
=== "Full Stack"
For the complete platform including media, newsletters, monitoring, and all services:
```bash title="Additional variables needed"
# Everything above, plus:
ENABLE_MEDIA_FEATURES=true
ENABLE_PAYMENTS=true
ENABLE_CHAT=true
LISTMONK_SYNC_ENABLED=true
GANCIO_SYNC_ENABLED=true
LISTMONK_DB_PASSWORD=...
LISTMONK_WEB_ADMIN_PASSWORD=...
LISTMONK_API_TOKEN=...
NC_ADMIN_PASSWORD=...
GITEA_DB_PASSWD=...
GITEA_DB_ROOT_PASSWORD=...
N8N_ENCRYPTION_KEY=...
N8N_USER_PASSWORD=...
VAULTWARDEN_ADMIN_TOKEN=...
ROCKETCHAT_ADMIN_PASSWORD=...
GANCIO_ADMIN_PASSWORD=...
EMAIL_TEST_MODE=false
SMTP_HOST=smtp.your-provider.com
SMTP_PORT=587
SMTP_USER=you@example.com
SMTP_PASS=your-smtp-password
```
```bash title="Start services"
docker compose up -d
docker compose --profile monitoring up -d
```