Cloudflare Infrastructure
Multi-app deployment infrastructure for Cloudflare Workers.
Supported Worker Types
| Type | Description | Best For |
|---|---|---|
| Regular Worker | Standard edge worker with fetch handler | APIs, proxies, static sites |
| Cron Worker | Scheduled worker with cron triggers | Background jobs, data sync, cleanup |
| Container Worker | Worker backed by Docker container | SSR apps, complex backends, stateful services |
Directory Structure
infra/cloudflare/├── .env.cloudflare # Credentials (gitignored)├── apps/│ ├── api.example.com/ # Regular Worker│ │ ├── src/index.ts│ │ ├── wrangler.toml│ │ ├── package.json│ │ └── tsconfig.json│ ├── sync-job/ # Cron Worker│ │ └── ...│ └── app.example.com/ # Container Worker│ └── ...└── README.mdQuick Start
1. Setup Credentials
make cf-setupThis interactive wizard will:
- Authenticate with Cloudflare (if not already)
- Save your Account ID
- Optionally configure an API token for CI/CD
2. Initialize an App
# Interactive mode - choose worker typemake cf-init a=myapp
# Or specify type directlymake cf-init a=myapp type=worker # Regular workermake cf-init a=myapp type=cron # Cron workermake cf-init a=myapp type=container # Container workerThe interactive wizard will walk you through configuring:
Regular Worker:
- Custom domain routing
- KV namespace bindings
- Environment variables
Cron Worker:
- Cron schedule(s)
- KV namespace for state persistence
- Webhook notifications
Container Worker:
- Container port
- Sleep timeout (auto-shutdown)
- Max instances (dev/prod)
- Custom domain
- Dockerfile template
3. Deploy
# Deploy single appmake cf-deploy a=myapp
# Deploy all appsmake cf-deploy-allWorker Type Details
Regular Workers
Standard edge workers that handle HTTP requests with minimal latency.
make cf-init a=api.example.com type=workerFeatures:
- Fetch handler for HTTP requests
- KV, R2, D1 bindings support
- Environment variables
- Custom domain routing
Example endpoints generated:
/_health- Health check/api/*- Your API routes
Cron Workers
Scheduled workers that run on a cron schedule.
make cf-init a=sync-job type=cronFeatures:
- Configurable cron schedules (e.g.,
*/5 * * * *) - Multiple schedules per worker
- KV namespace for state persistence
- Optional webhook notifications
- Manual trigger endpoint for testing
Example endpoints generated:
/_health- Health check with last run status/trigger- POST to manually trigger the job
Container Workers
Workers backed by Docker containers using Durable Objects.
make cf-init a=app.example.com type=containerFeatures:
- Full Docker container support
- Auto-scaling with configurable limits
- Sleep timeout (container shuts down after inactivity)
- Container lifecycle management endpoints
- Dockerfile template generation
Example endpoints generated:
/_health,/_healthz,/_readyz- Health checks/_container/status- Container state/_container/start- Start container/_container/stop- Stop container/_container/restart- Restart container
Commands Reference
Setup & Status
| Command | Description |
|---|---|
make cf-setup | Interactive setup wizard |
make cf-vars | Show the resolved credentials and which scope supplied them |
make cf-check-credentials | Fail early, naming both config paths, when no account id resolves |
make cf-login | Authenticate with Cloudflare |
make cf-whoami | Show current authentication |
make cf-status | Show all apps status |
make cf-list | List all configured apps |
App Management
| Command | Description |
|---|---|
make cf-init a=<app> | Initialize app (interactive) |
make cf-init a=<app> type=worker | Initialize regular worker |
make cf-init a=<app> type=cron | Initialize cron worker |
make cf-init a=<app> type=container | Initialize container worker |
make cf-install a=<app> | Install worker dependencies |
make cf-dev a=<app> | Run worker locally |
Build & Deploy
| Command | Description |
|---|---|
make cf-build a=<app> | Build container image |
make cf-push a=<app> | Push to Cloudflare registry |
make cf-publish a=<app> | Build and push |
make cf-deploy a=<app> | Full deploy to production |
make cf-deploy-preview a=<app> | Deploy to preview |
make cf-deploy-all | Deploy all apps |
Monitoring
| Command | Description |
|---|---|
make cf-logs a=<app> | Tail production logs |
make cf-logs-preview a=<app> | Tail preview logs |
make cf-images a=<app> | List registry images |
Worker Endpoints
Each deployed app exposes these endpoints:
| Endpoint | Method | Description |
|---|---|---|
/_health | GET | Health check |
/_healthz | GET | Kubernetes-style health |
/_readyz | GET | Readiness check |
/_container/status | GET | Container state & metadata |
/_container/start | POST | Start container |
/_container/stop | POST | Graceful stop |
/_container/restart | POST | Restart container |
Example
# Check healthcurl https://pricelove.co/_health
# Get container statuscurl https://pricelove.co/_container/status
# Restart containercurl -X POST https://pricelove.co/_container/restartConfiguration
Environment Variables
Credentials use the two names wrangler itself reads, CLOUDFLARE_ACCOUNT_ID and
CLOUDFLARE_API_TOKEN, so the value in a file reaches the deploy untranslated.
They resolve from two scopes, highest precedence first: the environment (or the
make command line), then the project file, then the global file.
| Scope | File | Written by |
|---|---|---|
| Project | infra/cloudflare/.env.cloudflare | make cf-setup |
| Global | ~/.mech-crate/config/infra/cloudflare.env | mx infra setup cloudflare |
The .env.cloudflare file (created by cf-setup, gitignored):
# RequiredCLOUDFLARE_ACCOUNT_ID=your_account_id
# Optional (for CI/CD)CLOUDFLARE_API_TOKEN=your_api_token
# Build optionsCF_DOCKER_PLATFORM=linux/amd64A global config alone is a complete setup: make cf-init and the deploy targets
resolve from it, and a project file is only needed when one project deploys to a
different account than the rest of the workstation.
CF_ACCOUNT_ID and CF_API_TOKEN are deprecated aliases. They are still
read, at their own scope’s precedence, so a file written by an older cf-setup
keeps working; nothing writes them any more, and the toolchain names the file
that still uses them.
Two targets make the resolution visible instead of guessable:
make cf-vars # resolved account id, which scope supplied it, both file pathsmake cf-check-credentials # fails loudly, naming both paths and both fixes, when nothing resolvesPer-App Configuration
Each app has its own wrangler.toml with:
- Preview environment: Deployed to
*.workers.dev - Production environment: Deployed to your domain
Edit infra/cloudflare/apps/<app>/wrangler.toml to customize:
# Adjust container limits[[env.production.containers]]max_instances = 10min_instances = 1
# Add custom routes[env.production]routes = [ { pattern = "pricelove.co/*", zone_name = "pricelove.co" }, { pattern = "www.pricelove.co/*", zone_name = "pricelove.co" }]CI/CD Integration
GitHub Actions Example
name: Deploy to Cloudflareon: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup Node uses: actions/setup-node@v4 with: node-version: '20'
- name: Install dependencies run: npm ci
- name: Deploy env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} run: make cf-deploy-allCI needs no credentials file: the environment is the highest-precedence scope, so those two variables are enough on their own.
Required Secrets
Create in your repo settings:
| Secret | Description |
|---|---|
CLOUDFLARE_API_TOKEN | API token with Workers & Registry permissions |
CLOUDFLARE_ACCOUNT_ID | Your Cloudflare account ID |
Adding Multiple Apps
# Initialize appsmake cf-init a=pricelove.comake cf-init a=theblock.comake cf-init a=myapp.co
# Deploy allmake cf-deploy-all
# Or deploy individuallymake cf-deploy a=pricelove.comake cf-deploy a=theblock.coVersioning
Container images are tagged from each app’s package.json:
{ "name": "pricelove.co-worker", "version": "1.2.3"}Results in: registry.cloudflare.com/<account>/pricelove.co:v1.2.3
To update version and deploy:
cd infra/cloudflare/apps/pricelove.conpm version patch # or minor, majorcd -make cf-deploy a=pricelove.coTroubleshooting
Container won’t start
# Check statuscurl https://pricelove.co/_container/status
# View logsmake cf-logs a=pricelove.co
# Force restartcurl -X POST https://pricelove.co/_container/restartImage not found
# List available imagesmake cf-images a=pricelove.co
# Rebuild and pushmake cf-publish a=pricelove.co
# Sync wrangler.toml with new tagmake cf-sync-image a=pricelove.coAuthentication issues
# Re-loginmake cf-login
# Verify identitymake cf-whoami
# Re-run setupmake cf-setup🦝 Built with MechCrate