Environment variables
Every variable the engine reads, from the four example files in the repository. The install stepper writes the production files; the development files are the ones nx serve reads on a laptop. A variable marked optional is commented out in the example and read only when set.
.env.example
Section titled “.env.example”Server
Section titled “Server”PORT. Example:53000.NODE_ENV. development and test are the only labels that unlock a development behaviour; every other label, and none, reads as production. Example:development.
Database
Section titled “Database”DATABASE_URL. Example:postgresql://postgres:postgres@localhost:55432/merchants_engine_dev.DATABASE_TEST_URL. Example:postgresql://postgres:postgres@localhost:55433/merchants_engine_test.
REDIS_HOST. Example:localhost.REDIS_PORT. Example:56379.REDIS_TEST_HOST. Example:localhost.REDIS_TEST_PORT. Example:56380.BETTER_AUTH_SECRET. Better Auth owns auth and sessions. BETTER_AUTH_SECRET is high-entropy, >= 32 bytes; rotate it when a person who knew it leaves. Example:change-in-production-min-32-bytes-abcde.BETTER_AUTH_URL. The API’s own externally-reachable base URL (used to build email links). Example:http://localhost:53000.AUTH_COOKIE_DOMAIN. Optional. Cross-subdomain session cookie, production only. Leading-dot root domain so the session is shared across the admin / storefront / api subdomains. The stepper leaves it unset: every install proxies /api on the admin host. Example:.shop.example.
Logging
Section titled “Logging”LOG_LEVEL. Example:debug.CORS_ORIGIN. CORS - admin dev (53200), admin e2e (53211), storefront dev (53300), storefront e2e (53311). Example:http://localhost:53200,http://localhost:53211,http://localhost:53300,http://localhost:53311.STOREFRONT_PUBLIC_BASE_URL. Public origins. In dev these are the local servers; the stepper writes the deployed origins, and the API refuses to boot in production without them. Example:http://localhost:53300.ADMIN_PUBLIC_BASE_URL. Example:http://localhost:53200.API_PUBLIC_BASE_URL. Example:http://localhost:53000.STOREFRONT_REVALIDATE_URL. Storefront revalidation - backend-to-backend signal that triggers SSR cache invalidation on product / category / CMS write events. Both values must match the storefront’s. The secret is a high-entropy string ≥ 32 bytes. Never log it. Example:http://localhost:53300/api/_internal/revalidate.STOREFRONT_REVALIDATE_SECRET. Example:change-in-production-min-32-bytes-12345.UNSUBSCRIBE_TOKEN_SECRET. Unsubscribe links in outbound mail are signed with this. Example:change-in-production-min-32-bytes-abcdef.
File Upload
Section titled “File Upload”MAX_FILE_SIZE. Example:10485760.ALLOWED_FILE_TYPES. Example:jpg,jpeg,png,gif,pdf,doc,docx.STORAGE_DRIVER. Asset storage - choose driver per environment. local - writes to ./uploads (dev fallback, no S3 needed) s3 - any S3-compatible bucket (set the S3_* vars below) Example:local.S3_ENDPOINT. Optional. Example:https://s3.<region>.example.S3_REGION. Optional.S3_BUCKET. Optional.S3_ACCESS_KEY. Optional.S3_SECRET_KEY. Optional.S3_PUBLIC_URL. Optional.PRIVATE_UPLOADS_DIR. Asset variant pipeline. Local driver - where uploaded originals are written. Must be OUTSIDE the public-serveduploads/root. Defaults to./uploads-private/. Example:./uploads-private.SIGNED_URL_SECRET. Secret used byLocalStorageDriver.signUrl()to mint HMAC-signed admin access URLs for private originals. High-entropy ≥ 32 bytes. Example:change-in-production-min-32-bytes-67890.RETAIN_ORIGINALS. Original-deletion gate: true keeps originals for re-processing (dev), false deletes them once every variant is verified (the production default). Example:true.
Payments
Section titled “Payments”STRIPE_API_KEY. Optional.
MAIL_TRANSPORT. Transport selection -console(default, no network) lets local dev + jest + e2e run without a Resend account.resendcalls the real API. Example:console.RESEND_API_KEY. Sending-only API key (re_…). REQUIRED when MAIL_TRANSPORT=resend.RESEND_FROM_EMAIL. Envelope-from address. Must be on a Resend-verified domain. Example:noreply@example.test.RESEND_FROM_NAME. Display name combined with RESEND_FROM_EMAIL to build the From: header. Example:My Store.RESEND_REPLY_TO. Default Reply-To header. Per-job overrides take precedence. Example:contact@example.test.CONTACT_FORM_RECIPIENT. Where the storefront contact form is delivered. Example:contact@example.test.RESEND_WEBHOOK_SECRET. Svix signing secret (whsec_…) for inbound webhooks at /api/v1/webhooks/resend. REQUIRED when MAIL_TRANSPORT=resend.MAIL_DEV_REDIRECT_TO. Development override - when set, every email is redirected here. Honoured under NODE_ENV=development and test only; a production boot refuses it.
Sign-up bot protection - Cloudflare Turnstile
Section titled “Sign-up bot protection - Cloudflare Turnstile”TURNSTILE_SECRET. Optional. Leave unset in dev and e2e: the widget stays inert and sign-up stays open.
Search-engine submission
Section titled “Search-engine submission”INDEXNOW_KEY. IndexNow key: 8 to 128 lower-case hex characters. The storefront serves the matching key file; the stepper writes it.INDEXNOW_HOST. Optional. Defaults to the storefront host.GSC_SUBMISSION_ENABLED. Google Search Console API (opt-in): Example:false.GSC_SITE_URL.sc-domain:<domain>for a domain property, orhttps://<domain>/for a URL-prefix property.GOOGLE_APPLICATION_CREDENTIALS. Absolute path to the service-account JSON key. Never commit the file.BING_API_KEY. Bing Webmaster API (opt-in):BING_SITE_URL. The verified site URL; falls back to STOREFRONT_PUBLIC_BASE_URL.
License
Section titled “License”LICENSE_FILE. Path of the Ed25519-signed license file issued for this store. Required in production (the API refuses to boot without one); unset on a developer box the API runs unlicensed and reportsmissingon GET /v1/admin/license.INVOICE_LOGO_PATH. Optional PNG drawn in the invoice header; without it the header carries the store name. The stepper writes it from the uploaded logo.
.env.production.example
Section titled “.env.production.example”Server
Section titled “Server”NODE_ENV. Example:production.PORT. Example:53000.API_GLOBAL_PREFIX. Example:api.LOG_LEVEL. Example:info.
Database and cache (compose service names)
Section titled “Database and cache (compose service names)”DB_PASSWORD. Example:<generated, 64 hex>.DATABASE_URL. Example:postgresql://merchants_app:<generated>@postgres:5432/merchants_engine?schema=public.REDIS_HOST. Example:redis.REDIS_PORT. Example:6379.REDIS_PASSWORD. Example:<generated, 64 hex>.
Auth and origins
Section titled “Auth and origins”BETTER_AUTH_SECRET. Example:<generated, 64 hex>.BETTER_AUTH_URL. The public API origin, used in every callback link inside an email. Example:https://api.shop.example.CORS_ORIGIN. Also Better Auth trustedOrigins. The admin reaches the API through its own origin. Example:https://shop.example,https://admin.shop.example.STOREFRONT_PUBLIC_BASE_URL. Example:https://shop.example.ADMIN_PUBLIC_BASE_URL. Example:https://admin.shop.example.API_PUBLIC_BASE_URL. Example:https://api.shop.example.STOREFRONT_REVALIDATE_URL. Example:https://shop.example/api/_internal/revalidate.STOREFRONT_REVALIDATE_SECRET. Example:<generated, 64 hex; the storefront box needs the same value>.UNSUBSCRIBE_TOKEN_SECRET. Example:<generated, 64 hex>.TURNSTILE_SECRET. Example:<entered and verified against siteverify>.
License
Section titled “License”LICENSE_FILE. Mounted read-only by the compose file from the path the stepper copied it to. Example:/app/config/license.json.
Uploads and asset storage
Section titled “Uploads and asset storage”MAX_FILE_SIZE. Example:10485760.ALLOWED_FILE_TYPES. Example:jpg,jpeg,png,gif,pdf,doc,docx.STORAGE_DRIVER. Example:s3.S3_ENDPOINT. Example:https://s3.<region>.example.S3_REGION. Example:<region>.S3_BUCKET. Example:<bucket, hyphens, no dots>.S3_ACCESS_KEY. Example:<entered and verified by a put, get and delete>.S3_SECRET_KEY. Example:<entered>.S3_PUBLIC_URL. Example:https://<bucket>.s3.<region>.example.SIGNED_URL_SECRET. Example:<generated, 64 hex>.RETAIN_ORIGINALS. Example:false.INVOICE_LOGO_PATH. Example:/app/config/invoice-logo.png.
MAIL_TRANSPORT. Example:resend.RESEND_API_KEY. Example:<entered and verified by a domain lookup and a test message>.RESEND_WEBHOOK_SECRET. Example:<entered>.RESEND_FROM_EMAIL. Example:noreply@shop.example.RESEND_FROM_NAME. Example:My Store.RESEND_REPLY_TO. Example:hello@shop.example.CONTACT_FORM_RECIPIENT. Example:hello@shop.example.MAIL_DEV_REDIRECT_TO. Honoured under NODE_ENV=development only; a production boot refuses a non-empty value.
Search-engine submission
Section titled “Search-engine submission”INDEXNOW_KEY.BING_API_KEY.BING_SITE_URL.GSC_SUBMISSION_ENABLED. Example:false.GSC_SITE_URL.GOOGLE_APPLICATION_CREDENTIALS.
Payments
Section titled “Payments”STRIPE_API_KEY.
apps/storefront/.env.example
Section titled “apps/storefront/.env.example”STOREFRONT_API_URL. API base URL the storefront fetches from. Browser-side proxy rewrites /v1/* to ${STOREFRONT_API_URL}/v1/* (see vite.config.ts). Example:http://localhost:53000/api.STOREFRONT_PUBLIC_BASE_URL. Public base URL the storefront uses for canonical, og:url, hreflang alternates, and JSON-LD URL fields. Production: the deployed origin. Example:http://localhost:53300.STOREFRONT_REVALIDATE_SECRET. Shared secret matched by the API webhook listener. Compared timing-safely on the inbound /api/_internal/revalidate request. Must equal the value of STOREFRONT_REVALIDATE_SECRET on the API side. Rotate it together with the API side. Example:change-in-production-min-32-bytes-12345.REDIS_HOST. Redis client for the inbound /api/_internal/revalidate webhook dedup (the same dedup the API applies to inbound webhooks). The storefront SSR process MUST resolve to a Redis instance in production (the boot guard in apps/storefront/src/server/lib/dedup-store.ts throws when NODE_ENV=production and REDIS_HOST is unset). In local dev these defaults match the dev Redis container on port 56379 per the reserved port table. Leave blank to opt into the in-memory fallback (acceptable for vitest + local single-process work; never for production). Example:localhost.REDIS_PORT. Example:56379.REDIS_PASSWORD.
apps/storefront/.env.production.example
Section titled “apps/storefront/.env.production.example”NODE_ENV. Example:production.PORT. Example:54300.HOST. Example:0.0.0.0.
Engine
Section titled “Engine”STOREFRONT_API_URL. On one box the storefront reaches the engine over the compose network (http://api:53000/api); on its own box it goes out over the public API origin. Example:https://api.shop.example/api.STOREFRONT_PUBLIC_BASE_URL. Public origin used for canonical, og:url, hreflang, JSON-LD URL fields. Example:https://shop.example.STOREFRONT_REVALIDATE_SECRET. Must equal the engine’s STOREFRONT_REVALIDATE_SECRET; a storefront-only box is asked for it and the engine box prints it at the end of its install. Example:<the engine's value>.VITE_TURNSTILE_SITE_KEY. Public; read at build time and passed by the stepper as a build argument. Example:<entered>.
Revalidate dedup store (compose service names)
Section titled “Revalidate dedup store (compose service names)”REDIS_HOST. Example:redis.REDIS_PORT. Example:6379.REDIS_PASSWORD. Example:<generated, 64 hex>.
Public API origin, for links the page builds itself
Section titled “Public API origin, for links the page builds itself”API_URL. Example:https://api.shop.example/api.