Skip to content

Cloudflare Infrastructure

Multi-app deployment infrastructure for Cloudflare Workers.

Supported Worker Types

TypeDescriptionBest For
Regular WorkerStandard edge worker with fetch handlerAPIs, proxies, static sites
Cron WorkerScheduled worker with cron triggersBackground jobs, data sync, cleanup
Container WorkerWorker backed by Docker containerSSR 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.md

Quick Start

1. Setup Credentials

Terminal window
make cf-setup

This 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

Terminal window
# Interactive mode - choose worker type
make cf-init a=myapp
# Or specify type directly
make cf-init a=myapp type=worker # Regular worker
make cf-init a=myapp type=cron # Cron worker
make cf-init a=myapp type=container # Container worker

The 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

Terminal window
# Deploy single app
make cf-deploy a=myapp
# Deploy all apps
make cf-deploy-all

Worker Type Details

Regular Workers

Standard edge workers that handle HTTP requests with minimal latency.

Terminal window
make cf-init a=api.example.com type=worker

Features:

  • 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.

Terminal window
make cf-init a=sync-job type=cron

Features:

  • 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.

Terminal window
make cf-init a=app.example.com type=container

Features:

  • 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

CommandDescription
make cf-setupInteractive setup wizard
make cf-varsShow the resolved credentials and which scope supplied them
make cf-check-credentialsFail early, naming both config paths, when no account id resolves
make cf-loginAuthenticate with Cloudflare
make cf-whoamiShow current authentication
make cf-statusShow all apps status
make cf-listList all configured apps

App Management

CommandDescription
make cf-init a=<app>Initialize app (interactive)
make cf-init a=<app> type=workerInitialize regular worker
make cf-init a=<app> type=cronInitialize cron worker
make cf-init a=<app> type=containerInitialize container worker
make cf-install a=<app>Install worker dependencies
make cf-dev a=<app>Run worker locally

Build & Deploy

CommandDescription
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-allDeploy all apps

Monitoring

CommandDescription
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:

EndpointMethodDescription
/_healthGETHealth check
/_healthzGETKubernetes-style health
/_readyzGETReadiness check
/_container/statusGETContainer state & metadata
/_container/startPOSTStart container
/_container/stopPOSTGraceful stop
/_container/restartPOSTRestart container

Example

Terminal window
# Check health
curl https://pricelove.co/_health
# Get container status
curl https://pricelove.co/_container/status
# Restart container
curl -X POST https://pricelove.co/_container/restart

Configuration

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.

ScopeFileWritten by
Projectinfra/cloudflare/.env.cloudflaremake cf-setup
Global~/.mech-crate/config/infra/cloudflare.envmx infra setup cloudflare

The .env.cloudflare file (created by cf-setup, gitignored):

Terminal window
# Required
CLOUDFLARE_ACCOUNT_ID=your_account_id
# Optional (for CI/CD)
CLOUDFLARE_API_TOKEN=your_api_token
# Build options
CF_DOCKER_PLATFORM=linux/amd64

A 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:

Terminal window
make cf-vars # resolved account id, which scope supplied it, both file paths
make cf-check-credentials # fails loudly, naming both paths and both fixes, when nothing resolves

Per-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 = 10
min_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 Cloudflare
on:
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-all

CI 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:

SecretDescription
CLOUDFLARE_API_TOKENAPI token with Workers & Registry permissions
CLOUDFLARE_ACCOUNT_IDYour Cloudflare account ID

Adding Multiple Apps

Terminal window
# Initialize apps
make cf-init a=pricelove.co
make cf-init a=theblock.co
make cf-init a=myapp.co
# Deploy all
make cf-deploy-all
# Or deploy individually
make cf-deploy a=pricelove.co
make cf-deploy a=theblock.co

Versioning

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:

Terminal window
cd infra/cloudflare/apps/pricelove.co
npm version patch # or minor, major
cd -
make cf-deploy a=pricelove.co

Troubleshooting

Container won’t start

Terminal window
# Check status
curl https://pricelove.co/_container/status
# View logs
make cf-logs a=pricelove.co
# Force restart
curl -X POST https://pricelove.co/_container/restart

Image not found

Terminal window
# List available images
make cf-images a=pricelove.co
# Rebuild and push
make cf-publish a=pricelove.co
# Sync wrangler.toml with new tag
make cf-sync-image a=pricelove.co

Authentication issues

Terminal window
# Re-login
make cf-login
# Verify identity
make cf-whoami
# Re-run setup
make cf-setup

🦝 Built with MechCrate