The most surprising thing about Wrangler’s worker deployment is that you’re not actually deploying to Cloudflare Workers, but rather deploying through Cloudflare’s edge network to a globally distributed object store.

Let’s see it in action. Imagine you have a simple "hello world" worker.

// index.js
export default {
  async fetch(request) {
    return new Response(`Hello from ${request.url}`);
  },
}

First, you need a wrangler.toml file to configure your worker.

name = "my-cool-worker"
main = "index.js"
compatibility_date = "2023-10-26"

[site]
bucket = "./public" # Directory for static assets

[triggers]
crons = ["0 * * * *"] # Example cron trigger

Now, let’s build and deploy.

wrangler publish

This command does a few things. It first builds your worker code, bundling any dependencies. Then, it uploads the compiled JavaScript to Cloudflare’s internal object store. Finally, it tells Cloudflare’s edge network to route requests matching your worker’s route (configured in your Cloudflare dashboard or via wrangler.toml’s routes section) to this deployed code. If you’ve configured static site hosting with the [site] section, wrangler publish will also upload the contents of your public directory to a separate, dedicated asset store, and your worker can then serve those assets.

The compatibility_date in wrangler.toml is crucial. It pins your worker to a specific set of Cloudflare runtime features and APIs. This ensures that your worker continues to function predictably even as Cloudflare updates its underlying infrastructure. You can think of it as a version lock for the Cloudflare Workers environment.

Here’s what’s happening under the hood when you run wrangler publish. Wrangler first reads your wrangler.toml. It then invokes the Rust-based bundler (similar to esbuild) to create a single JavaScript file containing your worker code and its dependencies. This bundled file is then uploaded to Cloudflare’s internal infrastructure. If you have the [site] configuration, the contents of your specified bucket directory are uploaded to Cloudflare’s asset delivery network. Finally, Wrangler updates the routing rules associated with your Cloudflare account to point to this new version of your worker. When a request hits Cloudflare’s edge, the edge server checks its routing table. If the request matches a route for your worker, it fetches the worker code from the nearest edge cache (or directly from the origin object store if not cached) and executes it.

A common point of confusion is how wrangler.toml interacts with your Cloudflare dashboard. While you can configure routes, custom domains, and environment variables in the dashboard, wrangler.toml acts as your infrastructure-as-code. For instance, defining routes in wrangler.toml using the routes array will automatically create or update those routes in your Cloudflare account when you run wrangler publish. This makes managing your worker deployments repeatable and versionable.

The [triggers] section allows you to schedule your worker to run at specific times using cron syntax. This is incredibly powerful for background jobs, data processing, or scheduled tasks that don’t require an incoming HTTP request. For example, crons = ["0 0 * * *"] would execute your worker every day at midnight UTC.

Most developers don’t realize that the worker code isn’t "run" on a specific server but rather executed in a sandboxed JavaScript environment on Cloudflare’s edge network. This means your code is geographically distributed, and the latency to execute your worker is minimized by running it as close as possible to the end-user making the request. The fetch event handler is the entry point for all incoming HTTP requests, and the request object it receives is a standard Web Fetch API Request object.

The next step is understanding how to manage different environments and secrets for your deployed workers.

Want structured learning?

Take the full Cloudflare course →