799 lines
24 KiB
Markdown
799 lines
24 KiB
Markdown
# Environment Variables Reference
|
|
|
|
## Overview
|
|
|
|
Changemaker Lite V2 uses over 100 environment variables to configure services, credentials, and feature flags. This document provides a complete reference organized by functional area.
|
|
|
|
**Configuration File**: `.env` (never committed to Git)
|
|
|
|
**Template**: `.env.example` (committed, safe to share)
|
|
|
|
**Validation**: `api/src/config/env.ts` (Zod schema validates all variables on startup)
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
### Initial Setup
|
|
|
|
```bash
|
|
# Copy template
|
|
cp .env.example .env
|
|
|
|
# Generate secrets
|
|
openssl rand -hex 32 # For JWT_ACCESS_SECRET
|
|
openssl rand -hex 32 # For JWT_REFRESH_SECRET
|
|
openssl rand -hex 32 # For ENCRYPTION_KEY (must differ from JWT secrets!)
|
|
openssl rand -hex 16 # For LISTMONK_API_TOKEN
|
|
|
|
# Edit .env
|
|
nano .env
|
|
```
|
|
|
|
### Minimal Required Variables
|
|
|
|
**Must set before first start**:
|
|
```bash
|
|
V2_POSTGRES_PASSWORD=<strong-password>
|
|
REDIS_PASSWORD=<strong-password>
|
|
JWT_ACCESS_SECRET=<openssl-rand-hex-32>
|
|
JWT_REFRESH_SECRET=<openssl-rand-hex-32>
|
|
ENCRYPTION_KEY=<openssl-rand-hex-32> # Production only
|
|
```
|
|
|
|
**All other variables** have safe defaults for development.
|
|
|
|
---
|
|
|
|
## General Configuration
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `NODE_ENV` | `development` | No | Environment mode (`development` \| `production`) |
|
|
| `DOMAIN` | `cmlite.org` | No | Base domain for subdomain routing |
|
|
| `USER_ID` | `1000` | No | Host user ID for volume permissions |
|
|
| `GROUP_ID` | `1000` | No | Host group ID for volume permissions |
|
|
| `DOCKER_GROUP_ID` | `984` | No | Docker group ID (for homepage container) |
|
|
|
|
**Usage**:
|
|
```bash
|
|
NODE_ENV=production docker compose up -d
|
|
```
|
|
|
|
---
|
|
|
|
## V2 PostgreSQL
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `V2_POSTGRES_USER` | `changemaker` | No | PostgreSQL username |
|
|
| `V2_POSTGRES_PASSWORD` | `CHANGE_ME_STRONG_PASSWORD` | **Yes** | PostgreSQL password |
|
|
| `V2_POSTGRES_DB` | `changemaker_v2` | No | Database name |
|
|
| `V2_POSTGRES_PORT` | `5433` | No | Host port (container always 5432) |
|
|
|
|
**Connection String** (auto-generated in docker-compose.yml):
|
|
```
|
|
postgresql://changemaker:PASSWORD@changemaker-v2-postgres:5432/changemaker_v2
|
|
```
|
|
|
|
**Port Binding**: `127.0.0.1:5433:5432` (localhost only for security)
|
|
|
|
**Important**: Change `V2_POSTGRES_PASSWORD` before production deployment.
|
|
|
|
---
|
|
|
|
## JWT Authentication
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `JWT_ACCESS_SECRET` | `GENERATE_WITH_openssl_rand_hex_32` | **Yes** | Access token secret (15min lifespan) |
|
|
| `JWT_REFRESH_SECRET` | `GENERATE_WITH_openssl_rand_hex_32` | **Yes** | Refresh token secret (7 day lifespan) |
|
|
| `JWT_ACCESS_EXPIRY` | `15m` | No | Access token expiration (`15m`, `1h`, etc.) |
|
|
| `JWT_REFRESH_EXPIRY` | `7d` | No | Refresh token expiration (`7d`, `30d`, etc.) |
|
|
| `ENCRYPTION_KEY` | `GENERATE_WITH_openssl_rand_hex_32` | **Yes (prod)** | DB encryption key for SMTP passwords, etc. |
|
|
|
|
**Security Requirements** (enforced by Zod schema):
|
|
- `JWT_ACCESS_SECRET` must be 32+ characters
|
|
- `JWT_REFRESH_SECRET` must be 32+ characters
|
|
- `ENCRYPTION_KEY` must be 32+ characters **and differ from JWT secrets**
|
|
|
|
**Generation**:
|
|
```bash
|
|
export JWT_ACCESS_SECRET=$(openssl rand -hex 32)
|
|
export JWT_REFRESH_SECRET=$(openssl rand -hex 32)
|
|
export ENCRYPTION_KEY=$(openssl rand -hex 32)
|
|
echo "JWT_ACCESS_SECRET=${JWT_ACCESS_SECRET}" >> .env
|
|
echo "JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET}" >> .env
|
|
echo "ENCRYPTION_KEY=${ENCRYPTION_KEY}" >> .env
|
|
```
|
|
|
|
**Production Note**: `ENCRYPTION_KEY` required in production (dev mode allows empty for testing).
|
|
|
|
---
|
|
|
|
## Redis
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `REDIS_PASSWORD` | `CHANGE_ME_REDIS_PASSWORD` | **Yes** | Redis authentication password |
|
|
| `REDIS_URL` | `redis://:PASSWORD@redis-changemaker:6379` | No | Full connection URL (auto-generated) |
|
|
|
|
**Format**: `redis://[:<password>@]<host>:<port>[/<db>]`
|
|
|
|
**Example**:
|
|
```bash
|
|
REDIS_PASSWORD=mySecurePassword123
|
|
REDIS_URL=redis://:mySecurePassword123@redis-changemaker:6379
|
|
```
|
|
|
|
**Security Note**: As of Security Audit 2025-02-11, Redis **requires authentication** in production.
|
|
|
|
**Docker Command** (in docker-compose.yml):
|
|
```yaml
|
|
command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru --requirepass "${REDIS_PASSWORD}"
|
|
```
|
|
|
|
---
|
|
|
|
## API Configuration
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `API_PORT` | `4000` | No | Express API port (host) |
|
|
| `API_URL` | `http://localhost:4000` | No | Public API URL (for emails, OAuth redirects) |
|
|
| `CORS_ORIGINS` | `http://localhost:3000,http://localhost` | No | Allowed CORS origins (comma-separated) |
|
|
|
|
**Production Example**:
|
|
```bash
|
|
API_PORT=4000
|
|
API_URL=https://api.cmlite.org
|
|
CORS_ORIGINS=https://app.cmlite.org,https://cmlite.org
|
|
```
|
|
|
|
**CORS Note**: List all frontend origins (admin, public site, media gallery).
|
|
|
|
---
|
|
|
|
## Admin GUI
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `ADMIN_PORT` | `3000` | No | Admin GUI port (host) |
|
|
| `ADMIN_URL` | `http://localhost:3000` | No | Public admin URL |
|
|
| `VITE_API_URL` | `http://changemaker-v2-api:4000` | No | API URL for Vite proxy (Docker internal) |
|
|
| `VITE_MEDIA_API_URL` | `http://changemaker-media-api:4100` | No | Media API URL for Vite proxy |
|
|
| `VITE_MKDOCS_URL` | `http://mkdocs-changemaker:8000` | No | MkDocs URL for iframe embed |
|
|
|
|
**Development vs Production**:
|
|
|
|
**Development** (Docker):
|
|
```bash
|
|
VITE_API_URL=http://changemaker-v2-api:4000 # Container name
|
|
VITE_MEDIA_API_URL=http://changemaker-media-api:4100
|
|
```
|
|
|
|
**Development** (local):
|
|
```bash
|
|
VITE_API_URL=http://localhost:4000 # Localhost
|
|
VITE_MEDIA_API_URL=http://localhost:4100
|
|
```
|
|
|
|
**Production**: Vite build embeds these URLs at build time.
|
|
|
|
---
|
|
|
|
## Nginx
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `NGINX_HTTP_PORT` | `80` | No | HTTP port |
|
|
| `NGINX_HTTPS_PORT` | `443` | No | HTTPS port |
|
|
|
|
**Port Mapping** (docker-compose.yml):
|
|
```yaml
|
|
nginx:
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
- "8881:8881" # NocoDB embed proxy
|
|
- "8882:8882" # n8n embed proxy
|
|
- "8883:8883" # Gitea embed proxy
|
|
- "8884:8884" # MailHog embed proxy
|
|
- "8885:8885" # Mini QR embed proxy
|
|
```
|
|
|
|
**Custom Ports** (if 80/443 occupied):
|
|
```bash
|
|
NGINX_HTTP_PORT=8080
|
|
NGINX_HTTPS_PORT=8443
|
|
```
|
|
|
|
---
|
|
|
|
## SMTP / Email
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `SMTP_HOST` | `mailhog-changemaker` | No | SMTP server hostname |
|
|
| `SMTP_PORT` | `1025` | No | SMTP server port |
|
|
| `SMTP_USER` | `` | No | SMTP username (empty for MailHog) |
|
|
| `SMTP_PASS` | `` | No | SMTP password |
|
|
| `SMTP_FROM` | `noreply@cmlite.org` | No | Default sender email |
|
|
| `SMTP_FROM_NAME` | `Changemaker Lite` | No | Default sender name |
|
|
| `EMAIL_TEST_MODE` | `true` | No | Route all emails to MailHog (dev mode) |
|
|
| `TEST_EMAIL_RECIPIENT` | `admin@cmlite.org` | No | Override recipient in test mode |
|
|
|
|
**Development** (MailHog):
|
|
```bash
|
|
SMTP_HOST=mailhog-changemaker
|
|
SMTP_PORT=1025
|
|
SMTP_USER=
|
|
SMTP_PASS=
|
|
EMAIL_TEST_MODE=true
|
|
```
|
|
|
|
**Production** (e.g., ProtonMail):
|
|
```bash
|
|
SMTP_HOST=smtp.protonmail.ch
|
|
SMTP_PORT=587
|
|
SMTP_USER=your@email.com
|
|
SMTP_PASS=your-app-password
|
|
EMAIL_TEST_MODE=false
|
|
```
|
|
|
|
**Test Mode Behavior**:
|
|
- `true`: All emails sent to MailHog (visible at http://localhost:8025)
|
|
- `false`: Emails sent to real recipients via SMTP
|
|
|
|
**SiteSettings Override**: Admins can override SMTP config via `/app/settings` (stored encrypted in DB).
|
|
|
|
---
|
|
|
|
## Listmonk
|
|
|
|
### Database
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `LISTMONK_DB_PORT` | `5432` | No | Listmonk PostgreSQL port |
|
|
| `LISTMONK_DB_USER` | `listmonk` | No | Database username |
|
|
| `LISTMONK_DB_PASSWORD` | `CHANGE_ME_LISTMONK_PASSWORD` | **Yes** | Database password |
|
|
| `LISTMONK_DB_NAME` | `listmonk` | No | Database name |
|
|
|
|
### Web Admin
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `LISTMONK_PORT` | `9001` | No | Listmonk web UI port |
|
|
| `LISTMONK_WEB_ADMIN_USER` | `admin` | No | Web UI username |
|
|
| `LISTMONK_WEB_ADMIN_PASSWORD` | `CHANGE_ME_LISTMONK_ADMIN` | **Yes** | Web UI password |
|
|
|
|
### API Integration
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `LISTMONK_API_USER` | `v2-api` | No | API user (auto-created by listmonk-init) |
|
|
| `LISTMONK_API_TOKEN` | `GENERATE_WITH_openssl_rand_hex_16` | **Yes** | API token (plaintext, not bcrypt) |
|
|
| `LISTMONK_ADMIN_USER` | `v2-api` | No | Alias for API user (V2 uses this) |
|
|
| `LISTMONK_ADMIN_PASSWORD` | `SAME_AS_LISTMONK_API_TOKEN` | **Yes** | Alias for API token |
|
|
| `LISTMONK_SYNC_ENABLED` | `false` | No | Enable participant/location sync |
|
|
| `LISTMONK_PROXY_PORT` | `9002` | No | OAuth proxy port (for future integrations) |
|
|
|
|
**API User Setup**: The `listmonk-init` container auto-creates the API user by directly inserting into PostgreSQL.
|
|
|
|
**Token Generation**:
|
|
```bash
|
|
export LISTMONK_API_TOKEN=$(openssl rand -hex 16)
|
|
echo "LISTMONK_API_TOKEN=${LISTMONK_API_TOKEN}" >> .env
|
|
echo "LISTMONK_ADMIN_PASSWORD=${LISTMONK_API_TOKEN}" >> .env
|
|
```
|
|
|
|
**Sync Behavior**:
|
|
- `false`: Manual sync only (default)
|
|
- `true`: Auto-sync participants/locations to Listmonk lists on signup/create
|
|
|
|
### SMTP Configuration
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `LISTMONK_SMTP_HOST` | `mailhog-changemaker` | No | SMTP server for newsletters |
|
|
| `LISTMONK_SMTP_PORT` | `1025` | No | SMTP port |
|
|
| `LISTMONK_SMTP_USER` | `` | No | SMTP username |
|
|
| `LISTMONK_SMTP_PASSWORD` | `` | No | SMTP password |
|
|
| `LISTMONK_SMTP_TLS_TYPE` | `none` | No | TLS mode (`none` \| `STARTTLS` \| `TLS`) |
|
|
| `LISTMONK_SMTP_FROM` | `Changemaker Lite <noreply@cmlite.org>` | No | Newsletter sender |
|
|
|
|
**listmonk-init Behavior**: Configures **dual SMTP providers** (MailHog + production if credentials set).
|
|
|
|
---
|
|
|
|
## Represent API
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `REPRESENT_API_URL` | `https://represent.opennorth.ca` | No | Represent API endpoint (Canadian electoral data) |
|
|
|
|
**Free Public API**: No authentication required.
|
|
|
|
**Usage**: Postal code → representative lookup for Influence campaigns.
|
|
|
|
---
|
|
|
|
## NocoDB
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `NOCODB_V2_PORT` | `8091` | No | NocoDB web UI port |
|
|
| `NOCODB_URL` | `http://changemaker-v2-nocodb:8080` | No | Internal NocoDB URL |
|
|
| `NC_ADMIN_EMAIL` | `admin@cmlite.org` | No | Admin email |
|
|
| `NC_ADMIN_PASSWORD` | `CHANGE_ME_NOCODB_PASSWORD` | **Yes** | Admin password |
|
|
| `NC_PUBLIC_URL` | `http://localhost:8091` | No | Public NocoDB URL |
|
|
|
|
**Database Connection**: Uses separate `nocodb_meta` database (auto-created by `init-nocodb-db.sh`).
|
|
|
|
**Connection String**:
|
|
```
|
|
pg://changemaker-v2-postgres:5432?u=changemaker&p=PASSWORD&d=nocodb_meta
|
|
```
|
|
|
|
---
|
|
|
|
## Media Management
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `ENABLE_MEDIA_FEATURES` | `false` | No | Enable media manager features |
|
|
| `MEDIA_API_PORT` | `4100` | No | Fastify media API port |
|
|
| `MEDIA_API_PUBLIC_URL` | `http://media-api:4100` | No | Public media API URL |
|
|
| `MEDIA_ROOT` | `/media/library` | No | Media library root path |
|
|
| `MEDIA_UPLOADS` | `/media/uploads` | No | Upload staging directory |
|
|
| `MAX_UPLOAD_SIZE_GB` | `10` | No | Max video upload size (GB) |
|
|
| `PUBLIC_MEDIA_PORT` | `3100` | No | Public media gallery port |
|
|
| `VIDEO_PLAYER_DEBUG` | `false` | No | Enable video.js debug logging |
|
|
|
|
**Feature Flag**: Set `ENABLE_MEDIA_FEATURES=true` to activate media routes.
|
|
|
|
**Volume Mounts** (in docker-compose.yml):
|
|
```yaml
|
|
volumes:
|
|
- ${MEDIA_ROOT:-./media}:/media:ro # Library (read-only)
|
|
- ${MEDIA_ROOT:-./media}/local/inbox:/media/local/inbox:rw # Inbox (writable)
|
|
```
|
|
|
|
**Supported Formats**: MP4, MOV, AVI, MKV, WebM, M4V, FLV
|
|
|
|
---
|
|
|
|
## Gitea
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `GITEA_URL` | `http://gitea-changemaker:3000` | No | Internal Gitea URL |
|
|
| `GITEA_WEB_PORT` | `3030` | No | Gitea web UI port |
|
|
| `GITEA_SSH_PORT` | `2222` | No | Gitea SSH port (for git push/pull) |
|
|
| `GITEA_DB_TYPE` | `mysql` | No | Database type |
|
|
| `GITEA_DB_HOST` | `gitea-db:3306` | No | MySQL hostname |
|
|
| `GITEA_DB_NAME` | `gitea` | No | Database name |
|
|
| `GITEA_DB_USER` | `gitea` | No | Database username |
|
|
| `GITEA_DB_PASSWD` | `CHANGE_ME_GITEA_DB` | **Yes** | Database password |
|
|
| `GITEA_DB_ROOT_PASSWORD` | `CHANGE_ME_GITEA_ROOT` | **Yes** | MySQL root password |
|
|
| `GITEA_ROOT_URL` | `https://git.cmlite.org` | No | Public Gitea URL |
|
|
| `GITEA_DOMAIN` | `git.cmlite.org` | No | Gitea domain |
|
|
|
|
**First-Time Setup**: Visit http://localhost:3030 to create admin account.
|
|
|
|
**Git Commands**:
|
|
```bash
|
|
# Clone via HTTP
|
|
git clone http://localhost:3030/user/repo.git
|
|
|
|
# Clone via SSH
|
|
git clone ssh://git@localhost:2222/user/repo.git
|
|
```
|
|
|
|
---
|
|
|
|
## n8n
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `N8N_URL` | `http://n8n-changemaker:5678` | No | Internal n8n URL |
|
|
| `N8N_PORT` | `5678` | No | n8n port |
|
|
| `N8N_HOST` | `n8n.cmlite.org` | No | Public n8n hostname |
|
|
| `N8N_ENCRYPTION_KEY` | `CHANGE_ME_N8N_KEY` | **Yes** | Workflow encryption key |
|
|
| `N8N_USER_EMAIL` | `admin@example.com` | No | Default admin email |
|
|
| `N8N_USER_PASSWORD` | `CHANGE_ME_N8N_PASSWORD` | **Yes** | Default admin password |
|
|
| `GENERIC_TIMEZONE` | `UTC` | No | Workflow timezone |
|
|
|
|
**First Start**: n8n creates admin user with `N8N_USER_EMAIL`/`N8N_USER_PASSWORD` automatically.
|
|
|
|
**Encryption Key**: Used to encrypt credentials in workflows.
|
|
|
|
---
|
|
|
|
## MkDocs
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `MKDOCS_PORT` | `4003` | No | MkDocs live preview port |
|
|
| `MKDOCS_SITE_SERVER_PORT` | `4001` | No | MkDocs static site port |
|
|
| `BASE_DOMAIN` | `https://cmlite.org` | No | Site URL for sitemap/canonical |
|
|
| `MKDOCS_PREVIEW_URL` | `http://mkdocs:8000` | No | Internal preview URL |
|
|
| `MKDOCS_DOCS_PATH` | `/mkdocs/docs` | No | Documentation source path |
|
|
|
|
**Port Change**: Was 4000 in V1, changed to 4003 to avoid conflict with API.
|
|
|
|
**Live Reload**: http://localhost:4003 (updates on file save)
|
|
|
|
**Static Build**: http://localhost:4001 (Nginx-served production build)
|
|
|
|
---
|
|
|
|
## Code Server
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `CODE_SERVER_PORT` | `8888` | No | Code Server port |
|
|
| `CODE_SERVER_URL` | `http://code-server:8080` | No | Internal Code Server URL |
|
|
| `USER_NAME` | `coder` | No | Code Server username |
|
|
|
|
**Access**: http://localhost:8888
|
|
|
|
**Password**: Set in `configs/code-server/.config/code-server/config.yaml`
|
|
|
|
---
|
|
|
|
## Homepage
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `HOMEPAGE_PORT` | `3010` | No | Homepage dashboard port |
|
|
| `HOMEPAGE_VAR_BASE_URL` | `http://localhost` | No | Base URL for service links |
|
|
|
|
**Configuration**: Edit `configs/homepage/services.yaml` to customize dashboard.
|
|
|
|
---
|
|
|
|
## Mini QR
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `MINI_QR_PORT` | `8089` | No | Mini QR service port |
|
|
| `MINI_QR_URL` | `http://mini-qr:8080` | No | Internal Mini QR URL |
|
|
| `MINI_QR_EMBED_PORT` | `8885` | No | Nginx embed proxy port |
|
|
|
|
**Usage**: Walk sheets + cut exports embed QR codes via API or iframe.
|
|
|
|
---
|
|
|
|
## MailHog
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `MAILHOG_SMTP_PORT` | `1025` | No | SMTP port (internal only) |
|
|
| `MAILHOG_WEB_PORT` | `8025` | No | Web UI port |
|
|
|
|
**Web UI**: http://localhost:8025
|
|
|
|
**SMTP**: Only accessible from Docker network (not exposed to host).
|
|
|
|
---
|
|
|
|
## NAR Import
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `NAR_DATA_DIR` | `/data` | No | Path to NAR data directory (in container) |
|
|
|
|
**Host Mount** (in docker-compose.yml):
|
|
```yaml
|
|
volumes:
|
|
- ./data:/data:ro # Read-only NAR data
|
|
```
|
|
|
|
**Data Structure**:
|
|
```
|
|
./data/
|
|
└─ 202501/ (YYYYMM)
|
|
├─ Addresses/
|
|
│ ├─ Address_10.txt (PEI)
|
|
│ ├─ Address_24_part_1.txt (Quebec part 1)
|
|
│ └─ ...
|
|
└─ Locations/
|
|
├─ Location_10.txt
|
|
└─ ...
|
|
```
|
|
|
|
**Download**: https://www150.statcan.gc.ca/n1/pub/46-26-0002/462600022022001-eng.htm
|
|
|
|
---
|
|
|
|
## Geocoding
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `MAPBOX_API_KEY` | `` | No | Mapbox API key (optional, 100k free/month) |
|
|
| `GEOCODING_RATE_LIMIT_MS` | `1100` | No | Delay between provider requests (ms) |
|
|
| `GEOCODING_CACHE_ENABLED` | `true` | No | Enable Redis caching |
|
|
| `GEOCODING_CACHE_TTL_HOURS` | `24` | No | Cache TTL in hours |
|
|
| `GOOGLE_MAPS_API_KEY` | `` | No | Google Maps API key (optional, paid) |
|
|
| `GOOGLE_MAPS_ENABLED` | `false` | No | Enable Google geocoding provider |
|
|
| `GEOCODING_PARALLEL_ENABLED` | `true` | No | Parallel geocoding for bulk imports |
|
|
| `GEOCODING_BATCH_SIZE` | `10` | No | Batch size for parallel geocoding |
|
|
| `BULK_GEOCODE_ENABLED` | `true` | No | Enable bulk re-geocode feature |
|
|
| `BULK_GEOCODE_MAX_BATCH` | `5000` | No | Max locations per bulk geocode batch |
|
|
|
|
**Providers** (in fallback order):
|
|
1. **Nominatim** (OpenStreetMap, free)
|
|
2. **ArcGIS** (free tier)
|
|
3. **Photon** (free)
|
|
4. **Mapbox** (100k free/month, requires API key)
|
|
5. **LocationIQ** (free tier)
|
|
6. **Google** (paid, most accurate)
|
|
|
|
**Recommendation**: Add `MAPBOX_API_KEY` for better accuracy without cost.
|
|
|
|
---
|
|
|
|
## Pangolin Tunnel
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `PANGOLIN_API_URL` | `https://api.bnkserve.org/v1` | No | Pangolin API endpoint |
|
|
| `PANGOLIN_API_KEY` | `` | No | Pangolin API key |
|
|
| `PANGOLIN_ORG_ID` | `` | No | Organization ID (from setup wizard) |
|
|
| `PANGOLIN_SITE_ID` | `` | No | Site ID (from setup wizard) |
|
|
| `PANGOLIN_ENDPOINT` | `https://pangolin.bnkserve.org` | No | Tunnel endpoint URL |
|
|
| `PANGOLIN_NEWT_ID` | `` | No | Newt connector ID |
|
|
| `PANGOLIN_NEWT_SECRET` | `` | No | Newt connector secret |
|
|
|
|
**Setup Workflow**:
|
|
1. Visit `/app/pangolin` in admin GUI
|
|
2. Enter `PANGOLIN_API_KEY`
|
|
3. Create org → site → endpoint → resource
|
|
4. Copy `NEWT_ID`/`NEWT_SECRET` to `.env`
|
|
5. Restart Newt container
|
|
|
|
**Manual Setup**:
|
|
```bash
|
|
# Set API key
|
|
export PANGOLIN_API_KEY=your-api-key
|
|
|
|
# Create org (returns ORG_ID)
|
|
curl -H "Authorization: Bearer $PANGOLIN_API_KEY" \
|
|
https://api.bnkserve.org/v1/orgs \
|
|
-d '{"name":"My Organization"}'
|
|
|
|
# Create site (returns SITE_ID)
|
|
curl -H "Authorization: Bearer $PANGOLIN_API_KEY" \
|
|
https://api.bnkserve.org/v1/sites \
|
|
-d '{"org_id":"ORG_ID","name":"Production Site"}'
|
|
|
|
# Continue setup...
|
|
```
|
|
|
|
See [Tunneling](tunneling.md) for complete guide.
|
|
|
|
---
|
|
|
|
## Monitoring
|
|
|
|
### Prometheus
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `PROMETHEUS_PORT` | `9090` | No | Prometheus port |
|
|
|
|
**Scrape Targets** (configured in `configs/prometheus/prometheus.yml`):
|
|
- `changemaker-v2-api:4000/api/metrics` (10s interval)
|
|
- `redis-exporter:9121` (15s interval)
|
|
- `cadvisor:8080` (15s interval)
|
|
- `node-exporter:9100` (15s interval)
|
|
|
|
**Retention**: 30 days (configured in docker-compose.yml command).
|
|
|
|
### Grafana
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `GRAFANA_PORT` | `3001` | No | Grafana port |
|
|
| `GRAFANA_ADMIN_PASSWORD` | `admin` | No | Admin password |
|
|
| `GRAFANA_ROOT_URL` | `http://localhost:3001` | No | Public Grafana URL |
|
|
|
|
**Default Login**: admin / admin (change on first login)
|
|
|
|
**Dashboards**: 3 pre-configured dashboards auto-provisioned from `configs/grafana/`
|
|
|
|
### Exporters
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `CADVISOR_PORT` | `8080` | No | cAdvisor container metrics port |
|
|
| `NODE_EXPORTER_PORT` | `9100` | No | Node exporter system metrics port |
|
|
| `REDIS_EXPORTER_PORT` | `9121` | No | Redis exporter port |
|
|
|
|
### Alertmanager
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `ALERTMANAGER_PORT` | `9093` | No | Alertmanager port |
|
|
|
|
**Configuration**: Edit `configs/alertmanager/alertmanager.yml` for notification receivers.
|
|
|
|
### Gotify
|
|
|
|
| Variable | Default | Required | Description |
|
|
|----------|---------|----------|-------------|
|
|
| `GOTIFY_PORT` | `8889` | No | Gotify push notification server port |
|
|
| `GOTIFY_ADMIN_USER` | `admin` | No | Gotify admin username |
|
|
| `GOTIFY_ADMIN_PASSWORD` | `admin` | No | Gotify admin password |
|
|
|
|
**Usage**: Create apps in Gotify UI, add webhook URL to Alertmanager.
|
|
|
|
---
|
|
|
|
## Security Checklist
|
|
|
|
**Before production deployment**:
|
|
|
|
- [ ] Change all `CHANGE_ME_*` passwords
|
|
- [ ] Generate strong `JWT_ACCESS_SECRET` (32+ chars)
|
|
- [ ] Generate strong `JWT_REFRESH_SECRET` (32+ chars)
|
|
- [ ] Generate strong `ENCRYPTION_KEY` (32+ chars, **different from JWT secrets**)
|
|
- [ ] Set strong `REDIS_PASSWORD`
|
|
- [ ] Set strong `V2_POSTGRES_PASSWORD`
|
|
- [ ] Set strong `LISTMONK_DB_PASSWORD`
|
|
- [ ] Set strong `LISTMONK_API_TOKEN`
|
|
- [ ] Set strong `GITEA_DB_PASSWD` + `GITEA_DB_ROOT_PASSWORD`
|
|
- [ ] Set strong `N8N_ENCRYPTION_KEY` + `N8N_USER_PASSWORD`
|
|
- [ ] Set strong `NC_ADMIN_PASSWORD` (NocoDB)
|
|
- [ ] Set strong `GRAFANA_ADMIN_PASSWORD`
|
|
- [ ] Disable `EMAIL_TEST_MODE` (set to `false`)
|
|
- [ ] Configure real SMTP credentials
|
|
- [ ] Set `NODE_ENV=production`
|
|
- [ ] Review `CORS_ORIGINS` (whitelist only trusted domains)
|
|
|
|
**Validation**:
|
|
```bash
|
|
# Check for remaining placeholders
|
|
grep -r "CHANGE_ME" .env
|
|
|
|
# Verify secrets are different
|
|
echo "JWT_ACCESS_SECRET: $(grep JWT_ACCESS_SECRET .env)"
|
|
echo "JWT_REFRESH_SECRET: $(grep JWT_REFRESH_SECRET .env)"
|
|
echo "ENCRYPTION_KEY: $(grep ENCRYPTION_KEY .env)"
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Missing .env File
|
|
|
|
**Symptoms**: Containers fail to start with "missing environment variable" errors
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Create from template
|
|
cp .env.example .env
|
|
|
|
# Verify file exists
|
|
ls -la .env
|
|
```
|
|
|
|
---
|
|
|
|
### Invalid Environment Variables
|
|
|
|
**Symptoms**: API fails to start with Zod validation errors
|
|
|
|
**Diagnosis**:
|
|
```bash
|
|
# View API startup logs
|
|
docker compose logs api | grep -A10 "Environment validation"
|
|
```
|
|
|
|
**Common errors**:
|
|
- `JWT_ACCESS_SECRET` too short (must be 32+ chars)
|
|
- `ENCRYPTION_KEY` same as `JWT_ACCESS_SECRET` (must differ)
|
|
- Invalid URL format (`API_URL` must start with http:// or https://)
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Regenerate secrets
|
|
export JWT_ACCESS_SECRET=$(openssl rand -hex 32)
|
|
export ENCRYPTION_KEY=$(openssl rand -hex 32)
|
|
|
|
# Update .env
|
|
sed -i "s/^JWT_ACCESS_SECRET=.*/JWT_ACCESS_SECRET=${JWT_ACCESS_SECRET}/" .env
|
|
sed -i "s/^ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${ENCRYPTION_KEY}/" .env
|
|
|
|
# Restart API
|
|
docker compose restart api
|
|
```
|
|
|
|
---
|
|
|
|
### PostgreSQL Connection Failures
|
|
|
|
**Symptoms**: API logs show `ECONNREFUSED` or `authentication failed`
|
|
|
|
**Diagnosis**:
|
|
```bash
|
|
# Check PostgreSQL is running
|
|
docker compose ps v2-postgres
|
|
|
|
# Test connection
|
|
docker compose exec api npx prisma db pull
|
|
|
|
# Verify DATABASE_URL
|
|
docker compose exec api printenv | grep DATABASE_URL
|
|
```
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Verify password matches in .env
|
|
grep V2_POSTGRES_PASSWORD .env
|
|
|
|
# Restart PostgreSQL
|
|
docker compose restart v2-postgres
|
|
|
|
# Wait for healthcheck
|
|
docker compose ps v2-postgres # Should show (healthy)
|
|
```
|
|
|
|
---
|
|
|
|
### Redis Connection Failures
|
|
|
|
**Symptoms**: API logs show `ECONNREFUSED` or `WRONGPASS invalid password`
|
|
|
|
**Diagnosis**:
|
|
```bash
|
|
# Check Redis is running
|
|
docker compose ps redis
|
|
|
|
# Test connection
|
|
docker compose exec redis redis-cli -a "${REDIS_PASSWORD}" ping
|
|
```
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Verify password in .env
|
|
grep REDIS_PASSWORD .env
|
|
|
|
# Ensure REDIS_URL includes password
|
|
grep REDIS_URL .env # Should be redis://:PASSWORD@redis-changemaker:6379
|
|
|
|
# Restart Redis
|
|
docker compose restart redis
|
|
```
|
|
|
|
---
|
|
|
|
### Environment Variables Not Updating
|
|
|
|
**Symptoms**: Changed `.env` but service still uses old value
|
|
|
|
**Cause**: Docker Compose reads `.env` at startup, not runtime
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Recreate container (picks up new env vars)
|
|
docker compose up -d --force-recreate api
|
|
|
|
# Or: stop and start
|
|
docker compose down
|
|
docker compose up -d
|
|
```
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- **[Docker Compose](docker-compose.md)** — Service orchestration
|
|
- **[SSL/TLS](ssl-tls.md)** — Certificate management
|
|
- **[Tunneling](tunneling.md)** — Pangolin setup
|
|
- **[Backup & Restore](backup-restore.md)** — Data protection
|
|
- **[Security Audit](../../SECURITY_AUDIT_2025-02-11.md)** — Security requirements
|