Environment Variables¶
Need help getting set up?
Bunker Operations provides managed infrastructure and hands-on setup assistance for organizations running Changemaker Lite. We handle domains, tunnels, SMTP, and servers so you can focus on your campaign. Get in touch: bnkops.com | admin@bnkops.ca
Changemaker Lite uses a single .env file at the project root to configure all services. Copy the example file to get started:
Security Essentials
- Change every
REQUIRED_STRONG_PASSWORD_CHANGE_THISvalue before starting services - Generate secrets with
openssl rand -hex 32(or-hex 16where noted) - Never commit
.envto 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 |
|---|---|
| Must be set before first run | |
| Has a working default; change for production | |
| 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. |
PostgreSQL (Main Database) ¶
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 |
— | Must change. Database password. |
V2_POSTGRES_DB |
changemaker_v2 |
Database name. |
V2_POSTGRES_PORT |
5433 |
Host port mapping. The container listens on 5432 internally. |
Connection string
The DATABASE_URL is constructed automatically inside Docker. If running locally, set:
JWT Authentication ¶
| Variable | Default | Description |
|---|---|---|
JWT_ACCESS_SECRET |
— | Secret for signing access tokens. Generate with openssl rand -hex 32. |
JWT_REFRESH_SECRET |
— | Secret for signing refresh tokens. Must differ from the access secret. |
JWT_INVITE_SECRET |
— | Secret for signing volunteer invite tokens. Must differ from access and refresh secrets. Generate with openssl rand -hex 32. |
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 ¶
| Variable | Default | Description |
|---|---|---|
ENCRYPTION_KEY |
— | 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 all environments (no longer falls back to JWT secret in development). |
Security Extras ¶
Additional secrets for key separation. These fall back to JWT_ACCESS_SECRET if empty, but setting unique values is strongly recommended for production.
| Variable | Default | Description |
|---|---|---|
GITEA_SSO_SECRET |
(empty) | Cookie signing secret for Gitea SSO integration. Falls back to JWT_ACCESS_SECRET if empty. Generate with openssl rand -hex 32. |
SERVICE_PASSWORD_SALT |
(empty) | Salt for deriving deterministic service passwords (Gitea, Rocket.Chat user provisioning). Falls back to JWT_ACCESS_SECRET if empty — rotating JWT_ACCESS_SECRET would then invalidate all provisioned service passwords. Generate with openssl rand -hex 32. |
CSP_ENABLED |
false |
Enable Content Security Policy headers in API responses. |
Key separation
If GITEA_SSO_SECRET and SERVICE_PASSWORD_SALT are left empty, the API logs security warnings on every startup. Set unique values to isolate secret rotation and prevent one compromised key from affecting other subsystems.
Initial Admin Account ¶
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 |
— | 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. |
Production CORS
If you deploy behind a tunnel (Pangolin, Cloudflare) and API requests fail with CORS errors, add your production app. subdomain here:
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. |
Rate Limiting ¶
| Variable | Default | Description |
|---|---|---|
RATE_LIMIT_WINDOW_MS |
900000 |
Rate limit window in milliseconds (default: 15 minutes). |
RATE_LIMIT_MAX |
500 |
Maximum requests per window per IP. Auth endpoints have a stricter limit (10/min). |
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 ¶
Shared by rate limiting, BullMQ job queues, geocoding cache, and session data.
| Variable | Default | Description |
|---|---|---|
REDIS_PASSWORD |
— | Must change. Redis requires authentication. |
REDIS_URL |
redis://:${REDIS_PASSWORD}@redis-changemaker:6379 |
Full connection URL. Uses the password variable automatically. |
Payments (Stripe) ¶
| Variable | Default | Description |
|---|---|---|
ENABLE_PAYMENTS |
false |
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 ¶
| 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. |
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) ¶
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). |
LISTMONK_DB_USER |
listmonk |
Listmonk database user. |
LISTMONK_DB_PASSWORD |
— | Listmonk database password. |
LISTMONK_DB_NAME |
listmonk |
Listmonk database name. |
LISTMONK_WEB_ADMIN_USER |
admin |
Login for the Listmonk web dashboard. |
LISTMONK_WEB_ADMIN_PASSWORD |
— | Password for the Listmonk web dashboard. |
LISTMONK_API_USER |
v2-api |
API user for programmatic access (auto-created by init container). |
LISTMONK_API_TOKEN |
— | Token for API user. Generate with openssl rand -hex 16. |
LISTMONK_ADMIN_USER |
v2-api |
Same as LISTMONK_API_USER (used by the sync service). |
LISTMONK_ADMIN_PASSWORD |
— | Same as LISTMONK_API_TOKEN. |
LISTMONK_SYNC_ENABLED |
false |
Set to true to sync participants/locations/users to Listmonk lists. |
LISTMONK_WEBHOOK_SECRET |
(empty) | Shared secret for Listmonk webhook callbacks. |
LISTMONK_PROXY_PORT |
9002 |
Nginx proxy port for Listmonk. |
Listmonk SMTP settings
Listmonk has its own SMTP configuration, separate from the main platform's:
| Variable | Default | Description |
|---|---|---|
LISTMONK_SMTP_HOST |
mailhog-changemaker |
SMTP host for Listmonk. |
LISTMONK_SMTP_PORT |
1025 |
SMTP port. |
LISTMONK_SMTP_USER |
(empty) | SMTP username. |
LISTMONK_SMTP_PASSWORD |
(empty) | SMTP password. |
LISTMONK_SMTP_TLS_TYPE |
none |
TLS mode: none, STARTTLS, or TLS. |
LISTMONK_SMTP_FROM |
Changemaker Lite <noreply@cmlite.org> |
From address for newsletters. |
Represent API (Canadian Electoral Data)¶
| Variable | Default | Description |
|---|---|---|
REPRESENT_API_URL |
https://represent.opennorth.ca |
OpenNorth Represent API endpoint. Used for postal code → representative lookups. No API key required. |
NocoDB (Data Browser) ¶
Read-only database browser. Useful for inspecting data without SQL.
| Variable | Default | Description |
|---|---|---|
NOCODB_V2_PORT / NOCODB_PORT |
8091 |
Host port for the NocoDB web UI. |
NOCODB_URL |
http://changemaker-v2-nocodb:8080 |
Internal Docker URL. |
NC_ADMIN_EMAIL |
admin@cmlite.org |
NocoDB admin email. |
NC_ADMIN_PASSWORD |
— | NocoDB admin password. |
Media Manager ¶
Video library with upload, analytics, scheduling, and a public gallery.
| Variable | Default | Description |
|---|---|---|
ENABLE_MEDIA_FEATURES |
false |
Set to true to enable the media system. |
MEDIA_API_PORT |
4100 |
Fastify media API port. |
MEDIA_API_PUBLIC_URL |
http://media-api:4100 |
Internal URL for the media API container. |
MEDIA_ROOT |
/media/library |
Path to the video library inside the container. |
MEDIA_UPLOADS |
/media/uploads |
Path for upload processing. |
MAX_UPLOAD_SIZE_GB |
10 |
Maximum single-file upload size in gigabytes. |
PUBLIC_MEDIA_PORT |
3100 |
Public media gallery server port. |
VIDEO_PLAYER_DEBUG |
false |
Enable verbose video player logging. |
Analytics & scheduling settings
| Variable | Default | Description |
|---|---|---|
VIDEO_ANALYTICS_RETENTION_DAYS |
90 |
Days to retain analytics data. GDPR-compliant with IP hashing. |
VIDEO_ANALYTICS_IP_HASHING_ENABLED |
true |
Hash viewer IPs for privacy. |
VIDEO_SCHEDULE_DEFAULT_TIMEZONE |
UTC |
Default timezone for scheduled publishing. |
VIDEO_SCHEDULE_NOTIFICATION_ENABLED |
true |
Notify on scheduled publish/unpublish. |
VIDEO_PREVIEW_LINK_EXPIRY_HOURS |
24 |
Preview link JWT expiry (hours). |
Gitea (Git Hosting) ¶
Self-hosted Git repository. Optional service.
| Variable | Default | Description |
|---|---|---|
GITEA_URL |
http://gitea-changemaker:3000 |
Internal container URL for Gitea. |
GITEA_PORT / GITEA_WEB_PORT |
3030 |
Gitea web UI port. |
GITEA_SSH_PORT |
2222 |
Gitea SSH port for git operations. |
GITEA_DB_TYPE |
mysql |
Database type (Gitea uses its own MySQL). |
GITEA_DB_HOST |
gitea-db:3306 |
Internal database host. |
GITEA_DB_NAME |
gitea |
Database name. |
GITEA_DB_USER |
gitea |
Database user. |
GITEA_DB_PASSWD |
— | Gitea database password. |
GITEA_DB_ROOT_PASSWORD |
— | MySQL root password for Gitea. |
GITEA_ROOT_URL |
https://git.cmlite.org |
Public-facing URL for Gitea. |
GITEA_DOMAIN |
git.cmlite.org |
Domain used in git clone URLs. |
Gitea Docs Comments
Enable comments on MkDocs documentation pages, backed by Gitea Issues.
| Variable | Default | Description |
|---|---|---|
GITEA_COMMENTS_ENABLED |
false |
Enable comments on MkDocs pages. |
GITEA_API_TOKEN |
(empty) | Personal access token with repo write scope. Create in Gitea → Settings → Applications. |
GITEA_COMMENTS_REPO_OWNER |
(empty) | Gitea username that owns the docs-comments repo. |
GITEA_COMMENTS_REPO_NAME |
docs-comments |
Repository name (auto-created via admin setup). |
GITEA_OAUTH_CLIENT_ID |
(empty) | OAuth2 application client ID (create in Gitea → Settings → Applications → OAuth2). |
GITEA_OAUTH_CLIENT_SECRET |
(empty) | OAuth2 application client secret. |
n8n (Workflow Automation) ¶
| Variable | Default | Description |
|---|---|---|
N8N_PORT |
5678 |
n8n web UI port. |
N8N_HOST |
n8n.cmlite.org |
Public hostname for n8n. |
N8N_ENCRYPTION_KEY |
— | Encryption key for n8n credentials storage. |
N8N_USER_EMAIL |
admin@example.com |
Initial n8n admin email. |
N8N_USER_PASSWORD |
— | Initial n8n admin password. |
GENERIC_TIMEZONE |
UTC |
Timezone for n8n cron triggers. |
MkDocs (Documentation)¶
| Variable | Default | Description |
|---|---|---|
MKDOCS_PORT |
4003 |
MkDocs dev server port (live preview). |
MKDOCS_SITE_SERVER_PORT |
4004 |
MkDocs static site server port. |
BASE_DOMAIN |
https://cmlite.org |
Base URL for generated documentation links. |
MKDOCS_PREVIEW_URL |
http://mkdocs:8000 |
Internal container URL. |
MKDOCS_DOCS_PATH |
/mkdocs/docs |
Documentation source directory inside the container. |
Code Server (Web IDE)¶
| Variable | Default | Description |
|---|---|---|
CODE_SERVER_PORT |
8888 |
Code Server web UI port. |
CODE_SERVER_URL |
http://code-server-changemaker:8443 |
Internal container URL. |
Homepage (Service Dashboard)¶
| Variable | Default | Description |
|---|---|---|
HOMEPAGE_PORT |
3010 |
Homepage web UI port. |
HOMEPAGE_EMBED_PORT |
8887 |
Port for iframe embedding in admin. |
HOMEPAGE_VAR_BASE_URL |
http://localhost |
Base URL used in Homepage service links. |
Mini QR (QR Code Generator)¶
| Variable | Default | Description |
|---|---|---|
MINI_QR_PORT |
8089 |
Mini QR direct access port. |
MINI_QR_URL |
http://mini-qr:8080 |
Internal container URL. |
MINI_QR_EMBED_PORT |
8885 |
Port for iframe embedding (walk sheets, cut exports). |
Excalidraw (Whiteboard)¶
| Variable | Default | Description |
|---|---|---|
EXCALIDRAW_PORT |
8090 |
Excalidraw web UI port. |
EXCALIDRAW_URL |
http://excalidraw-changemaker:80 |
Internal container URL. |
EXCALIDRAW_EMBED_PORT |
8886 |
Port for iframe embedding. |
EXCALIDRAW_WS_URL |
wss://draw.cmlite.org |
WebSocket URL for real-time collaboration. |
Vaultwarden (Password Manager) ¶
Self-hosted Bitwarden-compatible password manager. Optional service.
| Variable | Default | Description |
|---|---|---|
VAULTWARDEN_PORT |
8445 |
Vaultwarden web UI port. |
VAULTWARDEN_URL |
http://vaultwarden-changemaker:80 |
Internal container URL. |
VAULTWARDEN_EMBED_PORT |
8890 |
Port for iframe embedding in admin. |
VAULTWARDEN_ADMIN_TOKEN |
(empty) | 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. |
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) ¶
Self-hosted team chat for volunteer coordination. Requires MongoDB (auto-configured).
| Variable | Default | Description |
|---|---|---|
ENABLE_CHAT |
false |
Set to true to enable the Rocket.Chat integration. The initial default; once saved in admin Settings, the DB value is authoritative. |
ROCKETCHAT_ADMIN_USER |
rcadmin |
Rocket.Chat admin username. |
ROCKETCHAT_ADMIN_PASSWORD |
— | Rocket.Chat admin password. |
ROCKETCHAT_URL |
http://rocketchat-changemaker:3000 |
Internal container URL. |
ROCKETCHAT_EMBED_PORT |
8891 |
Port for iframe embedding in admin. |
MONGO_ROOT_USER |
rocketchat |
MongoDB admin username. |
MONGO_ROOT_PASSWORD |
— | MongoDB admin password. MongoDB runs with --auth enabled. |
Gancio (Event Management) ¶
Self-hosted event management platform. Uses the shared PostgreSQL database (auto-created by init-gancio-db.sh).
| Variable | Default | Description |
|---|---|---|
GANCIO_PORT |
8092 |
Gancio web UI port. |
GANCIO_URL |
http://gancio-changemaker:13120 |
Internal container URL. |
GANCIO_EMBED_PORT |
8892 |
Port for iframe embedding in admin. |
GANCIO_BASE_URL |
https://events.cmlite.org |
Public-facing URL for Gancio. Used in event links. |
GANCIO_ADMIN_USER |
admin |
Gancio admin username for shift-to-event sync (OAuth login). |
GANCIO_ADMIN_PASSWORD |
— | Gancio admin password. |
GANCIO_SYNC_ENABLED |
false |
Set to true to enable automatic shift → Gancio event synchronization. |
Jitsi Meet (Video Conferencing) ¶
Self-hosted video conferencing with JWT authentication. Integrates with Rocket.Chat for in-channel video calls.
| Variable | Default | Description |
|---|---|---|
ENABLE_MEET |
false |
Set to true to enable the Jitsi Meet integration. The initial default; once saved in admin Settings, the DB value is authoritative. |
JITSI_APP_ID |
changemaker |
JWT application ID. Must match across Jitsi Prosody, Rocket.Chat app settings, and JWT_ACCEPTED_ISSUERS/JWT_ACCEPTED_AUDIENCES. |
JITSI_APP_SECRET |
— | JWT secret for signing Jitsi tokens. Generate with openssl rand -hex 32. Shared between Jitsi Prosody, Rocket.Chat, and the API. |
JITSI_JICOFO_AUTH_PASSWORD |
— | Internal XMPP password for Jicofo (conference focus). Generate with openssl rand -hex 16. |
JITSI_JVB_AUTH_PASSWORD |
— | Internal XMPP password for JVB (video bridge). Generate with openssl rand -hex 16. |
JITSI_EMBED_PORT |
8893 |
Port for iframe embedding in admin. |
JITSI_URL |
http://jitsi-web-changemaker:80 |
Internal container URL. |
JVB_ADVERTISE_IP |
(empty) | Server's public IP address. Required in production for NAT traversal so remote participants can connect. |
JVB_PORT |
10000 |
UDP port for media traffic. Must be open in your firewall. |
Production requirements
JVB_ADVERTISE_IPmust be set to your server's public IP for calls to work outside the local network.- Port
10000/udpmust be open in your firewall for media traffic. - Calls must go through the production domain (not localhost) for SSL/JWT to work.
SMS Campaigns (Termux Android Bridge) ¶
Send SMS messages via an Android phone running the Termux API server. The phone acts as an SMS gateway.
| Variable | Default | Description |
|---|---|---|
ENABLE_SMS |
false |
Set to true to enable SMS campaigns. The initial default; once saved in admin Settings, the DB value is authoritative. |
TERMUX_API_URL |
http://10.0.0.193:5001 |
URL of the Termux API server running on the Android phone. |
TERMUX_API_KEY |
(empty) | API key for authenticating with the Termux server (HMAC auth via X-API-Key header). |
SMS_DELAY_BETWEEN_MS |
3000 |
Delay between sending individual SMS messages (ms). Prevents carrier throttling. |
SMS_MAX_RETRIES |
3 |
Maximum retry attempts for failed SMS sends. |
SMS_RESPONSE_SYNC_INTERVAL_MS |
30000 |
How often to poll the phone's inbox for responses (ms). |
SMS_DEVICE_MONITOR_INTERVAL_MS |
30000 |
How often to check device health — battery, connectivity (ms). |
GUI configuration
The Termux API URL and API key can also be configured from Admin → Settings → SMS. Database values override these env vars when set.
MailHog (Development Email)¶
| Variable | Default | Description |
|---|---|---|
MAILHOG_SMTP_PORT |
1025 |
SMTP port for capturing emails. |
MAILHOG_WEB_PORT |
8025 |
Web UI to view captured emails. |
NAR (National Address Register) ¶
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. |
Download NAR data from Statistics Canada.
Geocoding ¶
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 ¶
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 ¶
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). |
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 ¶
These services are behind the monitoring Docker Compose profile. Start them with:
| Variable | Default | Description |
|---|---|---|
PROMETHEUS_PORT |
9090 |
Prometheus web UI / query port. |
GRAFANA_PORT |
3005 |
Grafana dashboard port. |
GRAFANA_ADMIN_PASSWORD |
admin |
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 |
Change in production. |
GRAFANA_EMBED_PORT |
8894 |
Port for iframe embedding Grafana in admin. |
ALERTMANAGER_EMBED_PORT |
8895 |
Port for iframe embedding Alertmanager in admin. |
Bunker Ops (Fleet Management) ¶
Remote metrics push for managing multiple Changemaker Lite instances from a central monitoring server.
| Variable | Default | Description |
|---|---|---|
INSTANCE_LABEL |
(empty) | Unique label for this instance (used as a Prometheus metric label). Falls back to DOMAIN if empty. |
BUNKER_OPS_ENABLED |
false |
Enable remote metrics push to a central VictoriaMetrics server. |
BUNKER_OPS_REMOTE_WRITE_URL |
(empty) | VictoriaMetrics remote_write endpoint (e.g., https://ops.example.com/api/v1/write). |
Social, People & Analytics ¶
Feature flags for the social graph, CRM people module, and analytics dashboard.
| Variable | Default | Description |
|---|---|---|
ENABLE_SOCIAL |
false |
Enable the social module (friendships, challenges, spotlights, referrals). The initial default; once saved in admin Settings, the DB value is authoritative. |
ENABLE_PEOPLE |
false |
Enable the CRM people module. The initial default; once saved in admin Settings, the DB value is authoritative. |
ENABLE_ANALYTICS |
false |
Enable the analytics dashboard with visitor tracking and geographic insights. The initial default; once saved in admin Settings, the DB value is authoritative. |
GeoIP (MaxMind GeoLite2) ¶
Geographic IP lookup for analytics visitor location tracking. Requires a free MaxMind account.
| Variable | Default | Description |
|---|---|---|
MAXMIND_ACCOUNT_ID |
(empty) | MaxMind account ID. Sign up free. |
MAXMIND_LICENSE_KEY |
(empty) | MaxMind license key. When set, the GeoLite2-City database auto-downloads at startup. |
GEOIP_DB_PATH |
/data/geoip/GeoLite2-City.mmdb |
Path to the GeoLite2 database file inside the container. |
Control Panel Agent (CCP) ¶
Remote management agent for the Changemaker Control Panel — enables centralized multi-instance management.
| Variable | Default | Description |
|---|---|---|
ENABLE_CCP_AGENT |
false |
Enable the CCP remote management agent. |
CCP_URL |
(empty) | URL of the Changemaker Control Panel server. |
CCP_INVITE_CODE |
(empty) | One-time invite code for agent registration with the control panel. |
CCP_AGENT_URL |
(empty) | How the CCP can reach this agent (must be externally accessible). |
CCP_AGENT_PORT |
7443 |
Agent listener port. |
Container Registry ¶
Settings for pulling pre-built production images from the Gitea container registry.
| Variable | Default | Description |
|---|---|---|
GITEA_REGISTRY |
gitea.bnkops.com/admin |
Registry hostname and namespace for pulling images. |
IMAGE_TAG |
(empty) | Image tag to pull. Set to a commit SHA or latest for pre-built images. Leave empty (defaults to local) to build from source. |
COMPOSE_PROFILES |
(empty) | Docker Compose profiles to activate. Set to monitoring to include Prometheus/Grafana/Alertmanager in every docker compose up -d. |
GITEA_REGISTRY_USER |
admin |
Registry username for docker login and the registry status API endpoint. |
GITEA_REGISTRY_PASS |
(empty) | Registry password for the status API endpoint. For docker push/pull, use docker login gitea.bnkops.com. |
GITEA_REGISTRY_API_TOKEN |
(empty) | API token for the remote registry (gitea.bnkops.com). Used by build-release.sh --upload to publish release tarballs. Create at Gitea → User Settings → Applications. Not the same as GITEA_API_TOKEN. |
Docker / Container Management ¶
Internal settings for the admin dashboard's service status panel and container management.
| Variable | Default | Description |
|---|---|---|
DOCKER_NETWORK_NAME |
changemaker-lite |
Docker bridge network name. Used by the dashboard to auto-discover containers. |
DOCKER_PROXY_URL |
http://docker-socket-proxy:2375 |
Read-only Docker socket proxy URL for container inspection. |
NEWT_CONTAINER_NAME |
newt-changemaker |
Newt tunnel container name (for restart/status checks). |
NEWT_COMPOSE_SERVICE |
newt |
Docker Compose service name for the Newt container. |
Embed Proxy Ports ¶
Dedicated nginx ports for iframe embedding services in the admin dashboard without requiring DNS/subdomains. Change these to avoid port conflicts when running multiple instances on one host.
| Variable | Default | Description |
|---|---|---|
NOCODB_EMBED_PORT |
8881 |
NocoDB iframe port. |
N8N_EMBED_PORT |
8882 |
n8n iframe port. |
GITEA_EMBED_PORT |
8883 |
Gitea iframe port. |
MAILHOG_EMBED_PORT |
8884 |
MailHog iframe port. |
MINI_QR_EMBED_PORT |
8885 |
Mini QR iframe port. |
EXCALIDRAW_EMBED_PORT |
8886 |
Excalidraw iframe port. |
HOMEPAGE_EMBED_PORT |
8887 |
Homepage iframe port. |
VAULTWARDEN_EMBED_PORT |
8890 |
Vaultwarden iframe port. |
ROCKETCHAT_EMBED_PORT |
8891 |
Rocket.Chat iframe port. |
GANCIO_EMBED_PORT |
8892 |
Gancio iframe port. |
JITSI_EMBED_PORT |
8893 |
Jitsi iframe port. |
GRAFANA_EMBED_PORT |
8894 |
Grafana iframe port. |
ALERTMANAGER_EMBED_PORT |
8895 |
Alertmanager iframe port. |
Gitea Docs Version History ¶
Settings for the documentation version history feature (backed by Gitea repository commits).
| Variable | Default | Description |
|---|---|---|
GITEA_DOCS_REPO |
admin/changemaker.lite |
Gitea repository path for docs version history. |
GITEA_DOCS_PREFIX |
mkdocs/docs |
Path prefix within the repository where documentation files live. |
GITEA_DOCS_BRANCH |
v2 |
Git branch to query for version history. |
GITEA_ADMIN_PASSWORD |
(empty) | Gitea admin password. Used once during initial setup to create an API token, then can be cleared. |
Prisma CLI (Host-Side) ¶
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
postgresql://changemaker:YOUR_POSTGRES_PASSWORD@localhost:5433/changemaker_v2 |
Full PostgreSQL connection string. Only used when running Prisma CLI on the host (npx prisma migrate dev). Docker containers resolve the database hostname internally via Docker Compose environment variables. |
Generating Secrets¶
Use these commands to generate all required secrets at once:
# JWT secrets (three separate values)
echo "JWT_ACCESS_SECRET=$(openssl rand -hex 32)"
echo "JWT_REFRESH_SECRET=$(openssl rand -hex 32)"
echo "JWT_INVITE_SECRET=$(openssl rand -hex 32)"
# Encryption key (must differ from JWT secrets)
echo "ENCRYPTION_KEY=$(openssl rand -hex 32)"
# Security extras (key separation)
echo "GITEA_SSO_SECRET=$(openssl rand -hex 32)"
echo "SERVICE_PASSWORD_SALT=$(openssl rand -hex 32)"
# Database and Redis passwords
echo "V2_POSTGRES_PASSWORD=$(openssl rand -hex 24)"
echo "REDIS_PASSWORD=$(openssl rand -hex 24)"
# Listmonk
echo "LISTMONK_DB_PASSWORD=$(openssl rand -hex 24)"
echo "LISTMONK_WEB_ADMIN_PASSWORD=$(openssl rand -hex 16)"
LISTMONK_TOKEN=$(openssl rand -hex 16)
echo "LISTMONK_API_TOKEN=$LISTMONK_TOKEN"
echo "LISTMONK_ADMIN_PASSWORD=$LISTMONK_TOKEN"
# Supporting services
echo "GITEA_DB_PASSWD=$(openssl rand -hex 24)"
echo "GITEA_DB_ROOT_PASSWORD=$(openssl rand -hex 24)"
echo "N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)"
echo "N8N_USER_PASSWORD=$(openssl rand -hex 16)"
echo "NC_ADMIN_PASSWORD=$(openssl rand -hex 16)"
echo "INITIAL_ADMIN_PASSWORD=$(openssl rand -base64 18)"
# Vaultwarden
echo "VAULTWARDEN_ADMIN_TOKEN=$(openssl rand -hex 32)"
# Rocket.Chat + MongoDB
echo "ROCKETCHAT_ADMIN_PASSWORD=$(openssl rand -hex 16)"
echo "MONGO_ROOT_PASSWORD=$(openssl rand -hex 24)"
# Gancio
echo "GANCIO_ADMIN_PASSWORD=$(openssl rand -hex 16)"
# Jitsi Meet
echo "JITSI_APP_SECRET=$(openssl rand -hex 32)"
echo "JITSI_JICOFO_AUTH_PASSWORD=$(openssl rand -hex 16)"
echo "JITSI_JVB_AUTH_PASSWORD=$(openssl rand -hex 16)"
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¶
For a basic deployment with campaigns, map, and admin:
For the complete platform including media, newsletters, monitoring, and all services:
# Everything above, plus:
ENABLE_MEDIA_FEATURES=true
ENABLE_PAYMENTS=true
ENABLE_CHAT=true
ENABLE_MEET=true
ENABLE_SMS=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=...
MONGO_ROOT_PASSWORD=...
GANCIO_ADMIN_PASSWORD=...
JITSI_APP_SECRET=...
JITSI_JICOFO_AUTH_PASSWORD=...
JITSI_JVB_AUTH_PASSWORD=...
JVB_ADVERTISE_IP=your.public.ip.here
EMAIL_TEST_MODE=false
SMTP_HOST=smtp.your-provider.com
SMTP_PORT=587
SMTP_USER=you@example.com
SMTP_PASS=your-smtp-password