skills/cloudflare-ops/SKILL.md
Cloudflare Workers + Wrangler edge-platform operations - Workers runtime, bindings, local dev, secrets, deploy/CI, Pages-vs-Workers, observability. Use for: cloudflare, workers, wrangler, cloudflare pages, KV, D1, R2, durable objects, queues, hyperdrive, workers ai, vectorize, wrangler.toml, wrangler.jsonc, wrangler deploy, wrangler dev, wrangler secret, bindings, compatibility_date, static assets, edge functions, cron triggers, tail workers, gradual deployments.
npx skillsauth add 0xDarkMatter/claude-mods cloudflare-opsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Cloudflare Workers + Wrangler: runtime patterns, bindings, local dev, secrets, deploy, CI/CD, observability.
Version context (verified 2026-06): Wrangler v4.x · config is wrangler.jsonc (Cloudflare's recommended format for new projects — some newer features are JSON-config-only; wrangler.toml still works and is widespread in older repos) · deploy command is wrangler deploy (the old wrangler publish is deprecated — see gotchas). Workers can now serve static assets, which is the current direction for full-stack and static sites over Pages (see Workers vs Pages).
| File | Covers |
|------|--------|
| references/bindings.md | Every binding (KV/D1/R2/DO/Queues/Hyperdrive/AI/Vectorize/Service/Analytics Engine) — config block, runtime API, when to reach for each, consistency model |
| references/workers-runtime.md | Runtime APIs, handlers (fetch/scheduled/queue/email/tail), CORS, caching, streaming, WebSockets, Durable Objects deep-dive, limits |
| references/deploy-and-cicd.md | wrangler deploy, environments, secrets, Workers Builds, GitHub Actions + OIDC/API-token, gradual deployments, rollbacks, observability |
| assets/wrangler.jsonc.template | Commented, current wrangler.jsonc covering all common bindings + assets |
Cloudflare added static-asset hosting to Workers; a single Worker now serves a static site, a full-stack app, or an API + SPA. For new projects, default to Workers with static assets. Pages still works and isn't deprecated, but Workers has the broader, faster-moving feature set (Durable Objects, Cron Triggers, Queues, richer observability) and is where Cloudflare's investment goes.
New project?
│
├─ Pure static site (no server logic)
│ └─ Workers + assets binding (asset-only — requests matching files never invoke Worker code, $0 for those).
│ Pages is also fine here; Workers keeps one platform if you later add logic.
│
├─ Full-stack / SPA + API / SSR framework (Next, Astro, Remix, SvelteKit, Hono)
│ └─ Workers + assets + a Worker script. Use the framework's Cloudflare adapter (C3: `npm create cloudflare@latest`).
│ This is the current recommended path — Pages' framework story is converging into Workers.
│
├─ Already on Pages and happy
│ └─ Stay. "What works in Pages works in Workers" — migrate only when you need a Workers-only
│ feature (DO, Cron, Queues, advanced observability). See the migrate-from-pages guide.
│
└─ Need Durable Objects / Cron Triggers / Queues / Tail Workers
└─ Workers (these are Workers-only).
Asset serving modes (in the assets block): asset-only (no main) serves files directly and never bills Worker invocations for matches; assets + Worker serves matching files first, falls through to your fetch handler for everything else (or set run_worker_first to invoke the Worker before asset matching). Reach assets from code via env.ASSETS.fetch(request).
Full annotated version: assets/wrangler.jsonc.template.
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2026-06-01", // pins the runtime version — REQUIRED, bump deliberately
"compatibility_flags": ["nodejs_compat"], // opt-in runtime features (Node built-ins, etc.)
"observability": { "enabled": true }, // turn on Workers Logs (off by default)
"assets": { "directory": "./public", "binding": "ASSETS" },
"kv_namespaces": [{ "binding": "CACHE", "id": "<kv-id>" }],
"d1_databases": [{ "binding": "DB", "database_name": "app", "database_id": "<d1-id>" }],
"r2_buckets": [{ "binding": "BUCKET", "bucket_name": "uploads" }],
"vars": { "ENVIRONMENT": "production" }, // NON-secret config only — never put secrets here
"env": {
"staging": { "vars": { "ENVIRONMENT": "staging" } } // named env: deploy with --env staging
}
}
compatibility_date = yyyy-mm-dd, selects the runtime version. It's required and load-bearing: bumping it can change behaviour, so do it deliberately and test. compatibility_flags opt into upcoming/Node-compat features (e.g. nodejs_compat).vars — they land in plaintext in the deployed config. Use wrangler secret put / .dev.vars (secrets).[[kv_namespaces]], [[d1_databases]], …). New repos: prefer jsonc.Full config + runtime API for every binding: references/bindings.md.
| Binding | Reach for it when… | Consistency / note |
|---------|--------------------|--------------------|
| KV | Read-heavy config/cache, infrequent writes, global reads | Eventually consistent (~60s propagation). Fast reads, slow-ish writes. Not for "read your own write". |
| D1 | Relational/SQL data, moderate scale, per-app database | SQLite at the edge. Strong within a DB; read replication is async. Use for app data with joins. |
| R2 | Object/blob storage, large files, zero egress fees | S3-compatible. Replaces S3 for media/backups/assets you serve. |
| Durable Objects | Strong consistency, coordination, stateful realtime (chat, presence, game rooms, rate limit counters) | Single-threaded per object instance = serialized = consistent. The answer when KV's eventual consistency bites. SQLite-backed storage available. |
| Queues | Async/background work, decoupling, batching, retries | Producer binding + consumer Worker. Smooths spikes; guaranteed delivery with retries + DLQ. |
| Hyperdrive | Connecting to an existing external Postgres/MySQL with pooling + edge caching | Makes a regional DB feel fast from Workers. Needs nodejs_compat. |
| Workers AI | Run inference (LLM, embeddings, image) on Cloudflare's GPUs | ai binding → env.AI.run(model, ...). Pairs with Vectorize for RAG. |
| Vectorize | Vector DB for embeddings / semantic search / RAG | vectorize binding. Store + query embeddings, often fed by Workers AI. |
| Service bindings | Worker-to-Worker RPC without a network hop | Zero-latency internal calls; compose Workers as services. |
Decision shortcut: need strong consistency or coordination → Durable Objects. Relational queries → D1. Big files → R2. Cheap global cache → KV. Background work → Queues. External SQL DB → Hyperdrive.
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (url.pathname === "/health") return Response.json({ ok: true });
return new Response("Hello from the edge");
},
};
env carries every binding (env.DB, env.CACHE, env.ASSETS, secrets, vars). ctx.waitUntil(promise) runs background work after the response is sent. Workers require ES module format (export default { fetch }) — the old service-worker addEventListener("fetch") format is legacy. Full handler patterns (scheduled/queue/email/tail, CORS, caching, WebSockets, DO): references/workers-runtime.md.
npm create cloudflare@latest my-app # C3 scaffolder — picks framework + adapter + wrangler.jsonc
wrangler dev # local dev server (Miniflare/workerd) on localhost:8787
wrangler dev --remote # run on Cloudflare's edge (real bindings) instead of local sim
wrangler types # generate TS types for env from your bindings → worker-configuration.d.ts
Secrets (never in vars):
| Where | Mechanism |
|-------|-----------|
| Local dev | .dev.vars file (dotenv format, gitignored) — wrangler dev loads it as env.*. Per-env: .dev.vars.staging. |
| Deployed | wrangler secret put NAME (prompts for value, encrypts it) · wrangler secret list · wrangler secret delete NAME |
| CI bulk | wrangler secret bulk secrets.json |
| Newer | Cloudflare Secrets Store bindings (account-level shared secrets) — see deploy reference |
Add .dev.vars* to .gitignore. vars in config = plaintext public config; secrets are encrypted and write-only.
Full detail: references/deploy-and-cicd.md.
wrangler deploy # build + upload + activate (NOT `wrangler publish` — deprecated)
wrangler deploy --env staging # deploy a named environment
wrangler versions upload # upload a new version WITHOUT making it live (gradual deploys)
wrangler versions deploy # split traffic across versions (e.g. 10% new / 90% old)
wrangler rollback # revert to the previous deployed version
wrangler tail # stream live logs from the deployed Worker
cloudflare/wrangler-action. Authenticate with a scoped API token (CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID as secrets), least-privilege (Workers Scripts:Edit). Template + workflow in the deploy reference.versions upload then versions deploy to shift a percentage of traffic; instant rollback if metrics regress."observability": { "enabled": true } in config turns on Workers Logs (structured console.log capture in the dashboard) — off by default, opt in.wrangler tail for live request log streaming during an incident.env.AE.writeDataPoint(...)), query via GraphQL/SQL API.| Gotcha | Detail | Fix |
|--------|--------|-----|
| wrangler publish is gone | Renamed to wrangler deploy (Wrangler v3+). Old tutorials/CI still say publish. | Use wrangler deploy. Update any publish in scripts/CI. |
| wrangler.toml vs .jsonc | Both parse, but newer features are JSON-config-only and Cloudflare recommends jsonc for new projects. | New projects: wrangler.jsonc. Migrating: wrangler.toml → jsonc is a mechanical 1:1. |
| Missing compatibility_date | Required; absent or stale date silently pins old runtime behaviour. | Set it; bump deliberately and test — it can change semantics. |
| CPU time limit | Default 30s CPU per invocation (was 10ms/50ms historically; raised). Wall-clock can be longer while awaiting I/O. CPU-bound loops still get killed. | Offload heavy compute; use Queues for long async work; check the limits page for your plan. |
| Script size limit | 3 MB (free) / 10 MB (paid) gzipped. | Trim deps, dynamic-import large modules, avoid bundling node-only libs. |
| KV eventual consistency | A write isn't globally visible for up to ~60s; not "read your own write". | Use Durable Objects when you need strong consistency. |
| Node built-ins fail | fs, crypto, etc. aren't there by default. | "compatibility_flags": ["nodejs_compat"] enables a polyfill subset; check what's actually supported. |
| Secrets in vars | vars ships plaintext in the deployed config. | wrangler secret put (deployed) / .dev.vars (local). |
| request/response body read twice | Streams are single-use. | request.clone() before the first read. |
| Bundling surprises | Wrangler uses esbuild; some packages assume Node/CommonJS. | Prefer Workers-compatible libs; set nodejs_compat; check the build output. |
npm install -g wrangler (or use npx wrangler / npm create cloudflare@latest to scaffold).wrangler login (OAuth) for local; API token for CI.wrangler dev → wrangler deploy.tools
yt-dlp operations - the media ACQUISITION layer that feeds ffmpeg-ops: format selection (-S sort vs -f filters) that avoids post-download transcodes, --download-sections clip-at-download, audio-only extraction for STT pipelines (-x --audio-format opus), playlists + --download-archive incremental channel syncs, cookies/auth (--cookies-from-browser), rate limiting and politeness, SponsorBlock mark/remove, output templates (-o), subtitle download (--write-subs/--write-auto-subs), remux-vs-recode doctrine, and failure triage (403s, throttling, geo blocks, the nsig-extraction class that means yt-dlp is outdated). Triggers on: yt-dlp, ytdlp, youtube-dl, download video, download youtube, download from youtube, download playlist, download channel, archive channel, channel sync, rip audio, youtube to mp3, youtube to mp4, save video, grab video, video downloader, download subtitles, download transcript, clip from youtube, download section, sponsorblock, cookies-from-browser, download-archive, nsig, requested format is not available, sign in to confirm, download livestream, record stream, live-from-start, premiere, impersonate.
tools
Comprehensive ffmpeg/ffprobe operations - probe-first media processing: transcode and compress (H.264/H.265/AV1/Opus), frame-accurate cut/trim/concat, EDL-driven editing, color grading and .cube LUTs, audio loudnorm and mixing, STT/Whisper audio prep, subtitles, GIF and thumbnails, HLS packaging, hardware encoding (NVENC/QSV/AMF/VideoToolbox), restoration, scene and silence detection, VMAF quality gates, screen capture, yt-dlp interop. Triggers on: ffmpeg, ffprobe, transcode, convert video, compress video, encode video, extract audio, trim video, cut video, concat videos, video to gif, thumbnail, contact sheet, burn subtitles, watermark, resize video, crop video, change fps, slow motion, timelapse, loudnorm, normalize audio, audio for whisper, transcription prep, scene detection, silence detection, remove silence, color grade, LUT, tonemap HDR, vmaf, nvenc, hardware encode, hls, remux, faststart, deinterlace, stabilize video, denoise video, screen record, EDL, keyframes.
development
Payload CMS 3 (Next.js-native) architecture - collections, globals, fields, access control, hooks, Local API, storage adapters, and database (Postgres/MongoDB/SQLite). Use for: payload, payloadcms, payload cms, payload 3, collection config, access control, payload hooks, local api, payload fields, multi-tenant payload, payload nextjs, payload s3, payload r2, payloadcms architecture, headless cms typescript.
testing
Cypress end-to-end and component testing operations - selector/retry-ability strategy, cy.intercept network stubbing, cy.session auth, component vs e2e, flake diagnosis, CI, Test Replay. Use for: cypress, e2e test, component test, cy.get, cy.intercept, cy.session, data-cy, data-test, retry-ability, flake, flaky test, cypress.config, cy.mount, Test Replay, custom commands, fixtures.