Production Deployment
Deploy your app to Production
Last updated on: November 23, 2025
Complete guide for deploying your app to production:
- Frontend on Cloudflare Workers
- Backend on Convex
- Blob Storage on Cloudflare R2 (optional)
Configure Production Services
Copy Production Template
cp .env.convex.prod.example .env.convex.prodAuthentication
1. Generate Production Auth Secret
openssl rand -base64 322. Add to .env.convex.prod (must be different from dev)
BETTER_AUTH_SECRET="<paste-production-secret>"Important: Use a different secret than your development environment. Never reuse dev secrets in production.
Environment Variables: Core variables (VITE_CONVEX_URL, VITE_CONVEX_SITE_URL, SITE_URL) are automatically managed by the deploy script. For custom variables, see the Custom Environment Variables section at the bottom of this guide.
Service-Specific Setup
Authentication
Already configured via BETTER_AUTH_SECRET in .env.convex.prod.
Payments (SaaS Mode Only)
Skip this entire section if using Simple Mode!
Check config.ts in your project root:
- If
payments.trackInConvex: false(Simple Mode) - Skip payments setup entirely - If
payments.trackInConvex: true(SaaS Mode) - Follow all steps below
Simple Mode = Direct Polar checkout links, no database tracking, no webhooks needed
SaaS Mode = Full integration with user accounts, subscription tracking, requires webhooks
1. Create Production Account
2. Create Products
Create the same products as in sandbox, but for production environment.
3. Get Production Credentials
POLAR_ACCESS_TOKEN: Settings → General → Developers → New Token (All scopes, no expiration)POLAR_ORGANIZATION_ID: Settings → Profile → Copy theIdentifierPOLAR_SERVER: Set toproduction
4. Add to Environment
Add to .env.convex.prod:
POLAR_ACCESS_TOKEN=polar_at_prod_...
POLAR_ORGANIZATION_ID=your_prod_org_id
POLAR_SERVER=production
POLAR_WEBHOOK_SECRET= # Get after first deploy5. Configure Webhooks
After your first deployment:
- Get your production Convex site URL from dashboard
- Navigate to Polar Dashboard → Settings → Webhooks
- Click Add endpoint:
- URL:
https://YOUR-PROD.convex.site/payments/webhook - Format: Raw (not JSON)
- Select all events
- URL:
- Copy the Signing Secret
- Add to
.env.convex.prod:POLAR_WEBHOOK_SECRET=polar_whs_... - Re-apply:
bun run setup:convex:prod
Use .convex.site URL for webhooks, not your Workers URL or custom domain!
Prevent Chargebacks (Optional but Recommended)
As you scale, consider using a tool to monitor fraudulent activity and prevent chargebacks. This helps protect your Stripe account from getting banned due to too many chargebacks.
1. Create Production API Key
Use the same Resend account, but create a different API key for production.
2. Add to Environment
Add to .env.convex.prod:
RESEND_API_KEY=re_prod_...
SENDER_EMAIL=hello@yourdomain.com # Must be from verified domain
COMPANY_NAME=YourCompanyNameStorage
1. Create Production Bucket
Create a new bucket: your-bucket-name-prod
2. Set CORS policy for Production
Configure CORS for your production domain:
[
{
"AllowedOrigins": ["https://yourdomain.com"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3600
}
]3. Add Production Credentials
Add to .env.convex.prod:
R2_ACCESS_KEY_ID=...
R2_SECRET_ACCESS_KEY=...
R2_ACCOUNT_ID=...
R2_BUCKET_NAME=your-bucket-name-prodThe R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY are the same as the ones you used previously for development.
Update Site URL
Edit .env.convex.prod:
SITE_URL=https://yourdomain.comDeploy to Production
Apply Production Variables
bun run setup:convex:prodThis uploads all your credentials from .env.convex.prod to Convex for production.
Deploy to Cloudflare
bun run deployThis command:
- ✅ Deploys your TanStack Start Frontend to Cloudflare Workers
- ✅ Deploys your Convex Backend to Convex
- ✅ Creates a .env file in the web directory with the production URLs
Update the SITE_URL in the .env file with your custom domain.
# Production Convex URLs (auto-generated by deploy.sh)
VITE_CONVEX_URL=https://grateful-bird-17.convex.cloud
VITE_CONVEX_SITE_URL=https://grateful-bird-17.convex.site
SITE_URL=https://[your-unique-cloudflare-url].workers.devThen re-deploy your app to Cloudflare.
bun run deployThe logs will now say:
...
✓ built in 3.53s
[updated] web-build Updated Resource
[updating] web Updating Resource...
[skipped] web > url Skipped Resource (no changes)
[updated] web Updated Resource
Web -> https://[your-unique-cloudflare-url].workers.dev
💾 Saving production URLs to .env (preserving custom env vars)...
✅ Deployment complete!
Production URLs saved to apps/web/.env
SITE_URL: https://[your-custom-domain]
🔄 Updating Convex SITE_URL to match .env...
✔ Successfully set SITE_URL
✅ Convex SITE_URL updated to: https://[your-custom-domain]Verify Deployment
After completing deployment:
-
Visit Production URL
- Check your Workers URL or custom domain
- Verify app loads correctly
-
Test Authentication
- Sign up with a new account
- Test sign in flow
- Verify user data in Convex production database
-
Test Payments (if configured)
- Complete a test purchase
- Check payment status in dashboard
- Verify webhook events received
-
Monitor Logs
- Check Convex Dashboard → Logs for any errors
- Monitor Cloudflare Workers dashboard
Custom Environment Variables
When you need to add custom environment variables later, follow these steps:
Important: The deploy script uses values from apps/web/.env, not apps/web/.env.local. If you have the same variable in both files, the .env value will be used during deployment.
Add variable name to apps/web/deploy.sh:
Edit the CUSTOM_ENV_VARS array:
CUSTOM_ENV_VARS=(
"VITE_TEST_VAR" # Add your variable here
)Add binding to apps/web/alchemy.run.ts:
Add to the bindings object:
bindings: {
// ... existing bindings ...
VITE_TEST_VAR: process.env.VITE_TEST_VAR || "",
}Add value to apps/web/.env:
VITE_TEST_VAR=test-value-123Add to Cloudflare Dashboard:
Go to Workers & Pages → Your Worker → Settings → Variables and Secrets → Add variable
Deploy:
bun run deployUseful Commands
# Deploy to production
bun run deploy
# Update production environment variables
bun run setup:convex:prod
# View production logs
cd packages/backend
bunx convex logs --prod --watch
# Open Convex dashboard
bunx convex dashboardNext Steps
With your app deployed, consider adding monitoring and observability: