Skip to main content

Configuration

The backend is configured entirely from environment variables. python-dotenv loads .env from the repo root in manage.py, wsgi.py and celery.py; inside Docker, compose passes the same names through the x-app-env anchor. Values are read with os.getenv in EMAW_BACKEND/settings/*.py.

Names only

This page lists variable names. Values live in the shared .env distributed through the password manager and must never be committed, pasted into chat or written into documentation. The authoritative list, with defaults and behaviour notes, is CLAUDE.md ("Settings & Environment").

Required to boot​

VariableNotes
DJANGO_SETTINGS_MODULEEMAW_BACKEND.settings.development or .production (defaults to development)
SECRET_KEYAlso signs JWTs (SIMPLE_JWT["SIGNING_KEY"])
JWT_SIGNING_KEYRead into settings but not used for signing today; kept for forward compatibility
EMAW_SALTUsername hashing salt. Must match the database you connect to or nobody can log in. Production reads it from Key Vault instead (USE_LOCAL_SALT)
DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, DB_PORTPostgreSQL. DB_HOST is a Tailscale IP in dev. DB_PORT defaults to 5432
REDIS_URLDefault redis://redis:6379/0 in compose, redis://localhost:6379/0 on the host. Celery uses databases /1 (broker) and /2 (results) unless CELERY_BROKER_URL / CELERY_RESULT_BACKEND override
TUNNEL_TOKENFor the compose tunnel service only

ALLOWED_HOSTS in .env is not read; the list is hard-coded per settings module.

Email​

VariableFor
MS_TENANT_ID, MS_CLIENT_ID, MS_CLIENT_SECRET, MS_SENDER_UPNTransactional mail via Microsoft Graph (sign-up codes, password reset, receipts, order emails). DEFAULT_FROM_EMAIL falls back to support@emaw-app.com
CONTACT_FORM_RECIPIENTSWhere the website contact form goes
ACS_CONNECTION_STRING, ACS_SENDER_ADDRESS, ACS_SENDER_NAME, ACS_REPLY_TO, ACS_EVENTGRID_WEBHOOK_KEYMarketing campaigns via Azure Communication Services
CAMPAIGN_SENDING_ENABLED, CAMPAIGN_SEND_ALLOWED_GROUP_IDS, CAMPAIGN_SEND_RATE_PER_MINUTE, CAMPAIGN_BATCH_SIZE, CAMPAIGN_LARGE_THRESHOLD, CAMPAIGN_REQUIRE_TEST_SENDCampaign send controls. Keep sending off in dev; if on, allow only group 36
PUBLIC_BASE_URL, WEBSITE_BASE_URL, CAMPAIGN_POSTAL_ADDRESSLinks and footer in emails

Storage​

VariableFor
AZURE_BLOB_KEYAzure Blob (emawblob/media), Django's default storage. Account name and container are hard-coded
AZURE_BLOB_SATSAS token used for generated verse audio
R2_ACCOUNT_ID, R2_BUCKET, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_PUBLIC_BASE_URLCloudflare R2 for website resource media. Uploads return 503 until the four credentials are set

Payments and commerce (Stripe)​

VariableFor
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_API_VERSIONStripe client; API version is pinned (default 2026-09-30.endive). Test keys in dev
CHECKOUT_RETURN_URL, CHECKOUT_SUCCESS_URL, CHECKOUT_CANCEL_URLWhere Stripe sends the browser back
STORE_PAYMENT_METHOD_CONFIGURATION, STORE_AUTOMATIC_TAX, STORE_PRODUCT_TAX_CODE, STORE_ORDER_NOTIFY_RECIPIENTS, STORE_SHIPPING_COUNTRIES, STORE_LOW_STOCK_THRESHOLDStore
EVENTS_PAYMENT_METHOD_CONFIGURATION, EVENTS_NOTIFY_RECIPIENTSEvents (default to the store values)
DONATIONS_PAYMENT_METHOD_CONFIGURATION, DONATIONS_STRIPE_PRODUCT_ID, DONATIONS_PORTAL_CONFIGURATION, DONATIONS_NOTIFY_RECIPIENTS, DONATIONS_FEE_PERCENT, DONATIONS_FEE_FIXED_CENTS, DONATIONS_MIN_AMOUNT_CENTS, DONATIONS_MAX_AMOUNT_CENTS, DONATIONS_ORG_LEGAL_NAME, DONATIONS_ORG_EINDonations

Shipping​

VariableFor
SHIPPING_RATE_SOURCEfulfillment (historic warehouse costs, the default) or carriers (live quotes)
STORE_FALLBACK_SHIPPING_CENTS, STORE_FALLBACK_SHIPPING_LABEL, SHIPPING_RATE_CACHE_SECONDSFlat fallback (default 599) and quote cache
SHIPPING_ORIGIN_LINE1, SHIPPING_ORIGIN_CITY, SHIPPING_ORIGIN_STATE, SHIPPING_ORIGIN_POSTAL_CODE, SHIPPING_BOXES_JSONShip-from address and box sizes
SHIPPING_HISTORY_RECENT_DAYS, SHIPPING_HISTORY_HALF_LIFE_DAYS, FULFILLMENT_TIME_ZONEFulfilment-history pricing
UPS_CLIENT_ID, UPS_CLIENT_SECRET, UPS_ACCOUNT_NUMBER, UPS_BASE_URL, UPS_SERVICESUPS (used only when all credentials are set)
USPS_CLIENT_ID, USPS_CLIENT_SECRET, USPS_BASE_URL, USPS_MAIL_CLASSES, USPS_PRICE_TYPEUSPS

Warehouse (Deposco)​

VariableFor
DEPOSCO_USERNAME, DEPOSCO_PASSWORD, DEPOSCO_CODEAll three turn the integration on; DEPOSCO_ENABLED=0 forces it off
DEPOSCO_BASE_URLDefaults to the North America sandbox; sandbox in dev
DEPOSCO_BUSINESS_UNIT, DEPOSCO_ORDER_PREFIX (distinct per environment), DEPOSCO_ORDER_SOURCE, DEPOSCO_SHIP_VIA_MAP (JSON), DEPOSCO_TIMEOUT, DEPOSCO_MIN_REQUEST_INTERVALOrder mapping
DEPOSCO_SYNC_INTERVAL_MINUTES, DEPOSCO_SYNC_MAX_AGE_DAYS, DEPOSCO_INVENTORY_SYNC_MINUTESBeat schedule
VariableFor
OpenAIThe OpenAI API key (non-standard name; OPENAI_API_KEY is a fallback). Used by verse finder, TTS, Whisper recitation analysis, email template drafts
AI_SEARCH_ENABLED, AI_SEARCH_BASE_VERSIONRegisters /rest/bibles/search/ and /similar/; base version defaults to NIV
QDRANT_HOST, QDRANT_PORT, QDRANT_TIMEOUTDefault qdrant / 6333 in compose
EMBEDDINGS_DIR, EMBEDDINGS_URL, QDRANT_BATCH_SIZE, QDRANT_MAX_RETRIES, QDRANT_BACKOFF_SECONDSload_bible_embeddings
AI_TEMPLATE_ENABLED, AI_TEMPLATE_MODELAI email template drafts (default model gpt-4o)

Google​

VariableFor
GOOGLE_SIGNIN_CLIENT_IDSComma-separated web/iOS/Android OAuth client ids whose ID tokens may sign in app users. Empty = Google sign-in off
GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, GOOGLE_OAUTH_REDIRECT_URIS, GOOGLE_TOKEN_KEYSeparate internal OAuth client for Google Meet rooms, calendar invites, recordings and attendance; GOOGLE_TOKEN_KEY defaults to a derivation of SECRET_KEY

Setup runbook: docs/GOOGLE_SIGN_IN_SETUP.md.

CRM and geography​

VariableFor
AZURE_MAPS_KEY, GEOCODING_ENABLEDGeocoding contacts and venues
GEOGRAPHY_ASSIGN_SYNC_MAX, GEOGRAPHY_RUN_STALE_MINUTESBulk region/area assignment runs
CRM_CONTACT_LIST_CACHE_SECONDSRedis cache for contact list pages (0 disables)
PLANNING_CENTER_CLIENT_ID, PLANNING_CENTER_SECRET, PCO_MIGRATION_COMPLETEPlanning Center importers (migration only)
APP_EXTRA_ORIGINS, COMMERCE_EXTRA_ORIGINSExtra CORS / hand-off / checkout origins beyond the hard-coded lists

Security and logging​

VariableFor
CF_ACCESS_TEAM_DOMAIN, CF_ACCESS_AUDWith both set, staff endpoints require a valid Cloudflare Access JWT. Unset locally
JWT_ROTATE_REFRESH_TOKENS, REFRESH_ROTATION_GRACE_SECONDSGlobal refresh-token rotation (off until old mobile builds retire)
TRUST_CF_CONNECTING_IPUse Cloudflare's header for the client IP
LOG_DIR, DJANGO_LOG_LEVEL, LOG_TO_FILE, LOG_JSONLogging (see Logs and monitoring)

Tests​

TEST_DB_NAME, TEST_DB_USER, TEST_DB_PASSWORD, TEST_DB_HOST, TEST_DB_PORT replace the matching DB_* for the test process only. See Testing.

Known gaps​

  • These are read by settings but missing from the compose x-app-env anchor, so they do not reach the containers unless added: SHIPPING_RATE_SOURCE, SHIPPING_HISTORY_RECENT_DAYS, SHIPPING_HISTORY_HALF_LIFE_DAYS, FULFILLMENT_TIME_ZONE, EVENTS_PAYMENT_METHOD_CONFIGURATION, EVENTS_NOTIFY_RECIPIENTS, CONTACT_FORM_RECIPIENTS, GEOGRAPHY_ASSIGN_SYNC_MAX, GEOGRAPHY_RUN_STALE_MINUTES, CRM_CONTACT_LIST_CACHE_SECONDS, OPENAI_API_KEY.
  • Present in .env but never read: EMAIL_PASSWORD, QUICK_BOOKS_CLIENT_ID, QUICK_BOOKS_CLIENT_SECRET, ALLOWED_HOSTS.

Tracked on the known gaps page.