Cloudflare shipped more changes to Workers between June and August 2026 than in almost any other stretch since the product launched: a new tiered cache sitting in front of every Worker, built-in tracing spans, temporary deploy accounts for CI and AI agents, and general availability for MySQL support in Hyperdrive. If you last touched wrangler.toml a year ago, a lot of what you knew is now out of date. This tutorial walks through deploying a production-ready Worker from scratch on today’s tooling, using Wrangler 4.123.0 (released August 13, 2026) and the current Workers runtime.

By the end you will have a working edge API with a database connection through Hyperdrive, a tiered cache with stale-while-revalidate, custom tracing spans, and a CI/CD pipeline that deploys on every push. Twelve steps, roughly 30 to 40 minutes if you follow along with the code.

What Cloudflare Workers Are and Why They Changed This Summer

Cloudflare Workers run JavaScript, TypeScript, or WebAssembly directly on Cloudflare’s edge network instead of a single origin server. When Cloudflare first announced the product, it described Workers as “a new way for developers to deploy and execute their code directly at the edge of Cloudflare’s global network.” That framing still holds, but the platform underneath it has moved a long way from a simple request-handling function.

Three changes from the past two months matter most for anyone deploying today. First, Workers Cache launched in July 2026 as a regionally tiered cache sitting directly in front of Worker entrypoints, with automatic lower- and upper-tier caching plus native stale-while-revalidate support. Second, Wrangler picked up temporary preview accounts and the wrangler deploy --temporary flag in June 2026, letting CI systems and AI coding agents ship a Worker without a full Cloudflare account. Third, Hyperdrive reached general availability for MySQL on August 14, 2026, so Workers can now talk to a MySQL database at low latency without opening a fresh TCP connection on every request.

Cloudflare’s own documentation frames the deployment model plainly: “A deployment determines which version(s) of your Worker are actively serving traffic.” Every step below builds toward that end state, a versioned Worker running on Cloudflare’s network, reachable through your own domain, backed by a database, and observable through tracing.

The rest of the Workers ecosystem moved alongside these three changes. @cloudflare/workers-types jumped to a v5 major release that drops dated entrypoints, Wrangler now reports npm dependency data to Cloudflare on every deploy for future supply-chain alerts, and Cloudflare Gateway picked up the ability to inspect software package downloads as traffic. None of that is optional tooling bolted on top of Workers anymore. It is the default path a Worker takes from a local src/index.ts file to a request served from one of Cloudflare’s edge locations, and this tutorial follows that path in order.

Prerequisites

Confirm these are in place before starting:

  • Node.js 22.x LTS or newer, needed to run Wrangler and the create-cloudflare scaffolding tool.
  • npm 10.x or newer, bundled with recent Node.js releases.
  • Wrangler 4.123.0 (or the latest 4.x release) — Cloudflare’s CLI tool for developing, testing, and deploying Workers. As Cloudflare’s docs put it, “Wrangler is the command-line tool used to develop, test, and deploy Workers.”
  • A free Cloudflare account (the Workers Free plan is enough to complete this tutorial).
  • A code editor with TypeScript support (VS Code works well with the Workers types package).
  • Basic familiarity with the command line and with fetch-style request handlers.

No credit card is required for the Workers Free plan. You will only need a paid plan if you want to raise the subrequest limit past 50 per invocation or exceed 100,000 requests a day, both covered in the limits table further down.

Step 1: Create a Cloudflare Account and Get Your API Token

Sign up at Cloudflare’s dashboard if you do not already have an account. Once you are logged in, go to My Profile → API Tokens and create a new token using the “Edit Cloudflare Workers” template. This scopes the token to Workers-related permissions only, rather than granting account-wide access, which matters if the token ends up in a CI secret store later in this tutorial.

Copy the token somewhere safe immediately. Cloudflare shows it exactly once. You will use it in Step 12 for the GitHub Actions pipeline, and you can also set it as an environment variable locally:

export CLOUDFLARE_API_TOKEN="your-token-here"
export CLOUDFLARE_ACCOUNT_ID="your-account-id-here"

Your account ID is visible on the right-hand sidebar of any domain overview page in the Cloudflare dashboard, or you can pull it with wrangler whoami once Wrangler is installed in the next step.

Step 2: Install Wrangler and Set Up an Auth Profile

Install Wrangler as a dev dependency inside your project rather than globally, which keeps version drift under control across machines and CI runners:

mkdir edge-api && cd edge-api
npm init -y
npm i -D wrangler@latest
npx wrangler --version

Log in with npx wrangler login, which opens a browser window to authorize the CLI against your account. If you juggle multiple Cloudflare accounts (a personal account plus a client’s, for example), Wrangler added auth profiles in July 2026. Auth profiles let you bind a named OAuth login to a specific directory, so you stop re-running wrangler login every time you switch projects. Set one up with:

npx wrangler login --auth-profile client-a
npx wrangler deploy --auth-profile client-a

If you only manage one account, the default login is fine and you can skip auth profiles entirely.

Step 3: Scaffold Your Worker Project

Cloudflare maintains create-cloudflare, an npm package that scaffolds a minimal Worker project and installs Wrangler alongside it. Run it from an empty directory:

npm create cloudflare@latest -- edge-api --type=hello-world --lang=ts --no-deploy
cd edge-api

The --no-deploy flag skips the immediate deploy prompt so you can review the generated files first. You should now see a project with src/index.ts, a wrangler.jsonc configuration file, a package.json, and a tsconfig.json. The generated project also pulls in @cloudflare/workers-types, which shipped a major v5 release this summer that trimmed dated entrypoints and exposes only current runtime types, so TypeScript autocomplete matches what actually ships in production.

Step 4: Understand Your wrangler.jsonc Configuration

Open wrangler.jsonc. This file replaces the older wrangler.toml format in newer scaffolds (TOML still works if you prefer it) and controls the Worker’s name, compatibility date, bindings, and routing. Set it up like this:

{
  "name": "edge-api",
  "main": "src/index.ts",
  "compatibility_date": "2026-08-01",
  "compatibility_flags": ["nodejs_compat"],
  "observability": {
    "enabled": true
  }
}

The compatibility_date field pins the runtime behavior your Worker gets, so future Cloudflare runtime changes cannot silently alter how your code behaves. Set it to the date you are deploying and only bump it forward deliberately, after reading the changelog for what changed. The nodejs_compat flag turns on a Node.js-style compatibility layer covering APIs like Buffer, stream, and crypto, which most npm packages assume exist even though Workers do not run on an actual Node.js process.

Step 5: Write Your First Worker

Replace the contents of src/index.ts with a small JSON API that has two routes: a health check and a lookup endpoint. This is the complete project you will extend in the following steps with caching, a database, and tracing.

export interface Env {
  API_VERSION: string;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/health") {
      return Response.json({
        status: "ok",
        version: env.API_VERSION,
        colo: request.cf?.colo ?? "unknown",
      });
    }

    if (url.pathname.startsWith("/lookup/")) {
      const id = url.pathname.split("/lookup/")[1];
      if (!id) {
        return Response.json({ error: "missing id" }, { status: 400 });
      }
      return Response.json({ id, source: "edge", timestamp: Date.now() });
    }

    return new Response("Not found", { status: 404 });
  },
} satisfies ExportedHandler<Env>;

Add the API_VERSION variable to wrangler.jsonc under a vars block:

{
  "vars": {
    "API_VERSION": "1.0.0"
  }
}

The request.cf.colo field is worth noting here: it tells you which Cloudflare data center handled the request, useful later when you are debugging cache behavior across regions.

Step 6: Test Locally With wrangler dev

Run the local development server, which simulates the Workers runtime on your machine using the same workerd engine Cloudflare runs in production:

npx wrangler dev

Wrangler starts a local server, typically on http://localhost:8787. Test both routes with curl:

curl http://localhost:8787/health
# {"status":"ok","version":"1.0.0","colo":"unknown"}

curl http://localhost:8787/lookup/42
# {"id":"42","source":"edge","timestamp":1786737600000}

The colo field reads “unknown” locally because there is no real edge location behind wrangler dev. That value only populates once the Worker runs on Cloudflare’s actual network. Keep this dev server running in a separate terminal tab while you continue through the next steps; Wrangler hot-reloads on every file save.

Step 7: Connect a Database With Hyperdrive

Hyperdrive pools and caches database connections at the edge so a Worker does not pay the cost of a fresh TCP and TLS handshake to your origin database on every request. Cloudflare marked MySQL support in Hyperdrive as generally available on August 14, 2026, which puts it on equal footing with the existing Postgres support. If your data layer already runs MySQL, you no longer need to migrate it to use Hyperdrive.

Create a Hyperdrive configuration pointing at your existing MySQL database:

npx wrangler hyperdrive create edge-api-db \
  --connection-string="mysql://user:password@your-db-host:3306/app"

The command returns a Hyperdrive ID. Add it to wrangler.jsonc as a binding:

{
  "hyperdrive": [
    {
      "binding": "HYPERDRIVE",
      "id": "your-hyperdrive-id"
    }
  ]
}

Inside the Worker, connect through the binding’s connection string rather than the raw database host, so traffic routes through Hyperdrive’s pooled connections instead of opening a new one per request. Use a MySQL driver that supports the Workers runtime (such as mysql2 with the nodejs_compat flag enabled from Step 4), and pass env.HYPERDRIVE.connectionString as the connection target.

Step 8: Deploy to a Temporary Preview Environment

Before pushing to production, Cloudflare’s June 2026 release of temporary preview accounts gives you a way to deploy without touching your main account at all. This was originally built so AI coding agents and CI pipelines could ship a Worker for review without requiring a full Cloudflare login upfront, but it works just as well for a human doing a quick sanity check:

npx wrangler deploy --temporary

This spins up an ephemeral preview account and returns a URL on a Cloudflare-managed subdomain. It is disposable by design: do not point real traffic or a custom domain at it, and expect it to expire. Use it to confirm your Worker starts up cleanly and responds correctly before moving to Step 9.

Step 9: Deploy to Production

Once you have confirmed the temporary deploy works, ship to your real account:

npx wrangler deploy

Cloudflare’s documentation is explicit about what this command does by default: “By default, these two concepts are coupled together — when you run wrangler deploy, Workers creates a new version and immediately deploys it to 100% of traffic in a single step.” There is no built-in canary rollout unless you configure gradual deployments separately through the dashboard or the versions API. For a first deploy that is fine; for a Worker already serving real traffic, look into versioned, percentage-based rollouts before you rely on wrangler deploy alone.

By default your Worker runs across Cloudflare’s edge network with no extra configuration required, since Cloudflare’s own guidance for Workers is to “deploy once, run in Cloudflare’s 335+ cities by default,” with an optional Smart Placement mode that runs a Worker nearer to your backend data if that reduces end-to-end latency.

Step 10: Configure Workers Cache for Performance

Workers Cache is the biggest addition to the platform this summer. It is a regionally tiered cache that sits directly in front of your Worker’s entrypoint, combining automatic lower- and upper-tier caching across Cloudflare’s network with native stale-while-revalidate support, so a user never waits on a cache refresh; the Worker serves the stale response immediately while it revalidates in the background.

Enable it on the health check route, since that response changes rarely but should still reflect a fresh deploy within a minute or two:

if (url.pathname === "/health") {
  const response = Response.json({
    status: "ok",
    version: env.API_VERSION,
    colo: request.cf?.colo ?? "unknown",
  });
  response.headers.set(
    "Cache-Control",
    "public, max-age=60, stale-while-revalidate=300"
  );
  return response;
}

Workers Cache also added first-class support for the Vary header, which lets you cache different response variants (say, by language or device type) without collapsing them into one cache entry, and cache keys can be scoped through ctx.props for safe multi-tenant caching if your Worker serves more than one customer from the same code.

Fine-grained cache control with cf.vary

If your Worker calls an origin that returns its own Vary header, the fetch() API’s new cf.vary option lets you override how a single subrequest treats that header, using normalize, passthrough, or bypass per header name:

const originResponse = await fetch(originRequest, {
  cf: {
    vary: {
      "Accept-Language": "normalize",
    },
  },
});

This matters when an upstream API varies its response on a header you know is effectively constant for your users (a fixed API version string, for instance), where normalizing it lets Workers Cache hit far more often than it otherwise would.

Step 11: Add Tracing and Observability

The Workers runtime now ships built-in tracing.startActiveSpan() and span.end() APIs, added to the runtime on July 28, 2026, for instrumenting operations that outlive a single callback, such as a streaming response. Wrap the lookup route’s work in a custom span so it shows up alongside the automatic instrumentation Workers already provides for fetch, KV, and D1 calls:

import { tracing } from "cloudflare:workers";

async function handleLookup(id: string) {
  return tracing.startActiveSpan("lookup.resolve", async (span) => {
    const result = { id, source: "edge", timestamp: Date.now() };
    span.setAttribute("lookup.id", id);
    span.end();
    return result;
  });
}

Turn on observability in wrangler.jsonc (already set to true in Step 4) and view spans, request logs, and the newer Memory Usage chart in the Workers dashboard. That chart tracks V8 isolate memory at the P50, P90, P99, and P999 percentiles for each deployment, which is the fastest way to catch a memory regression against the 128 MB per-isolate ceiling before it starts throwing errors in production.

