Prepare production configuration and secrets

Prepare application settings, production service credentials, and environment files, and understand how deployment uploads variables and secrets.

Before launch, prepare production configuration from the branding, service URLs, and test credentials used during development. This article builds on Branding and project configuration and Optional services, focusing on which values the final deployment uses.

1. Know where configuration belongs

File or locationWhat it storesHow changes take effect
config/base.tsBranding, sender, support email, administrator emails, and related settingsBuild and deploy
config/deploy.tsFeature switches, email provider, additional allowed domains, and related settingsBuild and deploy
config/products.tsProducts, Plans, Price IDs, and purchase entitlementsBuild and deploy
config/i18n.ts, locales/Supported languages and UI textBuild and deploy
wrangler.jsoncAccount, Worker name, resource bindings, domains, and schedulesUpdate through the deployment flow
.envLocal development environment valuesRestart the local development server
.env.productionEnvironment values and application secrets for remote deploymentRun a deployment or update command
Third-party service dashboardsSending domains, OAuth callbacks, webhooks, and allowed hostsSave on the relevant platform, then redeploy if local secrets also change

The example.vars file lists variables. It is not a complete configuration ready for production. Configure only the services you enable. You do not need to activate unused providers just to eliminate every diagnostic message.

The first run of deploy:init collects the required information and creates or supplements .env.production. If you prepare the file manually beforehand, the script reads its existing values.

2. Keep the existing SAAS_SECRET

The current deployment scripts require SAAS_SECRET to exist in both .env and .env.production, with exactly the same value. If the production file lacks it during initial deployment, the script uses the value already in the local project. Subsequent updates and domain changes also check that the values match.

Do not generate a different production key for this project on launch day, or arbitrarily replace the values in both files to pass the check. The key is used to process existing encrypted data. Replacing it may make historical data unreadable.

If you have not generated a key for a new project, return to Local development and complete initialization. A separate new project can have its own key. Rotating a key for an existing project requires a dedicated data migration plan.

This requirement does not mean that other services should share test credentials. Configure Stripe, Turnstile, OAuth, and other services for their respective environments and sites.

3. Check production service configuration

ServiceValues to checkLaunch requirements
TurnstileCLOUDFLARE_TURNSTILE_SITE_KEY, CLOUDFLARE_TURNSTILE_SECRET_KEYBoth belong to the same production widget, whose host list includes the production site
ResendRESEND_API_KEYThe sending domain is verified, and the key has permission to send
StripeSTRIPE_CONNECTION_ID, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETLive payments use matching live configuration, with Price IDs from the same mode
GitHub OAuthGITHUB_CLIENT_ID, GITHUB_CLIENT_SECRETThey belong to an application configured with the production callback
Google OAuthGOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRETOrigins, callbacks, and application publishing status meet the requirements for actual use
Signed R2 accessR2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEYConfigure only when using API features such as presigned uploads or downloads
External notificationsVariables referenced by enabled destinations in config/notifications.tsDestination URLs and tokens match the intended receiving channels

Ordinary reads and writes through an R2 binding do not necessarily require R2 API keys. Prepare credentials for the interfaces you actually use, as described in Storage.

Resend is the default email provider. To use Cloudflare's email service, you need the corresponding account capabilities, an EMAIL binding, and allowed senders. Changing only the provider name in the configuration is not enough. See Email for setup instructions for both options.

You can use test Turnstile keys to initially verify that the deployment works. Replace them with real configuration before opening the site to users. The script mainly checks whether values are present. It does not determine whether you entered test values.

4. Set the site URL

Initial deployment uses the workers.dev URL generated by the script. When you connect a production domain, domain:set updates VITE_SITE_URL in .env.production to the production HTTPS origin, for example:

VITE_SITE_URL="https://app.example.com"

This shows one field, not a complete environment file. The site URL must not contain a path such as /docs, query parameters, or a fragment.

The production build uses this URL to generate site-related content, and the runtime also depends on it. Changing only the value in the Cloudflare dashboard without updating local configuration and redeploying may leave page links inconsistent with the runtime URL.

The hosts setting in config/deploy.ts allows additional hosts. The host in VITE_SITE_URL is allowed automatically, so you usually do not need to add it again. The trustedOrigins setting configures trust for cross-origin requests. It is not a DNS binding or OAuth callback list. Launching a same-origin site does not require adding a wildcard here.

For domains, the root domain and www, and file domains, see Production domains and third-party callbacks.

5. Understand how variables are uploaded

After reading .env.production, the deployment script processes values according to the project's maintained list of public fields:

  • Public fields are passed as Worker variables, such as VITE_SITE_URL, the Turnstile Site Key, and OAuth Client IDs.
  • Other nonempty fields are uploaded as secrets with the deployment, such as SAAS_SECRET, OAuth Client Secrets, and Stripe signing secrets.
  • Empty values are skipped. They do not automatically delete existing remote secrets.

Public fields are not identified solely by the VITE_ prefix. Do not add the VITE_ prefix to passwords, access tokens, or signing secrets. Variables with this prefix may end up in client build artifacts.

Cloudflare deployment credentials such as CLOUDFLARE_API_TOKEN must not go in .env.production. The script refuses to deploy them as application environment values. For interactive development, you can sign in through Wrangler. For automated deployment, manage credentials in the execution environment.

For routine changes, update the local production configuration, then run npm run deploy:update. If you change only a remote secret during an emergency, a later deployment may overwrite it. Update the protected configuration records as well.

When disabling a service or revoking credentials, clearing the local value is not enough. First update feature switches and callers, then revoke the credentials with the provider and remove remote secrets as needed. Do not assume an empty string has completed the cleanup.

6. Review product content before release

As you prepare configuration, also check that:

  • The site name, logo, support email, sender, and legal pages have been replaced with your own content.
  • The addresses in adminEmails can receive email and complete verification.
  • Every Plan being sold has a real Price, with matching amount, currency, billing interval, and entitlement descriptions.
  • Affiliate commission rates, coupon offers, and complimentary entitlements match this release's plans.
  • Documentation, blog pages, and the language menu show only content ready for release. Template test articles are not presented as a production help center.
  • External services specific to your business have been deployed, with matching bindings and credentials on the main site.

You can skip Stripe for an initial release that does not accept payments, but purchase options and product descriptions should reflect that decision. Do not present plans with placeholder Prices as available for real purchases.

7. Run configuration checks

Before the initial deployment, run:

npm run doctor
npm run verify

If the remote project has already been initialized, also run:

npm run doctor:remote

The verify command includes linting, type checking, tests, and a production build. The doctor:remote command checks configuration and identity. It does not send emails to users or complete payments for you.

Do not paste complete environment files into support tickets, chat histories, or version control. For troubleshooting, record only variable names, their environments, and whether they are configured.

Next: Manage production databases and content.