Skip to main content

Environment variables

All three services read the repo-root .env in development; under Compose the values come from the environment: blocks. Validation runs once at boot with zod — a malformed value fails the process immediately with the variable's name.

Secrets

VariableUsed byRequiredNotes
ENCRYPTION_KEYapi (admin), workeryes64 hex chars (32 bytes). AES-256-GCM key for every stored credential. The public API works without it; the admin surface and the worker refuse to.
HMAC_SECRETapi, workeryes≥ 16 chars. Signs tracking ids. Changing it invalidates tracking for pushes already in flight.
ADMIN_API_TOKENapi, dashboarddashboard only≥ 24 chars. Unset → /v1/admin answers 404.
SESSION_SECRETdashboardyesSigns the session cookie.
ADMIN_PASSWORD_HASHdashboardyesargon2id hash from bun run generate:admin-password, base64-encoded as printed. Only used while no members exist.

Datastores

VariableDefaultNotes
MONGO_URLmongodb://localhost:27017Set to mongodb://mongo:27017 by Compose.
MONGO_DB_NAMEopennotification
REDIS_URLredis://localhost:6379Must be reachable at API boot or the API exits 1.
MONGO_CACHE_GB1WiredTiger cache; about half the RAM you can spare. Compose only.
REDIS_MAXMEMORY512mbCompose only; policy is always noeviction.

API

VariableDefaultNotes
API_PORT3000
API_PUBLIC_URLhttp://localhost:3000The URL SDKs call. Informational (setup hints), not routing.
CORS_ORIGINS(none)Comma-separated browser origins allowed on /v1/subscriptions and /v1/e.
SUBSCRIBE_RATE_LIMIT10Per client IP per minute on subscribe/rotate — the public key is not a secret.
SEND_RATE_LIMIT100Per API key per minute on /v1/notifications.
EVENT_RATE_LIMIT600Per client IP per minute on tracking pings.
EVENT_RETENTION_DAYS180TTL on the events time-series collection. Applied when the collection is created.
API_KEY_CACHE_TTL60000 msHow long a verified key skips bcrypt.

Rate limits are per process. With N API replicas the effective ceiling is N × limit, which is fine for what they guard against (a runaway SDK loop, a scraper on subscribe).

Worker

VariableDefaultNotes
APNS_CONCURRENCY200Jobs in flight on the push-ios queue. One HTTP/2 connection multiplexes them.
FCM_CONCURRENCY100push-android.
WEBPUSH_CONCURRENCY100push-web.
FANOUT_CONCURRENCY4Campaigns being fanned out at once.
WEBHOOK_CONCURRENCY10Webhook batches in flight.
PUSH_RATE_LIMIT_MAX / PUSH_RATE_LIMIT_DURATION_MS5000 / 1000BullMQ limiter per push queue.
PUSH_MAX_ATTEMPTS3Retries for transient failures only (429, 503, UNAVAILABLE, INTERNAL). Permanent failures never retry.
PUSH_BACKOFF_MS2000Exponential backoff base.
INACTIVE_SUBSCRIPTION_DAYS90The maintenance sweep opts out subscriptions untouched this long.
SHUTDOWN_TIMEOUT_MS30000How long SIGTERM waits for in-flight jobs. Keep Compose's stop_grace_period above it.

Dashboard

VariableDefaultNotes
API_URLWhere the dashboard server reaches the API. Never exposed to the browser. Under Compose it is fixed to http://api:3000.
DASHBOARD_PORT3001Loopback host port for the dashboard container.
INSECURE_COOKIESfalsetrue drops the Secure flag for a plain-http localhost run.

Dokploy / Traefik file

VariableDefaultNotes
API_DOMAINRequired by docker-compose.dokploy.yml.
DASHBOARD_DOMAINRequired by docker-compose.dokploy.yml.
DOCS_DOMAINRequired by docker-compose.dokploy.yml.
TRAEFIK_NETWORKdokploy-networkThe external Traefik network.
TRAEFIK_CERT_RESOLVERletsencrypt
API_IMAGE / WORKER_IMAGE / DASHBOARD_IMAGEghcr.io/aproder/opennotification-*:latestPull instead of build.
LOG_LEVELinfo

Backups (--profile backup)

VariableDefaultNotes
BACKUP_INTERVAL_HOURS24Time between mongodump runs.
BACKUP_KEEP_DAYS14Archives older than this are deleted after each successful run.
BACKUP_DIRmongo_backups volumeSet to a host path to write dumps there instead.

VAPID (informational)

VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT in .env are produced by generate:keys for convenience. They are per app, stored in the database when you configure web push for an app, and not read by the services from the environment.