All skills
davila7 avatar

/cloudflare-deploy

@72b6736

Deploy applications and infrastructure to Cloudflare using Workers, Pages, and related platform services. Use when the user asks to deploy, host, publish, or set up a project on Cloudflare.

Use this Skill: https://skilld.dev/gh/davila7/claude-code-templates/cloudflare-deploy

This session only. Nothing lands on disk.

referencespulumipatterns.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Architecture Patterns

Component Resources

class WorkerApp extends pulumi.ComponentResource {
    constructor(name: string, args: WorkerAppArgs, opts?) {
        super("custom:cloudflare:WorkerApp", name, {}, opts);
        const defaultOpts = {parent: this};

        this.kv = new cloudflare.WorkersKvNamespace(`${name}-kv`, {accountId: args.accountId, title: `${name}-kv`}, defaultOpts);
        this.worker = new cloudflare.WorkerScript(`${name}-worker`, {
            accountId: args.accountId, name: `${name}-worker`, content: args.workerCode,
            module: true, kvNamespaceBindings: [{name: "KV", namespaceId: this.kv.id}],
        }, defaultOpts);
        this.domain = new cloudflare.WorkersDomain(`${name}-domain`, {
            accountId: args.accountId, hostname: args.domain, service: this.worker.name,
        }, defaultOpts);
    }
}

Full-Stack Worker App

const kv = new cloudflare.WorkersKvNamespace("cache", {accountId, title: "api-cache"});
const db = new cloudflare.D1Database("db", {accountId, name: "app-database"});
const bucket = new cloudflare.R2Bucket("assets", {accountId, name: "app-assets"});

const apiWorker = new cloudflare.WorkerScript("api", {
    accountId, name: "api-worker", content: fs.readFileSync("./dist/api.js", "utf8"),
    module: true, kvNamespaceBindings: [{name: "CACHE", namespaceId: kv.id}],
    d1DatabaseBindings: [{name: "DB", databaseId: db.id}],
    r2BucketBindings: [{name: "ASSETS", bucketName: bucket.name}],
});

Multi-Environment Setup

const stack = pulumi.getStack();
const worker = new cloudflare.WorkerScript(`worker-${stack}`, {
    accountId, name: `my-worker-${stack}`, content: code,
    plainTextBindings: [{name: "ENVIRONMENT", text: stack}],
});

Queue-Based Processing

const queue = new cloudflare.Queue("processing-queue", {accountId, name: "image-processing"});

// Producer: API receives requests
const apiWorker = new cloudflare.WorkerScript("api", {
    accountId, name: "api-worker", content: apiCode,
    queueBindings: [{name: "PROCESSING_QUEUE", queue: queue.id}],
});

// Consumer: Process async
const processorWorker = new cloudflare.WorkerScript("processor", {
    accountId, name: "processor-worker", content: processorCode,
    queueConsumers: [{queue: queue.name, maxBatchSize: 10, maxRetries: 3, maxWaitTimeMs: 5000}],
    r2BucketBindings: [{name: "OUTPUT_BUCKET", bucketName: outputBucket.name}],
});

Microservices with Service Bindings

const authWorker = new cloudflare.WorkerScript("auth", {accountId, name: "auth-service", content: authCode});
const apiWorker = new cloudflare.WorkerScript("api", {
    accountId, name: "api-service", content: apiCode,
    serviceBindings: [{name: "AUTH", service: authWorker.name}],
});

Event-Driven Architecture

const eventQueue = new cloudflare.Queue("events", {accountId, name: "event-bus"});
const producer = new cloudflare.WorkerScript("producer", {
    accountId, name: "api-producer", content: producerCode,
    queueBindings: [{name: "EVENTS", queue: eventQueue.id}],
});
const consumer = new cloudflare.WorkerScript("consumer", {
    accountId, name: "email-consumer", content: consumerCode,
    queueConsumers: [{queue: eventQueue.name, maxBatchSize: 10}],
});

v6.x Versioned Deployments (Blue-Green/Canary)

const worker = new cloudflare.Worker("api", {accountId, name: "api-worker"});
const v1 = new cloudflare.WorkerVersion("v1", {accountId, workerId: worker.id, content: fs.readFileSync("./dist/v1.js", "utf8"), compatibilityDate: "2025-01-01"});
const v2 = new cloudflare.WorkerVersion("v2", {accountId, workerId: worker.id, content: fs.readFileSync("./dist/v2.js", "utf8"), compatibilityDate: "2025-01-01"});