Step 12: Secure Secrets and Automate Deployment

Never put a database password or API key in wrangler.jsonc‘s plain-text vars block. Use encrypted secrets instead:

npx wrangler secret put DATABASE_PASSWORD

For CI/CD, add the API token from Step 1 as a GitHub repository secret named CLOUDFLARE_API_TOKEN, then commit a workflow file:

name: Deploy Worker
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npx wrangler deploy
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

On the access side, Cloudflare added two new protection modes for Workers through Cloudflare Access in August 2026, and extended Cloudflare Gateway to detect software package downloads and give you policy control over that supply-chain traffic. Pair that with Wrangler’s own new habit of sending npm package name, version constraints, and installed versions to Cloudflare’s API on every wrangler deploy or wrangler versions upload, which Cloudflare says it plans to use for dependency-based vulnerability alerts. Pin your dependency versions in package.json now so those future alerts are actually useful when they arrive.

The Complete Working Project

Here is everything from Steps 3 through 12 assembled into one project. The directory structure after following every step looks like this:

edge-api/
├── .github/
│   └── workflows/
│       └── deploy.yml
├── src/
│   └── index.ts
├── wrangler.jsonc
├── package.json
├── tsconfig.json
└── worker-configuration.d.ts

And here is the final src/index.ts, combining the health check, the lookup route, the cache headers from Step 10, and the tracing span from Step 11 into one file:

import { tracing } from "cloudflare:workers";

export interface Env {
  API_VERSION: string;
  HYPERDRIVE: Hyperdrive;
}

async function handleLookup(id: string) {
  return tracing.startActiveSpan("lookup.resolve", async (span) => {
    const result = { id, source: "edge", timestamp: Date.now() };
    span.setAttribute("lookup.id", id);
    span.end();
    return result;
  });
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/health") {
      const response = Response.json({
        status: "ok",
        version: env.API_VERSION,
        colo: request.cf?.colo ?? "unknown",
      });
      response.headers.set(
        "Cache-Control",
        "public, max-age=60, stale-while-revalidate=300"
      );
      return response;
    }

    if (url.pathname.startsWith("/lookup/")) {
      const id = url.pathname.split("/lookup/")[1];
      if (!id) {
        return Response.json({ error: "missing id" }, { status: 400 });
      }
      const result = await handleLookup(id);
      return Response.json(result);
    }

    return new Response("Not found", { status: 404 });
  },
} satisfies ExportedHandler<Env>;

Deploy it and confirm both routes respond from the edge rather than your local machine:

curl https://edge-api.your-subdomain.workers.dev/health
# {"status":"ok","version":"1.0.0","colo":"SJC"}

curl https://edge-api.your-subdomain.workers.dev/lookup/42
# {"id":"42","source":"edge","timestamp":1786737600000}

The colo value now shows a real three-letter airport code for the Cloudflare data center that handled the request (SJC for San Jose, LHR for London, and so on), which confirms the Worker is genuinely running on the edge network rather than a single origin server.

Wrangler CLI Quick Reference

These are the commands used throughout this tutorial, gathered in one place for reference once you are working on your own project.

CommandWhat it does
npx wrangler loginAuthorizes the CLI against your Cloudflare account through a browser flow
npx wrangler login --auth-profile NAMEBinds a named login to the current directory for multi-account setups
npm create cloudflare@latestScaffolds a new Worker project with Wrangler and TypeScript types included
npx wrangler devRuns the Worker locally on the workerd engine for fast iteration
npx wrangler deploy --temporaryDeploys to a disposable preview account with no login required upfront
npx wrangler deployCreates a new version and routes 100% of traffic to it on your real account
npx wrangler secret put NAMEStores an encrypted secret, prompted interactively, never written to disk in plain text
npx wrangler hyperdrive create NAMECreates a pooled database connection config for Postgres or MySQL
npx wrangler tailStreams live logs and trace events from a deployed Worker
npx wrangler whoamiPrints the account ID and email tied to the current login

Cloudflare Workers Free vs. Paid Plan Limits (August 2026)

Know these numbers before you hit them in production. They come directly from Cloudflare’s current pricing and limits documentation.

