Social LiveDocumentationPLATFORM DOCS

Environment Variables

Every backend setting (env var), what it controls, and the safe production value.

Last updated Thu Jul 16 2026 00:00:00 GMT+0000 (Coordinated Universal Time)

TL;DR -This is the master list of the backend's "settings dials" -the environment variables that control everything from the database connection to SMS delivery to in-app-purchase verification -with the correct production value for each.

Who this is for -DevOps & Operations: your reference sheet, keep it open when editing a server config. Engineering: the source of truth when adding a new variable. Executive & Product: skim the section headings to see what moving parts exist.

An environment variable is a named setting the server reads when it starts — like DATABASE_URL or PORT. Rather than hard-coding secrets and hostnames into the app, we keep them in a single settings file so the same code can run in development, staging, and production just by swapping the file. That file is .env.production and it lives only on the server, never in the code repo, because it holds real secrets.

Two example templates in the repo document every variable without any real secrets: backend/.env.example (development) and .env.production.example (production, at the repo root). When someone adds a new variable, they add it to these templates in the same change -that keeps this page and the templates honest.

The rest of this page is the full reference, grouped by area. Each table lists the variable, what it does, and the value to use in production.

Core

The essentials: what mode the server runs in, which port it listens on, how it reaches the database, and the secrets used to sign login tokens.

VariablePurposeProduction
NODE_ENVEnables/disables every dev overrideproduction
PORTAPI port3000
DATABASE_URLPostgres connectioncomposed in docker-compose
PUBLIC_APP_URL / CORS_ORIGINAllowed originsreal hosts only
API_DOMAIN / ADMIN_DOMAIN / LIVEROOM_DOMAIN / DOCS_DOMAINHostnames used by scripts/provision-server.sh for nginx + certbot (not read by the backend)e.g. api.social-live.app, docs.owletvpn.com
LETSENCRYPT_EMAILcertbot registration/expiry-notice address (provision script only)set
JWT_ACCESS_SECRET / JWT_REFRESH_SECRETToken signinglong random strings

OTP & messaging

These control how users receive login codes (by SMS or email), how social sign-in is validated, and how push notifications are sent. The OTP_TEST_* variables are QA conveniences that must be pruned before launch.

VariablePurposeProduction
OTP_SECRETOTP hash pepper -fails closed if unset in prodlong random string
TERMS_UPDATED_ATISO date the Terms of Use last changed; users with older acceptedTermsAt must re-accept in-app (see Authentication)empty (code default)
OTP_ALLOW_UNSENTSucceed without SMS/email providerfalse
OTP_EXPOSE_CODE(Dev) echo codes in responsesfalse (ignored in prod anyway)
OTP_TEST_CODE(Dev) fixed code for everyoneempty in prod
OTP_TEST_PHONE_NUMBERSQA phone allowlistprune before launch
OTP_TEST_EMAILS / OTP_TEST_EMAIL_CODEQA email allowlist + fixed codeprune before launch
TWILIO_ACCOUNT_SID / AUTH_TOKEN / FROM_NUMBERSMS deliveryset
RESEND_API_KEY / RESEND_FROM_EMAILEmail deliveryset
APPLE_SIGNIN_CLIENT_IDSAllowed audiences for Sign in with Apple tokens (bundle id / Service ID)com.sociallive.app
GOOGLE_SIGNIN_CLIENT_IDSAllowed audiences for Google ID tokens (iOS + web/server client ids)set
FIREBASE_SERVICE_ACCOUNT_BASE64Base64 Firebase service-account JSON for FCM push (Android + iOS via APNs). Empty disables push silentlyset

Media (SFU)

The SFU is the media server that relays one broadcaster's live video and audio out to many viewers. These variables tell it which network address to advertise and which UDP ports it may use for real-time media.

VariablePurpose
SFU_LISTEN_IPBind address (0.0.0.0 in container)
SFU_ANNOUNCED_IPPublic IP written into ICE candidates
SFU_RTC_MIN_PORT / MAX_PORTPublished UDP range (40000–40499 in production). One port per WebRTC transport (~2 per participant); a 101-port range exhausts under a modest stress run (no more available ports in the backend log). Keep the range >= 500 wide and mirror it in the ufw rule.

In-app purchases

These verify real money purchases with Apple and Google before any coins are credited. Getting them right is what stops fraudulent or replayed receipts.

VariablePurpose
APPLE_IAP_ISSUER_ID / KEY_ID / PRIVATE_KEYApp Store Connect API key (ES256 .p8)
APPLE_IAP_BUNDLE_IDExpected bundle id (com.sociallive.app)
APPLE_IAP_ENVIRONMENTproduction or sandbox
GOOGLE_IAP_CLIENT_EMAIL / PRIVATE_KEYPlay service account
GOOGLE_IAP_PACKAGE_NAMEAndroid applicationId (com.sociallive.app)
GOOGLE_RTDN_AUDIENCEExpected OIDC audience = the RTDN push URL
GOOGLE_RTDN_SERVICE_ACCOUNT_EMAILExpected Pub/Sub push identity
GOOGLE_IAP_VOIDED_SWEEP_HOURSRefund sweep interval (0 = off; prod 6)
ALLOW_DEV_COIN_GRANTSDev coin grants without a store receipt -must be false in production once real IAP is live

Misc

Everything else: diagnostics toggles, payout scaffolding, object storage for media, the admin login token, the docs container binding, database bootstrap, and the offsite backup destination.

VariablePurpose
ENABLE_LIVE_DIAGNOSTICSPersist live room event logs
LIVE_PERF_LOGSVerbose live-path performance logging
STRIPE_SECRET_KEY / STRIPE_CONNECT_CLIENT_IDPayout rails (scaffolded)
S3_ENDPOINT / S3_BUCKET / S3_REGION / S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEYObject storage for media (Cloudflare R2 in prod, MinIO in dev). Unset = media falls back to local disk
S3_PUBLIC_BASE_URLOptional public bucket/CDN base for avatars/banners; empty = served through the backend /api/media proxy
ADMIN_ACCESS_TOKENAdmin portal login token
DOCS_BIND / DOCS_PORTLoopback bind + host port for the public docs portal container (3013 in prod); nginx fronts it. No token -the docs site is public and read-only
POSTGRES_DB / USER / PASSWORDDatabase bootstrap
BACKUP_RCLONE_REMOTEOffsite backup destination (rclone remote path, e.g. b2:social-live-backups); read by scripts/db-backup.sh, not the backend -see Backup & Restore

Conventions

A few rules that apply across all of the above:

  • Multiline keys (*_PRIVATE_KEY) may be stored single-line with \n escapes -services normalize them.
  • Env-only changes deploy with up -d <service> (no --build).
  • docker-compose.prod.yml has no URL fallback defaults: PUBLIC_APP_URL, ADMIN_API_BASE_URL, and the two LIVE_ROOM_SIMULATOR_* URLs are required (${VAR:?}), and every compose command needs --env-file .env.production -see Server Provisioning.
  • Never commit real values; the two example files are the documentation of record for new variables -update them in the same PR that adds one.