Deployment (Cloudflare)
CruzJS deploys to Cloudflare Pages with automatic provisioning of D1 databases, KV namespaces, R2 buckets, and other bindings. The cruz CLI handles the full deployment lifecycle: infrastructure creation, builds, migrations, and shipping.
Quick Deploy
Section titled “Quick Deploy”If you want to get to production fast:
# 1. Initialize the production environment (creates D1, KV, etc.)cruz init production
# 2. Deploy (build + migrate + ship)cruz deploy productionThat is the complete workflow. The rest of this page explains each step in detail.
Environment Types
Section titled “Environment Types”CruzJS supports three environment patterns:
| Environment | Purpose | URL Pattern |
|---|---|---|
| production | Live user-facing app | myapp.pages.dev or custom domain |
| staging | Pre-production testing | staging.myapp.pages.dev |
| preview | Ephemeral per-branch deploys | <branch>.myapp.pages.dev |
Each environment gets its own isolated Cloudflare resources (D1 database, KV namespace, etc.), so staging data never touches production.
Initializing an Environment
Section titled “Initializing an Environment”Before your first deploy to a new environment, run cruz init to provision infrastructure:
cruz init productionThis command:
- Reads your
cruz.config.tsto determine which bindings are needed - Creates a Cloudflare Pages project (if it does not already exist)
- Provisions a D1 database named
<app-name>-production-db - Creates a KV namespace named
<app-name>-production-kv(ifkv: true) - Creates an R2 bucket named
<app-name>-production-bucket(ifr2: true) - Generates the
wrangler.tomlwith all binding IDs - Stores the environment configuration locally
You can initialize multiple environments:
cruz init productioncruz init stagingWhat Happens During Deploy
Section titled “What Happens During Deploy”When you run cruz deploy <env>, the CLI executes these steps in order:
cruz deploy production│├── 1. Build│ └── Runs `cruz build` (Vite production build for Cloudflare Pages)│├── 2. Migrate│ └── Runs `cruz db migrate --remote` against the environment's D1 database│├── 3. Ship│ └── Deploys the built output to Cloudflare Pages via Wrangler│└── 4. Verify └── Confirms the deployment is liveThe build produces a Cloudflare Pages-compatible output with server-side rendering via React Router v7 and the Cloudflare Pages Functions adapter.
Deploy Commands
Section titled “Deploy Commands”Full Deploy
Section titled “Full Deploy”# Deploy to production (build + migrate + ship)cruz deploy production
# Deploy to stagingcruz deploy stagingPreview Deploys
Section titled “Preview Deploys”Deploy a preview from your current branch:
cruz deploy previewPreview deploys create a unique URL tied to your branch name (e.g., feature-xyz.myapp.pages.dev). They share the staging D1 database by default, or you can configure a dedicated preview database.
Check Status
Section titled “Check Status”View the status of all environments:
cruz statusThis shows each environment, its URL, the last deploy time, and the status of all associated resources.
Database Migrations
Section titled “Database Migrations”Migrations are applied automatically during cruz deploy, but you can also run them independently:
# Apply migrations to the remote production D1 databasecruz db migrate --remote
# Run a SQL query against the remote databasecruz db query "SELECT count(*) FROM Notes" --remoteMigration Workflow
Section titled “Migration Workflow”- Modify your Drizzle schema in
*.schema.tsfiles - Generate a migration:
cruz db generate - Review the generated SQL in
src/database/migrations/ - Test locally:
cruz db migrate - Deploy (migrations run automatically):
cruz deploy production
If you need to apply migrations without a full deploy:
cruz db migrate --remote --env productionManaging Secrets
Section titled “Managing Secrets”Sensitive values (API keys, auth secrets) are stored as Cloudflare secrets, not in cruz.config.ts or .env files:
# Set a secretcruz secrets set AUTH_SECRET --env production# (prompts for the value interactively)
# List all secrets for an environmentcruz secrets list --env productionRequired Secrets for Production
Section titled “Required Secrets for Production”| Secret | Description |
|---|---|
AUTH_SECRET | Session encryption key (generate with openssl rand -base64 32) |
GOOGLE_CLIENT_ID | Google OAuth client ID (if using Google login) |
GOOGLE_CLIENT_SECRET | Google OAuth client secret |
STRIPE_SECRET_KEY | Stripe API key (if using @cruzjs/pro billing) |
STRIPE_WEBHOOK_SECRET | Stripe webhook signing secret |
Set all required secrets before your first production deploy.
Multiple Environments
Section titled “Multiple Environments”A typical setup uses two or three environments:
# Initialize both environmentscruz init stagingcruz init production
# Deploy to staging firstcruz deploy staging
# After testing, deploy to productioncruz deploy productionEnvironment-Specific Configuration
Section titled “Environment-Specific Configuration”Define per-environment variables in cruz.config.ts:
import { defineConfig } from '@cruzjs/cli/config';
export default defineConfig({ name: 'myapp', bindings: { d1: true, kv: true },
vars: { APP_NAME: 'My App', },
environments: { production: { vars: { NODE_ENV: 'production', APP_URL: 'https://myapp.com', }, domain: 'myapp.com', }, staging: { vars: { NODE_ENV: 'staging', APP_URL: 'https://staging.myapp.com', }, }, },});Variables in vars (top-level) are shared across all environments. Variables in environments.<name>.vars override the shared values for that specific environment.
Custom Domains
Section titled “Custom Domains”Add a custom domain to an environment in cruz.config.ts:
environments: { production: { domain: 'myapp.com', vars: { APP_URL: 'https://myapp.com', }, },},After deploying, configure DNS to point your domain to Cloudflare Pages. Cloudflare handles SSL certificates automatically.
Deploying External Processes
Section titled “Deploying External Processes”If you have standalone Workers, Workflows, or Queue consumers in external-processes/, they deploy automatically alongside your main app:
cruz deploy production# Deploys:# 1. Main Pages app# 2. All external-processes/* WorkersEach external process has its own wrangler.toml and deploys independently, but cruz deploy orchestrates all of them together.
Scaffold a New External Process
Section titled “Scaffold a New External Process”# Standalone Workercruz new worker email-sender
# Durable Workflow (retryable multi-step process)cruz new workflow onboarding-flow
# Queue consumercruz new queue-worker invoice-processor --queue invoicesDestroying an Environment
Section titled “Destroying an Environment”To tear down an environment and all its associated Cloudflare resources:
cruz destroy stagingThis deletes the D1 database, KV namespace, R2 bucket, and any other resources for that environment. Use with caution — this is irreversible for database data.
CI/CD Integration
Section titled “CI/CD Integration”For automated deployments in CI, use Wrangler directly with your CLOUDFLARE_API_TOKEN:
# In your CI pipelineexport CLOUDFLARE_API_TOKEN=${{ secrets.CF_API_TOKEN }}
# Build and deploycruz buildcruz db migrate --remote --env productionnpx wrangler pages deploy ./build/client --project-name myappOr use the cruz deploy command if you have the CLI available in CI:
cruz deploy productionTroubleshooting
Section titled “Troubleshooting”Deploy Fails with “D1 database not found”
Section titled “Deploy Fails with “D1 database not found””Run cruz init <env> to create the database first, then retry the deploy.
Migrations Fail on Remote D1
Section titled “Migrations Fail on Remote D1”Check the migration SQL for syntax incompatible with D1 (which is SQLite-based). Common issues:
- D1 does not support
ALTER COLUMN— useALTER TABLE ... RENAME COLUMNor create a new table - D1 does not support concurrent schema changes — run migrations sequentially
Preview Deploy Uses Wrong Database
Section titled “Preview Deploy Uses Wrong Database”Preview deploys share the staging database by default. To use an isolated database, initialize a dedicated preview environment:
cruz init previewDeploying to Other Platforms
Section titled “Deploying to Other Platforms”While Cloudflare is the default deployment target, CruzJS supports deploying to other platforms through runtime adapters. See the Runtime Adapters guide for details on deploying to:
- AWS — Lambda (serverless) or Fargate (container)
- Google Cloud — Cloud Run (container) or Cloud Functions (serverless)
- Azure — Azure Functions (serverless) or Container Apps
- DigitalOcean — App Platform
- Docker — Dokploy, Coolify, bare Docker, or Kubernetes
Each adapter maps CruzJS’s abstract bindings (database, cache, queue, storage) to the platform’s native services.
Next Steps
Section titled “Next Steps”- Configuration — fine-tune your cruz.config.ts
- Runtime Adapters — deploy to AWS, GCP, Azure, or Docker
- Directory Structure — understand the full project layout
- Set up CI/CD with GitHub Actions for automated staging and production deploys