LimitWorkers FreeWorkers Paid
Base price$0$5/month minimum
Requests100,000/day (resets 00:00 UTC)10 million/month included, then $0.30 per additional million
CPU time per invocation10 msUp to 5 min (default 30 sec); 30 million CPU ms/month included, then $0.02 per additional million
Memory per isolate128 MB128 MB
Subrequests per invocation50 external + 1,000 to Cloudflare services10,000 default, configurable up to 10,000,000
Workers KV1 GB storage, 100,000 reads/day, 1,000 writes/dayPay-as-you-go beyond free allotment
D1 database5 GB storage, 5 million rows read/day, 100,000 rows written/dayPay-as-you-go beyond free allotment
R2 object storage10 GB-month, 1 million Class A ops/month, 10 million Class B ops/monthPay-as-you-go beyond free allotment
Durable Objects100,000 requests/dayPay-as-you-go beyond free allotment

The subrequest limit is worth calling out specifically. Until a February 2026 change, Workers on paid plans were capped at 1,000 subrequests per invocation; that ceiling is now 10,000 by default and can be raised as high as 10,000,000 through the limits field in Wrangler for workloads that genuinely need to fan out to many services from a single request.

Common Pitfalls When Deploying Cloudflare Workers

  • Forgetting to pin compatibility_date. Leaving it unset or letting it drift forward without reading the changelog means a runtime update can change behavior underneath a Worker that nobody touched.
  • Storing secrets in vars instead of wrangler secret. The vars block in wrangler.jsonc is plain text and gets committed to source control. Anything sensitive belongs behind wrangler secret put.
  • Treating wrangler deploy –temporary as a staging environment. Temporary preview accounts are disposable by design. Point real users or a custom domain at one and you will lose it without warning.
  • Assuming nodejs_compat gives you a full Node.js runtime. It approximates common Node APIs, but Workers still run on workerd, not Node.js itself. Packages that rely on native addons or spawn child processes will not work.
  • Ignoring the 128 MB per-isolate memory ceiling. This limit does not increase on the Paid plan. Large in-memory caches or unbounded array buffering inside a Worker will hit it regardless of what plan you are on.
  • Skipping the memory metrics chart after a deploy. A creeping P99 memory number is often the first sign of a leak, and it is visible well before users start seeing errors.
  • Pointing a MySQL driver straight at the origin database instead of the Hyperdrive binding. This defeats the entire purpose of Hyperdrive, since each request pays the full TCP and TLS handshake cost again. Always connect through env.HYPERDRIVE.connectionString.

Troubleshooting Cloudflare Workers Deployments

SymptomLikely causeFix
Error 1027 on requestsFree plan daily request quota (100,000/day) exceededUpgrade to Paid, or add caching to cut origin requests
“Exceeded CPU time limit” errorsFree plan’s 10 ms CPU cap hit by heavy synchronous workMove CPU-heavy logic off the hot path, or upgrade to Paid for up to 5 min
Worker builds locally but fails on deploycompatibility_date mismatch or missing compatibility_flagsConfirm wrangler.jsonc matches what wrangler dev is using
nodejs_compat import errors (e.g. “Buffer is not defined”)compatibility_flags missing nodejs_compatAdd “nodejs_compat” to compatibility_flags and redeploy
Hyperdrive queries time outConnection string points at origin DB directly instead of the Hyperdrive bindingUse env.HYPERDRIVE.connectionString, not the raw DB host
Workers Cache never returns a HITMissing or misconfigured Cache-Control header on the responseExplicitly set Cache-Control with max-age and stale-while-revalidate
Wrong cache variant served to different usersVary header not configured, or cf.vary set to bypass unintentionallySet Vary explicitly and audit cf.vary options per subrequest
“Too many subrequests” errorFree plan’s 50 external subrequest cap hitBatch calls, cache upstream responses, or upgrade to Paid
Spans missing from the tracing dashboardobservability.enabled left false, or span.end() never calledEnable observability in wrangler.jsonc and always close spans
wrangler deploy –temporary link expiresExpected behavior for temporary preview accountsRe-run the command for a new session, or promote to a real account with wrangler deploy

Advanced Tips for Production Workers

Once the base deployment is stable, a few practices separate a hobby Worker from one that can carry real traffic. Scope cache keys through ctx.props whenever a single Worker serves multiple tenants or customers, so one customer’s cached response can never leak into another’s request. This matters more than it sounds like it should the first time two customers share a route pattern.

Watch the Workers AI model list if your Worker calls into it. As of the July 28, 2026 changelog, models including @cf/moonshotai/kimi-k2.6 and @cf/moonshotai/kimi-k2.7-code now require the Workers Paid plan, so budget for that if your Worker leans on agentic or code-generation models rather than assuming Free-plan access continues indefinitely.

If you chain Workers with Cloudflare Workflows for multi-step, retryable jobs, note that per-step billing for storage and step execution took effect no earlier than August 10, 2026 (request and CPU-time billing were already active from the public beta). Design retry logic with that per-step cost in mind, especially for workflows that use retries.delay functions to back off, since each retried step is billed.

Finally, treat compatibility_date bumps as their own reviewed change, not a routine dependency update. Read the entry in Cloudflare’s Workers changelog for the date you are moving to before you ship it, the same way you would read release notes before a major library upgrade.

FAQ: Deploying Cloudflare Workers in 2026

Do I need a paid Cloudflare plan to follow this tutorial?

No. Every step in this tutorial, including Workers Cache, tracing, and a temporary Hyperdrive-backed deploy, works on the Workers Free plan. You would only need Paid if you exceed 100,000 requests a day, need more than 10 ms of CPU time per invocation, or want to raise the subrequest limit above 50 per request.

What is the difference between wrangler.toml and wrangler.jsonc?

Both configure the same options. wrangler.jsonc is JSON with comment support and is what newer scaffolds from create-cloudflare generate by default; wrangler.toml is the older format and is still fully supported. Pick whichever your team is more comfortable with, but do not maintain both for the same Worker.

Can I use Cloudflare Workers with a MySQL database?

Yes, and as of August 14, 2026 this is fully supported through Hyperdrive at general availability rather than a beta feature. Hyperdrive pools connections at the edge so your Worker does not need to open a fresh connection to the database for every request, which is normally the biggest latency cost of connecting serverless functions to a traditional database.

Is wrangler deploy –temporary safe for production traffic?

No. Temporary preview accounts are explicitly disposable and were built for quick agent- or CI-driven previews, not production hosting. Use wrangler deploy without the --temporary flag, authenticated against your real account, for anything users will actually hit.

How is Workers Cache different from the standard Cloudflare CDN cache?

Workers Cache sits specifically in front of Worker entrypoints rather than being a general CDN cache for static assets. It adds native stale-while-revalidate behavior, first-class Vary header handling, and per-entrypoint cache control that you configure in code through response headers and the cf.vary fetch option, rather than through zone-level dashboard settings alone.

Why does my Worker’s memory metric matter if it stays under 128 MB?

Staying under the ceiling today does not guarantee you stay under it tomorrow. The P50–P999 memory chart in the dashboard shows how close a given deployment runs to the 128 MB isolate limit, which does not increase on the Paid plan. A Worker that creeps from P99 at 40 MB to P99 at 110 MB across a few releases is one dependency bump away from hard failures.

Do I need to instrument every function with tracing spans?

No. Workers already auto-instruments fetch, KV, and D1 calls. Add custom spans with tracing.startActiveSpan() specifically around logic that is not automatically covered, such as your own business logic, streaming operations, or calls to Hyperdrive, where a custom span gives you visibility you would not otherwise have.

What happens if I exceed the subrequest limit?

The Worker throws an exception once it exceeds 50 external subrequests on Free, or 10,000 on Paid by default. If your workload legitimately needs to fan out to more services than that from a single invocation, Paid plan customers can raise the ceiling up to 10,000,000 through the limits configuration in Wrangler, though redesigning around batching is usually the better fix first.

Can I roll back a bad deploy?

Yes. Because wrangler deploy creates a version before routing traffic to it, previous versions stay available. Roll back through the Cloudflare dashboard’s Deployments tab, or redeploy an older commit through your CI pipeline. This is also why gradual, percentage-based rollouts are worth setting up separately from the default wrangler deploy behavior once a Worker is carrying meaningful production traffic, since they let you catch a bad version before it reaches 100% of requests.

Does the Wrangler dependency data sent to Cloudflare expose my source code?

No. What Wrangler sends on deploy and versions upload is limited to npm package names, the version constraints from package.json, and the exact installed versions from your lockfile, not your application source. Cloudflare has said this data is intended to power future dependency vulnerability alerts, similar to what GitHub’s Dependabot already does for a repository.

That covers the full path from an empty directory to a Worker running in production: scaffolding with create-cloudflare, a typed fetch handler, a pooled MySQL connection through Hyperdrive, a tiered edge cache, custom tracing spans, and a GitHub Actions pipeline that redeploys on every push to main. Revisit the limits table and the troubleshooting section any time a deploy behaves unexpectedly, since most of what breaks a Worker in practice traces back to one of the rows in those two tables.

These articles cover adjacent infrastructure and security topics worth reading alongside this tutorial:

Authority references: