Configuration
Complete environment variable reference for every WaSphere service.
Configuration
All WaSphere configuration is driven by environment variables in the .env file at the project root. Copy .env.example to .env and edit it before starting the stack.
cp .env.example .env
Variables are grouped by the service that consumes them. Many variables are shared — when a variable appears in a shared section it applies to multiple services.
Never commit your .env file to source control. The .gitignore in the repository excludes it, but double-check before every git add.
Shared / Deployment
These variables affect the overall deployment and are consumed by Traefik and the dashboard services.
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
DOMAIN | string | — | Yes | Your domain name without protocol — e.g. wa.example.com. Traefik uses this to provision SSL and route traffic. |
ADMIN_EMAIL | string | — | Yes | Email address of the initial admin account. Also used as the Let's Encrypt registration email. |
ADMIN_PASSWORD | string | — | Yes | Password for the initial admin account. Must be at least 12 characters. |
REGISTRATION_LOCKED | boolean | false | No | Set to true to disable new user registrations after initial setup. Recommended for production once your team is onboarded. |
Dashboard API
The Dashboard API (NestJS, port 3000) is the main backend. It handles authentication, session management, message dispatch, webhooks, and the REST API your applications call.
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
DATABASE_URL | string | — | Yes | PostgreSQL connection string in the format postgresql://USER:PASSWORD@HOST:PORT/DATABASE. Inside Docker, use postgresql://wasphere:changeme@postgres:5432/wasphere. |
JWT_SECRET | string | — | Yes | Secret key used to sign JWT access tokens. Must be at least 32 characters. Generate with openssl rand -hex 32. Rotate this to invalidate all existing sessions. |
JWT_EXPIRES_IN | string | 7d | No | JWT token lifetime. Uses ms format — e.g. 1h, 7d, 30d. |
WA_SERVER_URL | string | http://wa-server:3001 | No | Internal URL the Dashboard API uses to reach the WA Server. In Docker this is the service name. Change only if running services on separate machines. |
WA_TOKEN | string | — | Yes | Shared secret the Dashboard API sends as X-Api-Token to authenticate with the WA Server. Generate with openssl rand -hex 32. Must match WA_TOKEN in the WA Server section. |
DASHBOARD_PORT | number | 3000 | No | Port the Dashboard API listens on. Only change if running without Docker. |
CORS_ORIGINS | string | * | No | Comma-separated list of allowed CORS origins. In production, set this to your exact frontend URL — e.g. https://wa.example.com. |
LOG_LEVEL | string | info | No | Log verbosity. Options: error, warn, info, debug, verbose. Use debug temporarily when diagnosing issues. |
Webhook Delivery
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
WEBHOOK_SECRET | string | — | Yes | Default HMAC-SHA256 signing secret for webhook payloads. Each webhook endpoint can override this with its own secret via the dashboard. |
WEBHOOK_TIMEOUT_MS | number | 10000 | No | Milliseconds to wait for a webhook endpoint to respond before marking the delivery as failed. |
WEBHOOK_MAX_RETRIES | number | 5 | No | Maximum number of delivery attempts per event. |
WEBHOOK_RETRY_BASE_DELAY_MS | number | 30000 | No | Base delay (in ms) for the first retry. Subsequent retries use exponential backoff: baseDelay * 2^attempt. |
Rate Limiting
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
RATE_LIMIT_TTL | number | 60 | No | Rate limit window in seconds. |
RATE_LIMIT_MAX | number | 100 | No | Maximum requests per API key per RATE_LIMIT_TTL window. |
RATE_LIMIT_SKIP_ADMIN | boolean | true | No | Whether to skip rate limiting for requests authenticated with the admin JWT (not API keys). |
Media & Storage
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
MEDIA_MAX_SIZE_MB | number | 64 | No | Maximum file size for media uploads in megabytes. WhatsApp's own limit is 64 MB for most file types. |
MEDIA_STORAGE_PATH | string | /app/media | No | Local path where uploaded media files are stored before being sent. |
WA Server
The WA Server (NestJS + Baileys, port 3001) manages WhatsApp connections. It only accepts requests from the Dashboard API.
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
WA_TOKEN | string | — | Yes | Shared secret. Must match the value set in the Dashboard API. Incoming requests without a valid X-Api-Token: <token> header are rejected with 401. |
PORT | number | 3001 | No | Port the WA Server listens on. Only change if running without Docker. |
SESSIONS_PATH | string | /app/sessions | No | Directory where Baileys stores session credentials. Map this to a Docker volume to persist sessions across container restarts. |
LOG_LEVEL | string | info | No | Log verbosity for the WA Server process. |
Anti-Ban Defaults
These are platform-wide defaults. Each session can override them via the dashboard.
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
MSG_DELAY_MIN_MS | number | 1000 | No | Minimum delay in milliseconds between consecutive messages sent by the same session. |
MSG_DELAY_MAX_MS | number | 3000 | No | Maximum delay in milliseconds. The actual delay is a random value between MIN and MAX. |
TYPING_SIMULATION | boolean | true | No | When true, the WA Server sends a typing indicator before each text message. Duration scales with message length. |
RATE_LIMIT_PER_MIN | number | 20 | No | Maximum messages per minute per session. Requests exceeding this limit are queued, not dropped. |
Dashboard UI
The Dashboard UI (Next.js, port 13004) is a browser application. Variables prefixed with NEXT_PUBLIC_ are embedded in the built JavaScript and visible to the browser.
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
NEXT_PUBLIC_API_URL | string | — | Yes | Public URL of the Dashboard API, as seen from the browser. E.g. https://wa.example.com/api. |
WA_SERVER_INTERNAL_URL | string | http://wa-server:3001 | No | Internal Docker URL the Next.js server-side code uses to reach the WA Server directly (for QR code streaming). |
NEXT_PUBLIC_APP_NAME | string | WaSphere | No | Brand name shown in the dashboard UI title and header. |
PostgreSQL (Docker Service)
These variables configure the PostgreSQL container in the Docker Compose stack.
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
POSTGRES_USER | string | wasphere | No | PostgreSQL superuser name. Must match the username in DATABASE_URL. |
POSTGRES_PASSWORD | string | changeme | Yes | PostgreSQL superuser password. Use a strong password in production. Must match the password in DATABASE_URL. |
POSTGRES_DB | string | wasphere | No | Database name. Must match the database in DATABASE_URL. |
The default POSTGRES_PASSWORD=changeme is intentionally insecure. Replace it before going to production. After changing it, update DATABASE_URL to match and recreate the postgres container: docker compose up -d --force-recreate postgres.
Example .env for Production
# ── Deployment ──────────────────────────────────────────
DOMAIN=wa.example.com
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=MyStr0ng!P@ssword
REGISTRATION_LOCKED=true
# ── Secrets ─────────────────────────────────────────────
JWT_SECRET=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
WA_TOKEN=f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5
WEBHOOK_SECRET=9z8y7x6w5v4u3t2s1r0q9z8y7x6w5v4u3t2s1r0q
# ── Database ─────────────────────────────────────────────
DATABASE_URL=postgresql://wasphere:Sup3rS3cur3DB!@postgres:5432/wasphere
POSTGRES_USER=wasphere
POSTGRES_PASSWORD=Sup3rS3cur3DB!
POSTGRES_DB=wasphere
# ── Dashboard API ────────────────────────────────────────
WA_SERVER_URL=http://wa-server:3001
CORS_ORIGINS=https://wa.example.com
LOG_LEVEL=info
JWT_EXPIRES_IN=7d
# ── WA Server ────────────────────────────────────────────
PORT=3001
SESSIONS_PATH=/app/sessions
MSG_DELAY_MIN_MS=1000
MSG_DELAY_MAX_MS=4000
TYPING_SIMULATION=true
RATE_LIMIT_PER_MIN=20
# ── Dashboard UI ─────────────────────────────────────────
NEXT_PUBLIC_API_URL=https://wa.example.com/api
WA_SERVER_INTERNAL_URL=http://wa-server:3001
Rotating Secrets
JWT_SECRET — Rotating this immediately invalidates all active user sessions. Everyone signed in to the dashboard will be logged out. To rotate:
- Generate a new value:
openssl rand -hex 32 - Update
.env - Restart the Dashboard API:
docker compose restart dashboard-api
WA_TOKEN — Must be updated in .env and both the Dashboard API and WA Server containers restarted simultaneously to avoid a broken state:
# Update .env, then:
docker compose restart dashboard-api wa-server
WEBHOOK_SECRET — Per-webhook secrets can be rotated individually from the dashboard without affecting other webhooks. The global default only applies to webhooks that don't have their own secret set.