Configuration Guide
Complete reference for NorthRelay environment variables, services, and configuration
Configuration Guide
Mailbox version boundary: the hosted mailbox service uses Stalwart Community 0.16.23 with native account administration. The application sending instance has a separate configuration and lifecycle. Older SQL-directory and container examples below must not be applied to the production mailbox; see Hosted email access and the platform
infra/mailbox-nextdeployment source.
Full reference for configuring NorthRelay services, environment variables, and integrations.
Environment Variables
Required Variables
These must be set for NorthRelay to function:
| Variable | Description | Example |
|---|---|---|
DATABASE_URL | PostgreSQL connection string | postgresql://user:pass@postgres:5432/northrelay |
NEXTAUTH_SECRET | Session encryption key (min 32 chars) | openssl rand -base64 32 |
NEXTAUTH_URL | Canonical URL of your dashboard | https://northrelay.ca |
NEXT_PUBLIC_APP_URL | Public-facing app URL | https://northrelay.ca |
Stalwart Mail Server
| Variable | Description | Example |
|---|---|---|
STALWART_API_URL | Relay Stalwart Management API | http://stalwart:8080 |
STALWART_API_TOKEN | Relay admin token | Hex token from config |
STALWART_WEBHOOK_SECRET | Webhook signature verification | Random secret string |
STALWART_MAILBOX_URL | Mailbox Stalwart API (for inbox) | https://mail.northrelay.ca |
STALWART_MAILBOX_TOKEN | Mailbox admin token | Hex token from mailbox config |
Important: The relay and mailbox Stalwart instances have different admin tokens. The relay handles outbound queuing; the mailbox handles inbound delivery and JMAP access.
Email Sending
| Variable | Description | Default |
|---|---|---|
SMTP_FROM | Sender address for system emails | no-reply@northrelay.ca |
SMTP_FROM_NAME | Sender display name | NorthRelay |
Redis
| Variable | Description | Default |
|---|---|---|
REDIS_HOST | Redis hostname | localhost |
REDIS_PORT | Redis port | 6379 |
REDIS_PASSWORD | Redis password | (empty) |
Cron Jobs
| Variable | Description | Example |
|---|---|---|
CRON_SECRET | Bearer token for cron endpoints | Random secret string |
Optional / Development
| Variable | Description | Default |
|---|---|---|
NODE_ENV | Runtime environment | production |
LOG_LEVEL | Logging verbosity | info |
Stalwart Two-Layer Configuration
Stalwart Mail Server uses two configuration storage layers. Understanding this is critical for operations.
Layer 1: TOML Config File (Static)
The TOML file is loaded at startup and defines the base configuration:
# Example: config.prod.toml
[server]
hostname = "relay.northrelay.ca"
max-connections = 1024
[server.listener.smtp]
bind = ["0.0.0.0:25"]
protocol = "smtp"
[server.listener.management]
bind = ["127.0.0.1:8080"]
protocol = "http"Changes to the TOML file require a restart or config reload to take effect.
Layer 2: RocksDB Database (Runtime)
Runtime state is stored in Stalwart's embedded RocksDB database. This includes:
- Blocked IP addresses (from auto-ban)
- Session counters
- Some dynamic settings overridden at runtime
Warning: Database entries take precedence over TOML file entries for the same key. If a setting exists in both places, the database value wins. Use the Stalwart CLI to inspect and manage database configuration.
Managing Configuration Layers
# List all database config keys matching a pattern
docker exec stalwart /usr/local/bin/stalwart-cli \
-u http://localhost:8080 -c admin:TOKEN \
server list-config 'server'
# Add a runtime config value
docker exec stalwart /usr/local/bin/stalwart-cli \
-u http://localhost:8080 -c admin:TOKEN \
server add-config 'some.setting.key' "value"
# Delete a runtime config value (reverts to TOML default)
docker exec stalwart /usr/local/bin/stalwart-cli \
-u http://localhost:8080 -c admin:TOKEN \
server delete-config 'some.setting.key'
# Reload configuration from TOML file
docker exec stalwart /usr/local/bin/stalwart-cli \
-u http://localhost:8080 -c admin:TOKEN \
server reload-configFor full Stalwart setup details, see Stalwart Setup.
Database Configuration
PostgreSQL Connection
NorthRelay uses PostgreSQL 15 with Prisma as the ORM. The schema source of truth is prisma/schema.prisma.
# Connection string format
DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE"
# Docker Compose internal
DATABASE_URL="postgresql://north_user:password@postgres:5432/northrelay"
# External access
DATABASE_URL="postgresql://north_user:password@51.161.10.58:5432/northrelay"Schema Management
# Validate schema
npx prisma validate
# Generate Prisma client after schema changes
npx prisma generate
# Push schema to database (development - no migration history)
npx prisma db push
# Run migrations (production - creates migration history)
npx prisma migrate deploy
# Open Prisma Studio (GUI)
npx prisma studioKey Schema Conventions
- All table names use snake_case with
@@mapdirective - Key enums:
plan_tier,pool_type,user_role,user_status,email_status - Every model requires a corresponding Prisma model in
schema.prisma
Redis Configuration
Redis is used for:
- Rate limiting - Per-user SMTP send rate enforcement
- Caching - Session data, DNS lookup caching
- Queues - Background job processing
# Docker Compose configuration
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD}
ports:
- "6379:6379"
volumes:
- redis_data:/dataIf Redis is unavailable, NorthRelay falls back to in-memory caching with reduced functionality.
Cron Jobs
NorthRelay uses a lightweight Alpine curl container to trigger scheduled API endpoints:
| Schedule | Endpoint | Purpose |
|---|---|---|
| Every 60s | /api/v1/emails/schedule/process | Process scheduled email sends |
| Every 60s | /api/v1/emails/batch/process | Process batch email operations |
| Every 60min | /api/cron/reputation-snapshot | Calculate reputation scores and detect incidents |
All cron requests authenticate with the CRON_SECRET as a Bearer token:
# Example cron request
curl -s -H "Authorization: Bearer ${CRON_SECRET}" \
http://app:3000/api/v1/emails/schedule/processThe cron container is defined in docker-compose.yml and runs continuously with crond managing the schedule.
DKIM Configuration
NorthRelay uses OpenDKIM on relay VPSes for DKIM signing, not Stalwart's built-in DKIM.
Why Relay-Based Signing
- Stalwart's DKIM is disabled (
queue.outbound.sign.enable = false) - OpenDKIM is battle-tested for dynamic domain signing
- Signing at the network edge means signatures are never broken by intermediate processing
Adding DKIM for a New Domain
# 1. Generate RSA-2048 key
openssl genrsa -out yourdomain.com.key 2048
# 2. Extract public key for DNS
openssl rsa -in yourdomain.com.key -pubout -outform PEM | \
grep -v '^-' | tr -d '\n'
# 3. Copy key to relay VPS
scp yourdomain.com.key relay:/opt/relay/dkim/yourdomain.com.default.key
# 4. Add to OpenDKIM KeyTable
echo "default._domainkey.yourdomain.com yourdomain.com:default:/etc/opendkim/keys/yourdomain.com.default.key" \
>> /opt/relay/KeyTable
# 5. Add to OpenDKIM SigningTable
echo "*@yourdomain.com default._domainkey.yourdomain.com" \
>> /opt/relay/SigningTable
# 6. Restart OpenDKIM
docker compose restart opendkimDNS Record
Publish the public key as a TXT record:
default._domainkey.yourdomain.com TXT "v=DKIM1; k=rsa; p=YOUR_PUBLIC_KEY_BASE64"OpenDKIM Configuration
Key settings in opendkim.conf:
SubDomains yes # Signs subdomain emails with parent domain key
Canonicalization relaxed/simple
Mode s # Sign only (not verify)Domain Separation
NorthRelay enforces strict domain separation between corporate and user traffic:
| Domain | Purpose | Usage |
|---|---|---|
northrelay.ca | Corporate | Dashboard, docs, admin email - never for user SMTP traffic |
north-pipe.ca | System | User subdomains, HELO identity, return-path - all user email traffic |
This separation protects the corporate domain reputation from user sending behavior.
User Subdomain Format
Each user gets a subdomain on the system domain:
{prefix}@{prefix}.north-pipe.caFor example: quantum@quantum.north-pipe.ca
Next Steps
- Stalwart Setup - Detailed mail server configuration
- Monitoring - Health checks and alerting
- Runbooks - Operational procedures