// Gradual rollout: 10% v2, 90% v1
const deployment = new cloudflare.WorkersDeployment("canary", {
    accountId, workerId: worker.id,
    versions: [{versionId: v2.id, percentage: 10}, {versionId: v1.id, percentage: 90}],
    kvNamespaceBindings: [{name: "MY_KV", namespaceId: kv.id}],
});

Use: Canary releases, A/B testing, blue-green. Most apps use WorkerScript (auto-versioning).

Wrangler.toml Generation (Bridge IaC with Local Dev)

Generate wrangler.toml from Pulumi config to keep local dev in sync:

import * as command from "@pulumi/command";

const workerConfig = {
    name: "my-worker",
    compatibilityDate: "2025-01-01",
    compatibilityFlags: ["nodejs_compat"],
};

// Create resources
const kv = new cloudflare.WorkersKvNamespace("kv", {accountId, title: "my-kv"});
const db = new cloudflare.D1Database("db", {accountId, name: "my-db"});
const bucket = new cloudflare.R2Bucket("bucket", {accountId, name: "my-bucket"});

// Generate wrangler.toml after resources created
const wranglerGen = new command.local.Command("gen-wrangler", {
    create: pulumi.interpolate`cat > wrangler.toml <<EOF
name = "${workerConfig.name}"
main = "src/index.ts"
compatibility_date = "${workerConfig.compatibilityDate}"
compatibility_flags = ${JSON.stringify(workerConfig.compatibilityFlags)}

[[kv_namespaces]]
binding = "MY_KV"
id = "${kv.id}"

[[d1_databases]]
binding = "DB"
database_id = "${db.id}"
database_name = "${db.name}"

[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "${bucket.name}"
EOF`,
}, {dependsOn: [kv, db, bucket]});

// Deploy worker after wrangler.toml generated
const worker = new cloudflare.WorkerScript("worker", {
    accountId, name: workerConfig.name, content: code,
    compatibilityDate: workerConfig.compatibilityDate,
    compatibilityFlags: workerConfig.compatibilityFlags,
    kvNamespaceBindings: [{name: "MY_KV", namespaceId: kv.id}],
    d1DatabaseBindings: [{name: "DB", databaseId: db.id}],
    r2BucketBindings: [{name: "MY_BUCKET", bucketName: bucket.name}],
}, {dependsOn: [wranglerGen]});

Benefits:

  • wrangler dev uses same bindings as production
  • No config drift between Pulumi and local dev
  • Single source of truth (Pulumi config)

Alternative: Read wrangler.toml in Pulumi (reverse direction) if wrangler is source of truth

Build + Deploy Pattern

import * as command from "@pulumi/command";
const build = new command.local.Command("build", {create: "npm run build", dir: "./worker"});
const worker = new cloudflare.WorkerScript("worker", {
    accountId, name: "my-worker",
    content: build.stdout.apply(() => fs.readFileSync("./worker/dist/index.js", "utf8")),
}, {dependsOn: [build]});

Content SHA Pattern (Force Updates)

Prevent false "no changes" detections:

const version = Date.now().toString();
const worker = new cloudflare.WorkerScript("worker", {
    accountId, name: "my-worker", content: code,
    plainTextBindings: [{name: "VERSION", text: version}], // Forces deployment
});

See: README.md, configuration.md, api.md, gotchas.md

Source: SKILL.md on GitHub

2 warnings1d4 checks · Risk SAFE
  • Gen Agent Trust Hub1d

    The skill is a comprehensive documentation and reference suite for deploying applications and infrastructure to the Cloudflare platform. It provides detailed technical guidance, code snippets, and architectural patterns for various services including Workers, Pages, D1, R2, and AI Gateway. No malicious patterns, obfuscation, or unauthorized data access were detected.

  • Socket1d

    1 alert: gptAnomaly

  • Snyk1d

    Risk: LOW · No issues

  • Runlayer7mo

    304/310 files flagged

Signed by skilld at 72b6736. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 19 hours ago.

Activeupdated 8 months ago
author
openai

README badge

README badge for davila7/claude-code-templates/cloudflare-deploy