
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Thu, 09 Apr 2026 15:39:05 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Python Workers redux: fast cold starts, packages, and a uv-first workflow]]></title>
            <link>https://blog.cloudflare.com/python-workers-advancements/</link>
            <pubDate>Mon, 08 Dec 2025 06:00:00 GMT</pubDate>
            <description><![CDATA[ Recent advancements in Cloudflare Python Workers means fast cold starts, comprehensive package support, and a great developer experience. We explain how they were achieved and show how Python can be used to build serverless applications on Cloudflare. ]]></description>
            <content:encoded><![CDATA[ <p><sup><i>Note: This post was updated with additional details regarding AWS Lambda.</i></sup></p><p>Last year we announced <a href="https://blog.cloudflare.com/python-workers/"><u>basic support for Python Workers</u></a>, allowing Python developers to ship Python to region: Earth in a single command and take advantage of the <a href="https://workers.cloudflare.com/"><u>Workers platform</u></a>.</p><p>Since then, we’ve been hard at work making the <a href="https://developers.cloudflare.com/workers/languages/python/"><u>Python experience on Workers</u></a> feel great. We’ve focused on bringing package support to the platform, a reality that’s now here — with exceptionally fast cold starts and a Python-native developer experience.</p><p>This means a change in how packages are incorporated into a Python Worker. Instead of offering a limited set of built-in packages, we now support any <a href="https://pyodide.org/en/stable/usage/packages-in-pyodide.html"><u>package supported by Pyodide</u></a>, the WebAssembly runtime powering Python Workers. This includes all pure Python packages, as well as many packages that rely on dynamic libraries. We also built tooling around <a href="https://docs.astral.sh/uv/"><u>uv</u></a> to make package installation easy.</p><p>We’ve also implemented dedicated memory snapshots to reduce cold start times. These snapshots result in serious speed improvements over other serverless Python vendors. In cold start tests using common packages, Cloudflare Workers start over <b>2.4x faster than AWS Lambda</b> <b>without SnapStart </b>and <b>3x faster than Google Cloud Run</b>.</p><p>In this blog post, we’ll explain what makes Python Workers unique and share some of the technical details of how we’ve achieved the wins described above. But first, for those who may not be familiar with Workers or serverless platforms – and especially those coming from a Python background — let us share why you might want to use Workers at all.</p>
    <div>
      <h3>Deploying Python globally in 2 minutes</h3>
      <a href="#deploying-python-globally-in-2-minutes">
        
      </a>
    </div>
    <p>Part of the magic of Workers is simple code and easy global deployments. Let's start by showing how you can deploy a FastAPI app across the world with fast cold starts in less than two minutes.</p><p>A simple Worker using FastAPI can be implemented in a handful of lines:</p>
            <pre><code>from fastapi import FastAPI
from workers import WorkerEntrypoint
import asgi

app = FastAPI()

@app.get("/")
async def root():
   return {"message": "This is FastAPI on Workers"}

class Default(WorkerEntrypoint):
   async def fetch(self, request):
       return await asgi.fetch(app, request.js_object, self.env)</code></pre>
            <p>To deploy something similar, just make sure you have <code>uv</code> and <code>npm</code> installed, then run the following:</p>
            <pre><code>$ uv tool install workers-py
$ pywrangler init --template \
    https://github.com/cloudflare/python-workers-examples/03-fastapi
$ pywrangler deploy</code></pre>
            <p>With just a little code and a <code>pywrangler deploy</code>, you’ve now deployed your application across Cloudflare’s edge network that extends to <a href="https://www.cloudflare.com/network/"><u>330 locations across 125 countries</u></a>. No worrying about infrastructure or scaling.</p><p>And for many use cases, Python Workers are completely free. Our free tier offers 100,000 requests per day and 10ms CPU time per invocation. For more information, check out the <a href="https://developers.cloudflare.com/workers/platform/pricing/"><u>pricing page in our documentation</u></a>.</p><p>For more examples, <a href="https://github.com/cloudflare/python-workers-examples"><u>check out the repo in GitHub</u></a>. And read on to find out more about Python Workers.</p>
    <div>
      <h3>So what can you do with Python Workers?</h3>
      <a href="#so-what-can-you-do-with-python-workers">
        
      </a>
    </div>
    <p>Now that you’ve got a Worker, just about anything is possible. You write the code, so you get to decide. Your Python Worker receives HTTP requests and can make requests to any server on the public Internet.</p><p>You can set up cron triggers, so your Worker runs on a regular schedule. Plus, if you have more complex requirements, you can make use of <a href="https://blog.cloudflare.com/python-workflows/"><u>Workflows for Python Workers</u></a>, or even long-running WebSocket servers and clients <a href="https://developers.cloudflare.com/durable-objects/get-started/"><u>using Durable Objects</u></a>.</p><p>Here are more examples of the sorts of things you can do using Python Workers:</p><ul><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/03-fastapi"><u>Render HTML templates on the edge, with a library like Jinja, while fetching dynamic content directly from your server</u></a></p></li><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/11-opengraph"><u>Modify the response from your server — for example, you can inject opengraph tags into your HTML dynamically, based on the content requested</u></a></p></li><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/15-chatroom"><u>Build a chat room using Durable Objects and WebSockets</u></a></p></li><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/14-websocket-stream-consumer"><u>Consume data from WebSocket connections, like the Bluesky firehose</u></a></p></li><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/12-image-gen"><u>Generate images using the Pillow Python package</u></a></p></li><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/13-js-api-pygments"><u>Write a small Python Worker that exposes the API of a Python package, and then access it from your JavaScript Worker using RPC</u></a></p></li></ul>
    <div>
      <h3>Faster package cold starts</h3>
      <a href="#faster-package-cold-starts">
        
      </a>
    </div>
    <p>Serverless platforms like Workers save you money by only running your code when it’s necessary to do so. This means that if your Worker isn’t receiving requests, it may be shut down and will need to be restarted once a new request comes in. This typically incurs a resource overhead we refer to as the “cold start.” It’s important to keep these as short as possible to minimize latency for end users.</p><p>In standard Python, booting the runtime is expensive, and our initial implementation of Python Workers focused on making the <i>runtime</i> boot fast. However, we quickly realized that this wasn’t enough. Even if the Python runtime boots quickly, in real-world scenarios the initial startup usually includes loading modules from packages, and unfortunately, in Python many popular packages can take several seconds to load.</p><p>We set out to make cold starts fast, regardless of whether packages were loaded.</p><p>To measure realistic cold start performance, we set up a benchmark that imports common packages, as well as a benchmark running a “hello world” using a bare Python runtime. Standard Lambda is able to <a href="https://cold.picheta.me/#bare"><u>start just the runtime</u></a> quickly, but once you need to import packages, the cold start times shoot up. In order to optimize for faster cold starts with packages, you can use SnapStart on Lambda (which we will be adding to the linked benchmarks shortly). This incurs a cost to store the snapshot and an additional cost on every restore. Python Workers will automatically apply memory snapshots for free for every Python Worker.</p><p>Here are the average cold start times when loading three common packages (<a href="https://www.python-httpx.org/"><u>httpx</u></a>, <a href="https://fastapi.tiangolo.com/"><u>fastapi</u></a> and <a href="https://docs.pydantic.dev/latest/"><u>pydantic</u></a>):</p><table><tr><td><p><b>Platform</b></p></td><td><p><b>Mean Cold Start (secs)</b></p></td></tr><tr><td><p>Cloudflare Python Workers</p></td><td><p>1.027</p></td></tr><tr><td><p>AWS Lambda (without SnapStart)</p></td><td><p>2.502</p></td></tr><tr><td><p>Google Cloud Run</p></td><td><p>3.069</p></td></tr></table><p>In this case, <b>Cloudflare Python Workers have 2.4x faster cold starts than AWS Lambda without SnapStart and 3x faster cold starts than Google Cloud Run</b>. We achieved these low cold start numbers by using memory snapshots, and in a later section we explain how we did so.</p><p>We are regularly running these benchmarks. Go <a href="https://cold.picheta.me/#bare"><u>here</u></a> for up-to-date data and more info on our testing methodology.</p><p>We’re architecturally different from these other platforms — namely, <a href="https://developers.cloudflare.com/workers/reference/how-workers-works/"><u>Workers is isolate-based</u></a>. Because of that, our aims are high, and we are planning for a zero cold start future.</p>
    <div>
      <h3>Package tooling integrated with uv</h3>
      <a href="#package-tooling-integrated-with-uv">
        
      </a>
    </div>
    <p>The diverse package ecosystem is a large part of what makes Python so amazing. That’s why we’ve been hard at work ensuring that using packages in Workers is as easy as possible.</p><p>We realised that working with the existing Python tooling is the best path towards a great development experience. So we picked the <code>uv</code> package and project manager, as it’s fast, mature, and gaining momentum in the Python ecosystem.</p><p>We built our own tooling around <code>uv</code> called <a href="https://github.com/cloudflare/workers-py#pywrangler"><u>pywrangler</u></a>. This tool essentially performs the following actions:</p><ul><li><p>Reads your Worker’s pyproject.toml file to determine the dependencies specified in it</p></li><li><p>Includes your dependencies in a <code>python_modules</code> folder that lives in your Worker</p></li></ul><p>Pywrangler calls out to <code>uv</code> to install the dependencies in a way that is compatible with Python Workers, and calls out to <code>wrangler</code> when developing locally or deploying Workers. </p><p>Effectively this means that you just need to run <code>pywrangler dev</code> and <code>pywrangler</code> <code>deploy</code> to test your Worker locally and deploy it. </p>
    <div>
      <h3>Type hints</h3>
      <a href="#type-hints">
        
      </a>
    </div>
    <p>You can generate type hints for all of the <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/"><u>bindings</u></a> defined in your wrangler config using <code>pywrangler types</code>. These type hints will work with <a href="https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance"><u>Pylance</u></a> or with recent versions of <a href="https://mypy-lang.org/"><u>mypy</u></a>.</p><p>To generate the types, we use <a href="https://developers.cloudflare.com/workers/wrangler/commands/#types"><u>wrangler types</u></a> to create typescript type hints, then we use the typescript compiler to generate an abstract syntax tree for the types. Finally, we use the TypeScript hints — such as whether a JS object has an iterator field — to generate <code>mypy</code> type hints that work with the Pyodide foreign function interface.</p>
    <div>
      <h3>Decreasing cold start duration using snapshots</h3>
      <a href="#decreasing-cold-start-duration-using-snapshots">
        
      </a>
    </div>
    <p>Python startup is generally quite slow and importing a Python module can trigger a large amount of work. We avoid running Python startup during a cold start using memory snapshots.</p><p>When a Worker is deployed, we execute the Worker’s top-level scope and then take a memory snapshot and store it alongside your Worker. Whenever we are starting a new isolate for the Worker, we restore the memory snapshot and the Worker is ready to handle requests, with no need to execute any Python code in preparation. This improves cold start times considerably. For instance, starting a Worker that imports <code>fastapi</code>, <code>httpx</code> and <code>pydantic</code> without snapshots takes around 10 seconds. With snapshots, it takes 1 second.</p><p>The fact that Pyodide is built on WebAssembly enables this. We can easily capture the full linear memory of the runtime and restore it. </p>
    <div>
      <h4>Memory snapshots and Entropy</h4>
      <a href="#memory-snapshots-and-entropy">
        
      </a>
    </div>
    <p>WebAssembly runtimes do not require features like address space layout randomization for security, so most of the difficulties with memory snapshots on a modern operating system do not arise. Just like with native memory snapshots, we still have to carefully handle entropy at startup to avoid using the <a href="https://xkcd.com/221/"><u>XKCD random number generator</u></a> (we’re <a href="https://www.cloudflare.com/learning/ssl/lava-lamp-encryption/"><u>very into actual randomness</u></a>).</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Q2LYb3LZhfC2q3vQCFr1R/ff8f229715a0d37484585664c61908dc/image1.png" />
          </figure><p>By snapshotting memory, we might inadvertently lock in a seed value for randomness. In this case, future calls for “random” numbers would consistently return the same sequence of values across many requests.</p><p>Avoiding this is particularly challenging because Python uses a lot of entropy at startup. These include the libc functions <code>getentropy()</code> and <code>getrandom()</code> and also reading from <code>/dev/random</code> and <code>/dev/urandom</code>. All of these functions share the same implementation in terms of the JavaScript <code>crypto.getRandomValues()</code> function.</p><p>In Cloudflare Workers, <code>crypto.getRandomValues()</code> has always been disabled at startup in order to allow us to switch to using memory snapshots in the future. Unfortunately, the Python interpreter cannot bootstrap without calling this function. And many packages also require entropy at startup time. There are essentially two purposes for this entropy:</p><ul><li><p>Hash seeds for hash randomization</p></li><li><p>Seeds for pseudorandom number generators</p></li></ul><p>Hash randomization we do at startup time and accept the cost that each specific Worker has a fixed hash seed. Python has no mechanism to allow replacing the hash seed after startup.</p><p>For pseudorandom number generators (PRNG), we take the following approach:</p><p>At deploy time:</p><ol><li><p>Seed the PRNG with a fixed “poison seed”, then record the PRNG state.</p></li><li><p>Replace all APIs that call into the PRNG with an overlay that fails the deployment with a user error.</p></li><li><p>Execute the top level scope of user code.</p></li><li><p>Capture the snapshot.</p></li></ol><p>At run time:</p><ol><li><p>Assert that the PRNG state is unchanged. If it changed, we forgot the overlay for some method. Fail the deployment with an internal error.</p></li><li><p>After restoring the snapshot, reseed the random number generator before executing any handlers.</p></li></ol><p>With this, we can ensure that PRNGs can be used while the Worker is running, but stop Workers from using them during initialization and pre-snapshot.</p>
    <div>
      <h4>Memory snapshots and WebAssembly state</h4>
      <a href="#memory-snapshots-and-webassembly-state">
        
      </a>
    </div>
    <p>An additional difficulty arises when creating memory snapshots on WebAssembly: The memory snapshot we are saving consists only of the WebAssembly linear memory, but the full state of the Pyodide WebAssembly instance is not contained in the linear memory. </p><p>There are two tables outside of this memory.</p><p>One table holds the values of function pointers. Traditional computers use a “Von Neumann” architecture, which means that code exists in the same memory space as data, so that calling a function pointer is a jump to some memory address. WebAssembly has a “Harvard architecture” where code lives in a separate address space. This is key to most of the security guarantees of WebAssembly and in particular why WebAssembly does not need address space layout randomization. A function pointer in WebAssembly is an index into the function pointer table.</p><p>A second table holds all JavaScript objects referenced from Python. JavaScript objects cannot be directly stored into memory because the JavaScript virtual machine forbids directly obtaining a pointer to a JavaScript object. Instead, they are stored into a table and represented in WebAssembly as an index into the table.</p><p>We need to ensure that both of these tables are in exactly the same state after we restore a snapshot as they were when we captured the snapshot.</p><p>The function pointer table is always in the same state when the WebAssembly instance is initialized and is updated by the dynamic loader when we load dynamic libraries — native Python packages like numpy. </p><p>To handle dynamic loading:</p><ol><li><p>When taking the snapshot, we patch the loader to record the load order of dynamic libraries, the address in memory where the metadata for each library is allocated, and the function pointer table base address for relocations. </p></li><li><p>When restoring the snapshot, we reload the dynamic libraries in the same order, and we use a patched memory allocator to place the metadata in the same locations. We assert that the current size of the function pointer table matches the function pointer table base we recorded for the dynamic library.</p></li></ol><p>All of this ensures that each function pointer has the same meaning after we’ve restored the snapshot as it had when we took the snapshot.</p><p>To handle the JavaScript references, we implemented a fairly limited system. If a JavaScript object is accessible from globalThis by a series of property accesses, we record those property accesses and replay them when restoring the snapshot. If any reference exists to a JavaScript object that is not accessible in this way, we fail deployment of the Worker. This is good enough to deal with all the existing Python packages with Pyodide support, which do top level imports like:</p>
            <pre><code>from js import fetch</code></pre>
            
    <div>
      <h3>Reducing cold start frequency using sharding</h3>
      <a href="#reducing-cold-start-frequency-using-sharding">
        
      </a>
    </div>
    <p>Another important characteristic of our performance strategy for Python Workers is sharding. There is a very detailed description of what went into its implementation <a href="https://blog.cloudflare.com/eliminating-cold-starts-2-shard-and-conquer/"><u>here</u></a>. In short, we now route requests to existing Worker instances, whereas before we might have chosen to start a new instance.</p><p>Sharding was actually enabled for Python Workers first and proved to be a great test bed for it. A cold start is far more expensive in Python than in JavaScript, so ensuring requests are routed to an already-running isolate is especially important.</p>
    <div>
      <h3>Where do we go from here?</h3>
      <a href="#where-do-we-go-from-here">
        
      </a>
    </div>
    <p>This is just the start. We have many plans to make Python Workers better:</p><ul><li><p>More developer-friendly tooling</p></li><li><p>Even faster cold starts by utilising our isolate architecture</p></li><li><p>Support for more packages</p></li><li><p>Support for native TCP sockets, native WebSockets, and more bindings</p></li></ul><p>To learn more about Python Workers, check out the documentation available <a href="https://developers.cloudflare.com/workers/languages/python/"><u>here</u></a>. To get help, be sure to join our <a href="https://discord.cloudflare.com/"><u>Discord</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Python]]></category>
            <guid isPermaLink="false">4TlYGwUzSQPqAGP7gIEton</guid>
            <dc:creator>Dominik Picheta</dc:creator>
            <dc:creator>Hood Chatham</dc:creator>
            <dc:creator>Mike Nomitch</dc:creator>
        </item>
        <item>
            <title><![CDATA[Simple, scalable, and global: Containers are coming to Cloudflare Workers in June 2025]]></title>
            <link>https://blog.cloudflare.com/cloudflare-containers-coming-2025/</link>
            <pubDate>Fri, 11 Apr 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare Containers are coming this June. Run new types of workloads on our network with an experience that is simple, scalable, global and deeply integrated with Workers. ]]></description>
            <content:encoded><![CDATA[ <p>It is almost the end of Developer Week and we haven’t talked about containers: until now. As some of you <a href="https://blog.cloudflare.com/container-platform-preview/"><u>may know</u></a>, we’ve been working on a container platform behind the scenes for some time.</p><p>In late June, we plan to release Containers in open beta, and today we’ll give you a sneak peek at what makes it unique.</p><p>Workers are the simplest way to ship software around the world with little overhead. But sometimes you need to do more. You might want to:</p><ul><li><p>Run user-generated code in any language</p></li><li><p>Execute a CLI tool that needs a full Linux environment</p></li><li><p>Use several gigabytes of memory or multiple CPU cores</p></li><li><p>Port an existing application from AWS, GCP, or Azure without a major rewrite</p></li></ul><p>Cloudflare Containers let you do all of that while being simple, scalable, and global.</p><p>Through a deep integration with <a href="https://www.cloudflare.com/developer-platform/products/workers/">Workers</a> and an architecture built on <a href="https://www.cloudflare.com/developer-platform/products/durable-objects/">Durable Objects</a>, Workers can be your:</p><ul><li><p><b>API Gateway</b>: Letting you control routing, authentication, caching, and rate-limiting before requests reach a container</p></li><li><p><b>Service Mesh</b>: Creating private connections between containers with a programmable routing layer</p></li><li><p><b>Orchestrator</b>: Allowing you to write custom scheduling, scaling, and health checking logic for your containers</p></li></ul><p>Instead of having to deploy new services, write custom Kubernetes operators, or wade through control plane configuration to extend the platform, you just write code.</p><p>Let’s see what it looks like.</p>
    <div>
      <h2>Deploying different application types</h2>
      <a href="#deploying-different-application-types">
        
      </a>
    </div>
    
    <div>
      <h3>A stateful workload: executing AI-generated code</h3>
      <a href="#a-stateful-workload-executing-ai-generated-code">
        
      </a>
    </div>
    <p>First, let’s take a look at a stateful example.</p><p>Imagine you are building a platform where end-users can run code generated by an <a href="https://www.cloudflare.com/learning/ai/what-is-large-language-model/">LLM</a>. This code is untrusted, so each user needs their own secure sandbox. Additionally, you want users to be able to run multiple requests in sequence, potentially writing to local files or saving in-memory state.</p><p>To do this, you need to create a container on-demand for each user session, then route subsequent requests to that container. Here’s how you can accomplish this:</p><p>First, you write some <a href="https://github.com/cloudflare/containers-demos/blob/main/ai/wrangler.jsonc#L6"><u>basic Wrangler config</u></a>, then you route requests to containers via your Worker:</p>
            <pre><code>import { Container } from "cloudflare:workers";

export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    if (url.pathname.startsWith("/execute-code")) {
      const { sessionId, messages } = await request.json();
      // pass in prompt to get the code from Llama 4
      const codeToExecute = await env.AI.run("@cf/meta/llama-4-scout-17b-16e-instruct", { messages });

      // get a different container for each user session
      const id = env.CODE_EXECUTOR.idFromName(sessionId);
      const sandbox = env.CODE_EXECUTOR.get(id);

      // execute a request on the container
      return sandbox.fetch("/execute-code", { method: "POST", body: codeToExecute });
    }

    // ... rest of Worker ...
  },
};

// define your container using the Container class from cloudflare:workers
export class CodeExecutor extends Container {
  defaultPort = 8080;
  sleepAfter = "1m";
}</code></pre>
            <p>Then, deploy your code with a single command: <code>wrangler deploy</code>. This builds your container image, pushes it to Cloudflare’s registry, readies containers to boot quickly across the globe, and deploys your Worker.</p>
            <pre><code>$ wrangler deploy</code></pre>
            <p>That’s it.</p><p>How does it work?</p><p>Your Worker creates and starts up containers on-demand. Each time you call <code>env.CODE_EXECUTOR.get(id)</code> with a unique ID, it sends requests to a unique container instance. The container will automatically boot on the first <code>fetch</code>, then put itself to sleep after a configurable timeout, in this case 1 minute. You only pay for the time that the container is actively running.</p><p>When you request a new container, we boot one in a Cloudflare location near the incoming request. This means that low-latency workloads are well-served no matter the region. Cloudflare takes care of all the pre-warming and caching so you don’t have to think about it.</p><p>This allows each user to run code in their own secure environment.</p>
    <div>
      <h3>Stateless and global: FFmpeg everywhere</h3>
      <a href="#stateless-and-global-ffmpeg-everywhere">
        
      </a>
    </div>
    <p>Stateless and autoscaling applications work equally well on Cloudflare Containers.</p><p>Imagine you want to run a container that takes a video file and turns it into an animated GIF using <a href="https://www.ffmpeg.org/"><u>FFmpeg</u></a>. Unlike the previous example, any container can serve any request, but you still don’t want to send bytes across an ocean and back unnecessarily. So, ideally the app can be deployed everywhere.</p><p>To do this, you declare a container in Wrangler config and turn on <code>autoscaling</code>. This specific configuration ensures that one instance is always running and if CPU usage increases beyond 75% of capacity, additional instances are added:</p>
            <pre><code>"containers": [
  {
    "class_name": "GifMaker",
    "image": "./Dockerfile", // container source code can be alongside Worker code
    "instance_type": "basic",
    "autoscaling": {
      "minimum_instances": 1,
      "cpu_target": 75,
    }
  }
],
// ...rest of wrangler.jsonc...</code></pre>
            <p>To route requests, you just call <code>env.GIF_MAKER.fetch</code> and requests are automatically sent to the closest container:</p>
            <pre><code>import { Container } from "cloudflare:workers";

export class GifMaker extends Container {
  defaultPort: 1337,
}

export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    if (url.pathname === "/make-gif") {
      return env.GIF_MAKER.fetch(request)
    }

    // ... rest of Worker ...
  },
};</code></pre>
            
    <div>
      <h3>Going beyond the basics</h3>
      <a href="#going-beyond-the-basics">
        
      </a>
    </div>
    <p>From the examples above, you can see that getting a basic container service running on Workers just takes a few lines of config and a little Workers code. There’s no need to worry about capacity, artifact registries, regions, or scaling.</p><p>For more advanced use, we’ve designed Cloudflare Containers to run on top of Durable Objects and work in tandem with Workers. Let’s take a look at the underlying architecture and see some of the advanced use cases it enables.</p>
    <div>
      <h2>Durable Objects as programmable sidecars</h2>
      <a href="#durable-objects-as-programmable-sidecars">
        
      </a>
    </div>
    <p>Routing to containers is enabled using <a href="https://developers.cloudflare.com/durable-objects/"><u>Durable Objects</u></a> under the hood. In the examples above, the <code>Container</code> class from <code>cloudflare:workers</code> just wraps a container-enabled Durable Object and provides helper methods for common patterns. In the rest of this post, we’ll look at examples using Durable Objects directly, as this should shed light on the platform’s underlying design.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/tEAz34lNlHaJtLQVp1qia/734fc01c90e2aca8e5d75c060f09be9e/1.png" />
          </figure><p>Each Durable Object acts as a programmable sidecar that can proxy requests to the container and manages its lifecycle. This allows you to control and extend your containers in ways that are hard on other platforms. </p><p>You can manually start, stop, and execute commands on a specific container by calling RPC methods on its Durable Object, which now has a new object at <code>this.ctx.container</code>:</p>
            <pre><code>class MyContainer extends DurableObject {
  // these RPC methods are callable from a Worker
  async customBoot(entrypoint, envVars) {
    this.ctx.container.start({ entrypoint, env: envVars });
  }

  async stopContainer() {
    const SIGTERM = 15;
    this.ctx.container.signal(SIGTERM);
  }

  async startBackupScript() {
    await this.ctx.container.exec(["./backup"]);
  }
}</code></pre>
            <p>You can also monitor your container and run hooks in response to Container status changes.</p><p>For instance, say you have a CI job that runs builds in a Container. You want to post a message to a <a href="https://developers.cloudflare.com/queues/"><u>Queue</u></a> based on the exit status. You can easily program this behavior:</p>
            <pre><code>class BuilderContainer extends DurableObject {
  constructor(ctx, env) {
    super(ctx, env)
    async function onContainerExit() {
      await this.env.QUEUE.send({ status: "success", message: "Build Complete" });
    }

    async function onContainerError(err) {
      await this.env.QUEUE.send({ status: "error", message: err});
    }

    this.ctx.container.start();
    this.ctx.container.monitor().then(onContainerExit).catch(onContainerError); 
  }

  async isRunning() { return this.ctx.container.running; }
}</code></pre>
            <p>And lastly, if you have state that needs to be loaded into a container each time it runs, you can use status hooks to persist state from the container before it sleeps and to reload state into the container after it starts:</p>
            <pre><code>import { startAndWaitForPort } from "./helpers"

class MyContainer extends DurableObject {
  constructor(ctx, env) {
    super(ctx, env)
    this.ctx.blockConcurrencyWhile(async () =&gt; {
      this.ctx.storage.sql.exec('CREATE TABLE IF NOT EXISTS state (value TEXT)');
      this.ctx.storage.sql.exec("INSERT INTO state (value) SELECT '' WHERE NOT EXISTS 
(SELECT * FROM state)");
      await startAndWaitForPort(this.ctx.container, 8080);
      await this.setupContainer();
      this.ctx.container.monitor().then(this.onContainerExit); 
    });
  }

  async setupContainer() {
    const initialState = this.ctx.storage.sql.exec('SELECT * FROM state LIMIT 1').one().value;
    return this.ctx.container
      .getTcpPort(8080)
      .fetch("http://container/state", { body: initialState, method: 'POST' });
  }

  async onContainerExit() {
    const response = await this.ctx.container
      .getTcpPort(8080)
      .fetch('http://container/state');
    const newState = await response.text();
    this.ctx.storage.sql.exec('UPDATE state SET value = ?', newState);
  }
}</code></pre>
            
    <div>
      <h2>Building around your Containers with Workers</h2>
      <a href="#building-around-your-containers-with-workers">
        
      </a>
    </div>
    <p>Not only do Durable Objects allow you to have fine-grained control over the Container lifecycle, the whole Workers platform allows you to extend routing and scheduling behavior as you see fit.</p>
    <div>
      <h3>Using Workers as an API gateway</h3>
      <a href="#using-workers-as-an-api-gateway">
        
      </a>
    </div>
    <p>Workers provide programmable ingress logic from <a href="https://www.cloudflare.com/network/"><u>over 300 locations</u></a> around the world. In this sense, they provide similar functionality to an <a href="https://www.cloudflare.com/learning/security/api/what-is-an-api-gateway/">API gateway</a>.</p><p>For instance, let’s say you want to route requests to a different version of a container based on information in a header. This is accomplished in a few lines of code:</p>
            <pre><code>export default {
  async fetch(request, env) {
    const isExperimental = request.headers.get("x-version") === "experimental";
    
    if (isExperimental) {
      return env.MY_SERVICE_EXPERIMENTAL.fetch(request);
    } else {
      return env.MY_SERVICE_STANDARD.fetch(request);
    }
  },
};</code></pre>
            <p>Or you want to rate limit and authenticate requests to the container:</p>
            <pre><code>async fetch(request, env) {
  const url = new URL(request.url);

  if (url.pathname.startsWith('/api/')) {
    const token = request.headers.get("token");

    const isAuthenticated = await authenticateRequest(token);
    if (!isAuthenticated) {
      return new Response("Not authenticated", { status: 401 });
    }

    const { withinRateLimit } = await env.MY_RATE_LIMITER.limit({ key: token });
    if (!withinRateLimit) {
      return new Response("Rate limit exceeded for token", { status: 429 });
    }

    return env.MY_APP.fetch(request);
  }
  // ...
}</code></pre>
            
    <div>
      <h3>Using Workers as a service mesh</h3>
      <a href="#using-workers-as-a-service-mesh">
        
      </a>
    </div>
    
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/56gJk7HEDJs4dsNXDkBX1x/7c9d6be661d0cd6baea13caeedf478c0/2.png" />
          </figure><p>By default, Containers are private and can only be accessed via Workers, which can connect to one of many container ports. From within the container, you can expose a plain HTTP port, but requests will still be encrypted from the end user to the moment we send the data to the container’s TCP port in the host. Due to the communication being relayed through the Cloudflare network, the container does not need to set up <a href="https://www.cloudflare.com/application-services/products/ssl/">TLS certificates</a> to have secure connections in its open ports.</p><p>You can connect to the container through a WebSocket from the client too. See <a href="https://github.com/cloudflare/containers-demos/tree/main/websockets"><u>this repository</u></a> for a full example of using Websockets.</p><p>Just as the Durable Object can act as proxy <i>to the container</i>, it can act as a proxy <i>from the container </i>as well. When setting up a container, you can toggle Internet access off and ensure that outgoing requests pass through Workers.</p>
            <pre><code>// ... when starting the container...
this.ctx.container.start({ 
  workersAddress: '10.0.0.2:8080',
  enableInternet: false, // 'enableInternet' is false by default
});

// ... container requests to '10.0.0.2:8080' securely route to a different service...
override async onContainerRequest(request: Request) {
  const containerId = this.env.SUB_SERVICE.idFromName(request.headers['X-Account-Id']);
  return this.env.SUB_SERVICE.get(containerId).fetch(request);
}</code></pre>
            <p>You can ensure all traffic in and out of your container is secured and encrypted end to end without having to deal with networking yourself.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/9PzlxveRZ6NNBiNeZTiWs/c70e5db9cc7d63c02a21f21769801bc4/3.png" />
          </figure><p>This allows you to protect and connect containers within Cloudflare’s network… or even when connecting to <a href="https://blog.cloudflare.com/workers-virtual-private-cloud"><u>external private networks</u></a>.</p>
    <div>
      <h3>Using Workers as an orchestrator</h3>
      <a href="#using-workers-as-an-orchestrator">
        
      </a>
    </div>
    <p>You might require custom scheduling and scaling logic that goes beyond what Cloudflare provides out of the box.</p><p>We don’t want you having to manage complex chains of API calls or writing an <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/operator/"><u>operator</u></a> to get the logic you need. Just write some Worker code.</p><p>For instance, imagine your containers have a long startup period that involves loading data from an external source. You need to pre-warm containers manually, and need control over the specific region to prewarm. Additionally, you need to set up manual health checks that are accessible via Workers. You’re able to achieve this fairly simply with Workers and Durable Objects.</p>
            <pre><code>import { Container, DurableObject } from "cloudflare:workers";

// A singleton Durable Object to manage and scale containers

class ContainerManager extends DurableObject {
  scale(region, instanceCount) {
    for (let i = 0; i &lt; instanceCount; i++) {
      const containerId = env.CONTAINER.idFromName(`instance-${region}-${i}`);
      // spawns a new container with a location hint
      await env.CONTAINER.get(containerId, { locationHint: region }).start();
    }
  }

  async setHealthy(containerId, isHealthy) {
    await this.ctx.storage.put(containerId, isHealthy);
  }
}

// A Container class for the underlying compute

class MyContainer extends Container {
  defaultPort = 8080;

  async onContainerStart() {
    // run healthcheck every 500ms
    await this.scheduleEvery(0.5, 'healthcheck');
  }

  async healthcheck() {
    const manager = this.env.MANAGER.get(
      this.env.MANAGER.idFromName("manager")
    );
    const id = this.ctx.id.toString();

    await this.container.fetch("/_health")
      .then(() =&gt; manager.setHealthy(id, true))
      .catch(() =&gt; manager.setHealthy(id, false));
  }
}</code></pre>
            <p>The <code>ContainerManager </code>Durable Object exposes the <code>scale</code> RPC call, which you can call as needed with a <code>region</code> and <code>instanceCount</code> which scales up the number of active Container instances in a given region <a href="https://developers.cloudflare.com/durable-objects/reference/data-location/#provide-a-location-hint"><u>using a location hint</u></a>. The <code>this.schedule</code> code executes a manually defined <code>healthcheck</code> method on the Container and tracks its state in the Manager for use by other logic in your system.</p><p>These building blocks let users handle complex scheduling logic themselves. For a more detailed example using standard Durable Objects, see <a href="https://github.com/cloudflare/containers-demos/tree/main/load-balancer"><u>this repository</u></a>.</p><p>We are excited to see the patterns you come up with when orchestrating complex applications built with containers, and trust that between Workers and Durable Objects, you’ll have the tools you need.</p>
    <div>
      <h2>Integrating with more of Cloudflare’s Developer Platform</h2>
      <a href="#integrating-with-more-of-cloudflares-developer-platform">
        
      </a>
    </div>
    <p>Since it is <a href="https://blog.cloudflare.com/welcome-to-developer-week-2025/"><u>Developer Week 2025</u></a>, we would be remiss to not talk about <a href="https://developers.cloudflare.com/workflows/"><u>Workflows</u></a>, which <a href="https://blog.cloudflare.com/workflows-ga-production-ready-durable-execution/"><u>just went GA</u></a>, and <a href="https://agents.cloudflare.com/"><u>Agents</u></a>, which <a href="https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/"><u>just got even better</u></a>.</p><p>Let’s finish up by taking a quick look at how you can integrate Containers with these two tools.</p>
    <div>
      <h3>Running a short-lived job with Workflows &amp; R2</h3>
      <a href="#running-a-short-lived-job-with-workflows-r2">
        
      </a>
    </div>
    <p>You need to download a large file from <a href="https://developers.cloudflare.com/r2/"><u>R2</u></a>, compress it, and upload it. You want to ensure that this succeeds, but don’t want to write retry logic and error handling yourself. Additionally, you don’t want to deal with rotating R2 API tokens or worry about network connections — it should be secure by default.</p><p>This is a perfect opportunity for a <a href="https://developers.cloudflare.com/workflows/"><u>Workflow</u></a> using Containers. The container can do the heavy lifting of compressing files, Workers can stream the data to and from R2, and the Workflow can ensure durable execution.</p>
            <pre><code>export class EncoderWorkflow extends WorkflowEntrypoint&lt;Env, Params&gt; {
  async run(event: WorkflowEvent&lt;Params&gt;, step: WorkflowStep) {
    const id = this.env.ENCODER.idFromName(event.instanceId);
    const container = this.env.ENCODER.get(id);

    await step.do('init container', async () =&gt; {
      await container.init();
    });

    await step.do('compress the object with zstd', async () =&gt; {
      await container.ensureHealthy();
      const object = await this.env.ARTIFACTS.get(event.payload.r2Path);
      const result = await container.fetch('http://encoder/zstd', {
        method: 'POST', body: object.body 
      });
      await this.env.ARTIFACTS.put(`results${event.payload.r2Path}`, result.body);
    });

    await step.do('cleanup container', async () =&gt; {
      await container.destroy();
    });
  }
}</code></pre>
            
    <div>
      <h3>Calling a Container from an Agent</h3>
      <a href="#calling-a-container-from-an-agent">
        
      </a>
    </div>
    <p>Lastly, imagine you have an AI agent that needs to spin up cloud infrastructure (you like to live dangerously). To do this, you want to use <a href="https://github.com/hashicorp/terraform"><u>Terraform</u></a>, but since it’s run from the command line, you can’t run it on Workers.</p><p>By defining a <a href="https://developers.cloudflare.com/agents/concepts/tools/"><u>tool</u></a>, you can enable your Agent to run the shell commands from a container:</p>
            <pre><code>// Make tools that call to a container from an agent

const createExternalResources = tool({
  description: "runs Terraform in a container to create resources",
  parameters: z.object({ sessionId: z.number(), config: z.string() }),
  execute: async ({ sessionId, config }) =&gt; {
    return this.env.TERRAFORM_RUNNER.get(sessionId).applyConfig(config);
  },
});

// Expose RPC Methods that call to the container

class TerraformRunner extends DurableObject {
  async applyConfig(config) {
    await this.ctx.container.getTcpPort(8080).fetch(APPLY_URL, {
      method: 'POST',
      body: JSON.stringify({ config }),
    });
  }

  // ...rest of DO...
}</code></pre>
            <p>Containers are so much more powerful when combined with other tools. Workers make it easy to do so in a secure and simple way.</p>
    <div>
      <h2>Pay for what you use and use the right tool</h2>
      <a href="#pay-for-what-you-use-and-use-the-right-tool">
        
      </a>
    </div>
    <p>The deep integration between Workers and Containers also makes it easy to pick the right tool for the job with regards to cost.</p><p>With Cloudflare Containers, you only pay for what you use. Charges start when a request is sent to the container or it is manually started. Charges stop after the container goes to sleep, which can happen automatically after a configurable timeout. This makes it easy to scale to zero, and allows you to get high utilization even with highly-variable traffic.</p><p>Containers are billed for every 10ms that they are actively running at the following rates:</p><ul><li><p>Memory: $0.0000025 per GB-second</p></li><li><p>CPU: $0.000020 per vCPU-second</p></li><li><p>Disk $0.00000007 per GB-second
</p></li></ul><p>After 1 TB of free data transfer per month, egress from a Container will be priced per-region. We'll be working out the details between now and the beta, and will be launching with clear, transparent pricing across all dimensions so you know where you stand.</p><p>Workers are lighter weight than containers and <a href="https://blog.cloudflare.com/workers-pricing-scale-to-zero"><u>save you money by not charging when waiting on I/O</u></a>. This means that if you can, running on a Worker helps you save on cost. Luckily, on Cloudflare it is easy to route requests to the right tool.</p>
    <div>
      <h3>Cost comparison</h3>
      <a href="#cost-comparison">
        
      </a>
    </div>
    <p>Comparing containers and functions services on paper is always going to be an apples to oranges exercise, and results can vary so much depending on use case. But to share a real example of our own, a year ago when Cloudflare acquired Baselime, Baselime was a heavy user of AWS Lambda. By moving to Cloudflare, <a href="https://blog.cloudflare.com/80-percent-lower-cloud-cost-how-baselime-moved-from-aws-to-cloudflare/"><u>they lowered their cloud compute bill by 80%</u></a>.</p><p>Below we wanted to share one representative example that compares costs for an application that uses both containers and serverless functions together. It’d be easy for us to come up with a contrived example that uses containers sub-optimally on another platform, for the wrong types of workloads. We won’t do that here. We know that navigating cloud costs can be challenging, and that cost is a critical part of deciding what type of compute to use for which pieces of your application.</p><p>In the example below, we’ll compare Cloudflare Containers + Workers against Google Cloud Run, a very well-regarded container platform that we’ve been impressed by.</p>
    <div>
      <h4>Example application</h4>
      <a href="#example-application">
        
      </a>
    </div>
    <p>Imagine that you run an application that serves 50 million requests per month, and each request consumes an average 500 ms of wall-time. Requests to this application are not all the same though — half the requests require a container, and the other half can be served just using serverless functions.</p><table><tr><td><p>Requests per month</p></td><td><p>Wall-time (duration)</p></td><td><p>Compute required</p></td><td><p>Cloudflare</p></td><td><p>Google Cloud</p></td></tr><tr><td><p>25 million</p></td><td><p>500ms</p></td><td><p>Container + serverless functions</p></td><td><p>Containers + Workers</p></td><td><p>Google Cloud Run + Google Cloud Run Functions</p></td></tr><tr><td><p>25 million</p></td><td><p>500ms</p></td><td><p>Serverless functions</p></td><td><p>Workers</p></td><td><p>Google Cloud Run Functions</p></td></tr></table>
    <div>
      <h4>Container pricing</h4>
      <a href="#container-pricing">
        
      </a>
    </div>
    <p>On both Cloud Run and Cloudflare Containers, a container can serve multiple requests. On some platforms, such as AWS Lambda, each container instance is limited to a single request, pushing cost up significantly as request count grows. In this scenario, 50 requests can run simultaneously on a container with 4 GB memory and half of a vCPU. This means that to serve 25 million requests of 500ms each, we need 625,000 seconds worth of compute</p><p>In this example, traffic is bursty and we want to avoid paying for idle-time, so we’ll use Cloud Run’s request-based pricing.</p><table><tr><td><p>
</p></td><td><p>Price per vCPU second</p></td><td><p>Price per GB-second of memory</p></td><td><p>Price per 1m requests</p></td><td><p>Monthly Price for Compute + Requests</p></td></tr><tr><td><p>Cloudflare Containers</p></td><td><p>$0.000020</p></td><td><p>$0.0000025</p></td><td><p>$0.30</p></td><td><p>$20.00</p></td></tr><tr><td><p>Google Cloud Run</p></td><td><p>$0.000024</p></td><td><p>$0.0000025</p></td><td><p>$0.40</p></td><td><p>$23.75</p></td></tr></table><p><sup><i>* Comparison does not include free tiers for either provider and uses a single Tier 1 GCP region</i></sup></p><p>Compute pricing for both platforms are comparable. But as we showed earlier in this post, Containers on Cloudflare run anywhere, on-demand, without configuring and managing regions. Each container has a programmable sidecar with its own database, backed by Durable Objects. It’s the depth of integration with the rest of the platform that makes containers on Cloudflare uniquely programmable.</p>
    <div>
      <h4>Function pricing</h4>
      <a href="#function-pricing">
        
      </a>
    </div>
    <p>The other requests can be served with less compute, and code written in <a href="https://developers.cloudflare.com/workers/languages/javascript/"><u>JavaScript</u></a>, <a href="https://developers.cloudflare.com/workers/languages/typescript/"><u>TypeScript</u></a>, <a href="https://developers.cloudflare.com/workers/languages/python/"><u>Python</u></a> or <a href="https://developers.cloudflare.com/workers/languages/rust/"><u>Rust</u></a>, so we’ll use Workers and Cloud Run Functions.</p><p>These 25 million requests also run for 500 ms each, and each request spends 480 ms waiting on I/O. This means that Workers will <a href="https://blog.cloudflare.com/workers-pricing-scale-to-zero/"><u>only charge for 20 ms of “CPU-time”</u></a>, the time that the Worker actually spends using compute. This ratio of low CPU time to high wall time is extremely common when building AI apps that make inference requests, or even when just building REST APIs and other business logic. Most time is spent waiting on I/O. Based on our data, we typically see Workers use less than 5 ms of CPU time per request vs seconds of wall time (waiting on APIs or I/O).</p><p>The Cloud Run Function will use an instance with 0.083 vCPU and 128 MB memory and charge on both CPU-s and GiB-s for the full 500 ms of wall-time.</p><table><tr><td><p>
</p></td><td><p>Total Price for “wall-time”</p></td><td><p>Total Price for “CPU-time”</p></td><td><p>Total Price for Compute + Requests</p></td></tr><tr><td><p>Cloudflare Workers</p></td><td><p>N/A</p></td><td><p>$0.83</p></td><td><p>$8.33</p></td></tr><tr><td><p>Google Cloud Run Functions</p></td><td><p>$1.44</p></td><td><p>N/A</p></td><td><p>$11.44</p></td></tr></table><p><sup><i>* Comparison does not include free tiers and uses a single Tier 1 GCP region.</i></sup></p><p>This comparison assumes you have configured Google Cloud Run Functions with a max of 20 concurrent requests per instance. On Google Cloud Run Functions, the maximum number of concurrent requests an instance can handle varies based on the efficiency of your function, and your own tolerance for tail latency that can be introduced by traffic spikes. </p><p>Workers automatically scale horizontally, don’t require you to configure concurrency settings (and hope to get it right), and can run in <a href="https://www.cloudflare.com/en-gb/network/"><u>over 300 locations</u></a>.</p>
    <div>
      <h4>A holistic view of costs</h4>
      <a href="#a-holistic-view-of-costs">
        
      </a>
    </div>
    <p>The most important cost metric is the total cost of developing and running an application. And the only way to get the best results is to use the right compute for the job. So the question boils down to friction and integration. How easily can you integrate the ideal building blocks together?</p><p>As more and more software makes use of <a href="https://www.cloudflare.com/learning/ai/what-is-generative-ai/">generative AI</a>, and makes inference requests to LLMs, modern applications must communicate and integrate with a myriad of services. Most systems are increasingly real-time and chatty, often holding open long-lived connections, performing tasks in parallel. Running an instance of an application in a VM or container and calling it a day might have worked 10 years ago, but when we talk to developers in 2025, they are most often bringing many forms of compute to the table for particular use cases.</p><p>This shows the importance of picking a platform where you can seamlessly shift traffic from one source of compute to another. If you want to <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/"><u>rate-limit</u></a>, <a href="https://blog.cloudflare.com/full-stack-development-on-cloudflare-workers/"><u>serve server-side rendered pages, API responses and static assets</u></a>, handle authentication and authorization, make<a href="https://developers.cloudflare.com/workers-ai/"><u> inference requests to AI models</u></a>, run core business logic via <a href="https://blog.cloudflare.com/workflows-ga-production-ready-durable-execution/"><u>Workflows</u></a>, or <a href="https://blog.cloudflare.com/cloudflare-acquires-arroyo-pipelines-streaming-ingestion-beta/"><u>ingest streaming data</u></a>, just handle the request in Workers. Save the heavier compute only for where it is actually the only option. With Cloudflare Workers and Containers, this is as simple as an if-else statement in your Worker. This makes it easy to pick the right tool for the job.</p>
    <div>
      <h2>Coming June 2025</h2>
      <a href="#coming-june-2025">
        
      </a>
    </div>
    <p>We are collecting feedback and putting the finishing touches on our APIs now, and will release the open beta to the public in late June 2025.</p><p>From day one of building Cloudflare Workers, it’s been our goal to build an integrated platform, where Cloudflare products work together as a system, rather than just as a collection of separate products. We’ve taken this same approach with Containers, and aim to make Cloudflare not only the best place to deploy containers across the globe, but the best place to deploy the types of complete applications that developers are building, that use containers in tandem with serverless functions, <a href="http://developers.cloudflare.com/workflows"><u>Workflows</u></a>, <a href="https://agents.cloudflare.com/"><u>Agents</u></a>, and <a href="https://developers.cloudflare.com/"><u>much more</u></a>.</p><p>We’re excited to get this into your hands soon. Stay on the lookout this summer.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6tb0K0eJoK6L4QrUCTWISx/4346edb7593b47768cebcb48369cfa35/4.png" />
          </figure><p></p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Containers]]></category>
            <guid isPermaLink="false">7yspClA37lzZVogRwQzn5F</guid>
            <dc:creator>Mike Nomitch</dc:creator>
            <dc:creator>Gabi Villalonga Simón</dc:creator>
        </item>
        <item>
            <title><![CDATA[Our container platform is in production. It has GPUs. Here’s an early look]]></title>
            <link>https://blog.cloudflare.com/container-platform-preview/</link>
            <pubDate>Fri, 27 Sep 2024 13:00:00 GMT</pubDate>
            <description><![CDATA[ We’ve been working on something new — a platform for running containers across Cloudflare’s network. We already use it in production, for AI inference and more. Today we want to share an early look at ]]></description>
            <content:encoded><![CDATA[ <p>We’ve been working on something new — a platform for running containers across Cloudflare’s network. We already use it in production for <a href="https://developers.cloudflare.com/workers-ai/"><u>Workers AI</u></a>, <a href="https://developers.cloudflare.com/workers/ci-cd/"><u>Workers Builds</u></a>, <a href="https://www.cloudflare.com/zero-trust/products/browser-isolation/"><u>Remote Browsing Isolation</u></a>, and the <a href="https://developers.cloudflare.com/browser-rendering/"><u>Browser Rendering API</u></a>. Today, we want to share an early look at how it’s built, why we built it, and how we use it ourselves.</p><p>In 2024, Cloudflare Workers celebrates its 7th birthday. <a href="https://blog.cloudflare.com/introducing-cloudflare-workers/"><u>When we first announced Workers</u></a>, it was a completely new model for running compute in a multi-tenant way — on isolates, as opposed to containers. While, at the time, Workers was a pretty bare-bones functions-as-a-service product, we took a big bet that this was going to become the way software was going to be written going forward. Since introducing Workers, in addition to expanding our developer products in general to include storage and AI, we have been steadily adding more compute capabilities to Workers:</p><table><tr><td><p><b>2020</b></p></td><td><p><a href="https://blog.cloudflare.com/introducing-cron-triggers-for-cloudflare-workers/"><u>Cron Triggers</u></a></p></td></tr><tr><td><p><b>2021</b></p></td><td><p><a href="https://blog.cloudflare.com/durable-objects-easy-fast-correct-choose-three/"><u>Durable Objects</u></a></p><p><a href="https://blog.cloudflare.com/workers-rust-sdk/"><u>Write Workers in Rust</u></a></p><p><a href="https://blog.cloudflare.com/introducing-worker-services/"><u>Service Bindings</u></a></p></td></tr><tr><td><p><b>2022</b></p></td><td><p><a href="https://blog.cloudflare.com/introducing-cloudflare-queues/"><u>Queues</u></a></p><p><a href="https://blog.cloudflare.com/announcing-route-to-workers/"><u>Email Workers</u></a></p><p><a href="https://blog.cloudflare.com/durable-objects-alarms/"><u>Durable Objects Alarms</u></a></p></td></tr><tr><td><p><b>2023</b></p></td><td><p><a href="https://blog.cloudflare.com/workers-tcp-socket-api-connect-databases/"><u>Workers TCP Socket API</u></a> </p><p><a href="https://blog.cloudflare.com/hyperdrive-making-regional-databases-feel-distributed/"><u>Hyperdrive</u></a></p><p><a href="https://blog.cloudflare.com/announcing-workers-smart-placement/"><u>Smart Placement</u></a></p><p><a href="https://blog.cloudflare.com/best-place-region-earth-inference/"><u>Workers AI</u></a></p></td></tr><tr><td><p><b>2024</b></p></td><td><p><a href="https://blog.cloudflare.com/python-workers/"><u>Python Workers</u></a></p><p><a href="https://blog.cloudflare.com/javascript-native-rpc/"><u>JavaScript-native RPC</u></a></p><p><a href="https://blog.cloudflare.com/more-npm-packages-on-cloudflare-workers-combining-polyfills-and-native-code/"><u>Node.js compatibility</u></a></p><p><a href="https://blog.cloudflare.com/sqlite-in-durable-objects"><u>SQLite in Durable Objects</u></a></p></td></tr></table><p>With each of these, we’ve faced a question — can we build this natively into the platform, in a way that removes, rather than adds complexity? Can we build it in a way that lets developers focus on building and shipping, rather than managing infrastructure, so that they don’t have to be a distributed systems engineer to build distributed systems?</p><p>In each instance, the answer has been YES. We try to solve problems in a way that simplifies things for developers in the long run, even if that is the harder path for us to take ourselves. If we didn’t, you’d be right to ask — why not self-host and manage all of this myself? What’s the point of the cloud if I’m still provisioning and managing infrastructure? These are the questions many are asking today about the earlier generation of cloud providers.</p><p>Pushing ourselves to build platform-native products and features helped us answer this question. Particularly because some of these actually use containers behind the scenes, even though as a developer you never interact with or think about containers yourself.</p><p>If you’ve used AI inference on GPUs with <a href="https://developers.cloudflare.com/workers-ai/"><u>Workers AI</u></a>, spun up headless browsers with <a href="https://developers.cloudflare.com/browser-rendering/"><u>Browser Rendering</u></a>, or enqueued build jobs with the new <a href="https://developers.cloudflare.com/workers/ci-cd/"><u>Workers Builds</u></a>, you’ve run containers on our network, without even knowing it. But to do so, we needed to be able to run untrusted code across Cloudflare’s network, outside a <a href="https://developers.cloudflare.com/workers/reference/how-workers-works/#isolates"><u>v8 isolate</u></a>, in a way that fits what we promise:</p><ol><li><p>You shouldn’t have to think about regions or data centers. Routing, scaling, load balancing, scheduling, and capacity are our problem to solve, not yours, with tools like <a href="https://blog.cloudflare.com/announcing-workers-smart-placement/"><u>Smart Placement</u></a>.</p></li><li><p>You should be able to build distributed systems without being a distributed systems engineer.</p></li><li><p>Every millisecond matters — Cloudflare has to be fast.</p></li></ol><p>There wasn’t an off-the-shelf container platform that solved for what we needed, so we built it ourselves — from scheduling to IP address management, pulling and caching images, to improving startup times and more. Our container platform powers many of our newest products, so we wanted to share how we built it, optimized it, and well, you can probably guess what’s next.</p>
    <div>
      <h2>Global scheduling — “The Network is the Computer”</h2>
      <a href="#global-scheduling-the-network-is-the-computer">
        
      </a>
    </div>
    <p>Cloudflare serves the entire world — region: earth. Rather than asking developers to provision resources in specific regions, data centers and availability zones, we think <a href="https://blog.cloudflare.com/the-network-is-the-computer/"><u>“The Network is the Computer”</u></a>. When you build on Cloudflare, you build software that runs on the Internet, not just in a data center.</p><p>When we started working on this, Cloudflare’s architecture was to just run every service via <a href="https://systemd.io/"><u>systemd</u></a> on every server (we call them “metals” — we <a href="https://blog.cloudflare.com/gen-12-servers"><u>run our own hardware</u></a>), allowing all services to take advantage of new capacity we add to our network. That fits running <a href="https://blog.cloudflare.com/upgrading-one-of-the-oldest-components-in-cloudflare-software-stack/"><u>NGINX</u></a> and a few dozen other services, but cannot fit a world where we need to run <i>many thousands</i> of different compute heavy, resource hungry workloads. We’d <a href="https://blog.cloudflare.com/its-crowded-in-here/"><u>run out of space</u></a> just trying to load all of them! Consider a canonical AI workload — deploying <a href="https://blog.cloudflare.com/meta-llama-3-1-available-on-workers-ai/"><u>Llama 3.1 8B</u></a> to an inference server. If we simply ran a Llama 3.1 8B service on every Cloudflare metal, we’d have no flexibility to use GPUs for the <a href="https://developers.cloudflare.com/workers-ai/models/"><u>many other models</u></a> that Workers AI supports.</p><p>We needed something that would allow us to still take advantage of the full capacity of Cloudflare’s entire network, not just the capacity of individual machines. And ideally not put that burden on the developer.</p><p>The answer: we built a control plane on our own Developer Platform that lets us schedule a container anywhere on <a href="https://www.cloudflare.com/en-gb/network/"><u>Cloudflare’s Network</u></a>:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/63nPDnWzVLvx2OB9ZPZXM5/d08be3dd36fbd08bb7205a82b20bf90e/BLOG-2573_2.png" />
          </figure><p>The global scheduler is built on Cloudflare <a href="https://developers.cloudflare.com/workers/"><u>Workers</u></a>, <a href="https://developers.cloudflare.com/durable-objects/"><u>Durable Objects</u></a>, and <a href="https://developers.cloudflare.com/kv/"><u>KV</u></a>, and decides which Cloudflare location to schedule the container to run in. Each location then runs <i>its own</i> scheduler, which decides which metals within that location to schedule the container to run on. Location schedulers monitor compute capacity, and expose this to the global scheduler. This allows Cloudflare to dynamically place workloads based on capacity and hardware availability (e.g. multiple types of GPUs) across our network.</p>
    <div>
      <h2>Why does global scheduling matter?</h2>
      <a href="#why-does-global-scheduling-matter">
        
      </a>
    </div>
    <p>When you run compute on a first generation cloud, the “contract” between the developer and the platform is that the developer must specify what runs where. This is regional scheduling, the status quo.</p><p>Let’s imagine for a second if we applied regional scheduling to running compute on Cloudflare’s network, with locations in <a href="https://www.cloudflare.com/en-gb/network/"><u>330+ cities, across 120+ countries</u></a>. One of the obvious reasons people tell us they want to run on Cloudflare is because we have compute in places where others don’t, within 50ms of 95% of the world’s Internet-connected population. In South America, other clouds have one region in one city. Cloudflare has 19:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/53Ba51lqKDRNQPbEBselC3/0b9cd69b4f9fa29dad355df0076d05b9/BLOG-2573_3.png" />
          </figure><p>Running anywhere means you can be faster, highly available, and have more control over data location. But with regional scheduling, the more locations you run in, the more work you have to do. You configure and manage load balancing, routing, auto-scaling policies and more. Balancing performance and cost in a multi-region setup is literally a full-time job (or more) at most companies who have reached meaningful scale on traditional clouds.</p><p>But most importantly, no matter what tools you bring, you were the one who told the cloud provider, “run this container over here”. The cloud platform can’t move it for you, even if moving it would make your workload faster. This prevents the platform from adding locations, because for each location, it has to convince developers to take action themselves to move their compute workloads to the new location. Each new location carries a risk that developers won’t migrate workloads to it, or migrate too slowly, delaying the return on investment.</p><p>Global scheduling means Cloudflare can add capacity and use it immediately, letting you benefit from it. The “contract” between us and our customers isn’t tied to a specific datacenter or region, so we have permission to move workloads around to benefit customers. This flexibility plays an essential role in all of our own uses of our container platform, starting with GPUs and AI.</p>
    <div>
      <h2>GPUs everywhere: Scheduling large images with Workers AI</h2>
      <a href="#gpus-everywhere-scheduling-large-images-with-workers-ai">
        
      </a>
    </div>
    <p>In late 2023, <a href="https://blog.cloudflare.com/workers-ai/"><u>we launched Workers AI</u></a>, which provides fast, easy to use, and affordable GPU-backed AI inference.</p><p>The more efficiently we can use our capacity, the better pricing we can offer. And the faster we can make changes to which models run in which Cloudflare locations, the closer we can move AI inference to the application, <a href="https://blog.cloudflare.com/making-workers-ai-faster/"><u>lowering Time to First Token (TTFT)</u></a>. This also allows us to be more resilient to spikes in inference requests.</p><p>AI models that rely on GPUs present three challenges though:</p><ol><li><p>Models have different GPU memory needs. GPU memory is the most scarce resource, and different GPUs have different amounts of memory.</p></li><li><p>Not all container runtimes, such as <a href="https://github.com/firecracker-microvm/firecracker/issues/1179"><u>Firecracker</u></a>, support GPU drivers and other dependencies.</p></li><li><p>AI models, particularly LLMs, are very large. Even a smaller parameter model, like <a href="https://developers.cloudflare.com/workers-ai/models/llama-3.1-8b-instruct/"><u>@cf/meta/llama-3.1-8b-instruct</u></a>, is at least 5 GB. The larger the model, the more bytes we need to pull across the network when scheduling a model to run in a new location.</p></li></ol><p>Let’s dive into how we solved each of these…</p><p>First, GPU memory needs. The global scheduler knows which Cloudflare locations have blocks of GPU memory available, and then delegates scheduling the workload on a specific metal to the local scheduler. This allows us to prioritize placement of AI models that use a large amount of GPU memory, and then move smaller models to other machines in the same location. By doing this, we maximize the overall number of locations that we run AI models in, and maximize our efficiency.</p><p>Second, container runtimes and GPU support. Thankfully, from day one we built our container platform to be <i>runtime agnostic</i>. Using <a href="https://blog.cloudflare.com/how-we-use-hashicorp-nomad/"><u>a runtime agnostic scheduler</u></a>, we’re able to support <a href="https://gvisor.dev/"><u>gVisor</u></a>, <a href="https://firecracker-microvm.github.io/"><u>Firecracker</u></a> microVMs, and traditional VMs with <a href="https://www.qemu.org/"><u>QEMU</u></a>. We are also evaluating adding support for another one: <a href="https://github.com/cloud-hypervisor/cloud-hypervisor"><u>cloud-hypervisor</u></a> which is based on <a href="https://github.com/rust-vmm"><u>rust-vmm</u></a> and has a few compelling advantages for our use case:</p><ol><li><p>GPU passthrough <a href="https://github.com/cloud-hypervisor/cloud-hypervisor/blob/main/docs/vfio.md"><u>support</u></a> using VFIO</p></li><li><p><a href="https://github.com/cloud-hypervisor/cloud-hypervisor/blob/main/docs/device_model.md#vhost-user-net"><u>vhost-user-net</u></a> support, enabling high throughput between the host network interface and the VM</p></li><li><p><a href="https://github.com/cloud-hypervisor/cloud-hypervisor/blob/main/docs/device_model.md#vhost-user-net"><u>vhost-user-blk</u></a> support, adding flexibility to introduce novel network-based storage backed by other Cloudflare Workers products</p></li><li><p>all the while being a smaller codebase than QEMU and written in a memory-safe language</p></li></ol><p>Our goal isn’t to build a platform that makes you as the developer choose between runtimes, and ask, “should I use Firecracker or gVisor”. We needed this flexibility in order to be able to run workloads with different needs efficiently, including workloads that depend on GPUs. gVisor has <a href="https://gvisor.dev/docs/user_guide/gpu/"><u>GPU support</u></a>, while Firecracker microVMs currently <a href="https://github.com/firecracker-microvm/firecracker/issues/1179"><u>does not</u></a>.</p><p>gVisor’s main component is an application kernel (called Sentry) that implements a Linux-like interface but is written in a memory-safe language (Go) and runs in userspace. It works by intercepting application system calls and acting as the guest kernel, without the need for translation through virtualized hardware.</p><p>The resource footprint of a containerized application running on gVisor is lower than a VM because it does not require managing virtualized hardware and booting up a kernel instance. However, this comes at the price of reduced application compatibility and higher per-system call overhead.</p><p>To add GPU support, the Google team introduced <a href="https://gvisor.dev/docs/user_guide/gpu/"><u>nvproxy</u></a> which works using the same principles as described above for syscalls: it intercepts <a href="https://en.wikipedia.org/wiki/Ioctl"><u>ioctls</u></a> destined to the GPU and proxies a subset to the GPU kernel module.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1jqt4l5joKUkPNek1xACaM/64df1f3dcab2cae7d9ecf05deb407242/image__19_.png" />
          </figure><p>To solve the third challenge, and make scheduling fast with large models, we weren’t satisfied with the status quo. So we did something about it.</p>
    <div>
      <h2>Docker pull was too slow, so we fixed it (and cut the time in half)</h2>
      <a href="#docker-pull-was-too-slow-so-we-fixed-it-and-cut-the-time-in-half">
        
      </a>
    </div>
    <p>Many of the images we need to run for AI inference are over 15 GB. Specialized inference libraries and GPU drivers add up fast. For example, when we make a scheduling decision to run a fresh container in Tokyo, naively running docker pull to fetch the image from a storage bucket in Los Angeles would be unacceptably slow. And scheduling speed is critical to being able to scale up and down in new locations in response to changes in traffic.</p><p>We had 3 essential requirements:</p><ul><li><p>Pulling and pushing very large images should be fast</p></li><li><p>We should not rely on a single point of failure</p></li><li><p>Our teams shouldn’t waste time managing image registries</p></li></ul><p>We needed globally distributed storage, so we used <a href="https://developers.cloudflare.com/r2/"><u>R2</u></a>. We needed the highest cache hit rate possible, so we used Cloudflare’s <a href="https://developers.cloudflare.com/cache/"><u>Cache</u></a>, and will soon use <a href="https://developers.cloudflare.com/cache/how-to/tiered-cache/"><u>Tiered Cache</u></a>. And we needed a fast container image registry that we could run everywhere, in every Cloudflare location, so we built and open-sourced <a href="https://github.com/cloudflare/serverless-registry"><u>serverless-registry</u></a>, which is built on <a href="https://developers.cloudflare.com/workers/"><u>Workers</u></a>. You can deploy serverless-registry to your own Cloudflare account in about 5 minutes. We rely on it in production.</p><p>This is fast, but we can be faster. Our performance bottleneck was, somewhat surprisingly, <a href="https://docs.docker.com/reference/cli/docker/image/push/"><u>docker push</u></a>. Docker uses <a href="https://www.gzip.org/"><u>gzip</u></a> to compress and decompress layers of images while pushing and pulling. So we started using <a href="https://github.com/facebook/zstd"><u>Zstandard</u></a> (zstd) instead, which compresses and decompresses faster, and results in smaller compressed files.</p><p>In order to build, chunk, and push these images to the R2 registry, we built a custom CLI tool that we use internally in lieu of running docker build and docker push. This makes it easy to use zstd and split layers into 500 MB chunks, which allows uploads to be processed by Workers while staying under <a href="https://developers.cloudflare.com/workers/platform/limits/#request-limits"><u>body size limits</u></a>.</p><p>Using our custom build and push tool doubled the speed of image pulls. Our 30 GB GPU images now pull in 4 minutes instead of 8. We plan on open sourcing this tool in the near future.</p>
    <div>
      <h2>Anycast is the gift that keeps on simplifying — Virtual IPs and the Global State Router</h2>
      <a href="#anycast-is-the-gift-that-keeps-on-simplifying-virtual-ips-and-the-global-state-router">
        
      </a>
    </div>
    <p>We still had another challenge to solve. And yes, we solved it with anycast. We’re Cloudflare, did you expect anything else?</p><p>First, a refresher — Cloudflare operates <a href="https://blog.cloudflare.com/unimog-cloudflares-edge-load-balancer/"><u>Unimog</u></a>, a Layer 4 load balancer that handles all incoming Cloudflare traffic. Cloudflare’s network uses <a href="https://www.cloudflare.com/learning/cdn/glossary/anycast-network"><u>anycast</u></a>, which allows a single IP address to route requests to a variety of different locations. For most Cloudflare services with anycast, the given IP address will route to the nearest Cloudflare data center, reducing latency. Since Cloudflare runs almost every service in every data center, Unimog can simply route traffic to any Cloudflare metal that is online and has capacity, without needing to map traffic to a specific service that runs on specific metals, only in some locations.</p><p>The new compute-heavy, GPU-backed workloads we were taking on forced us to confront this fundamental “everything runs everywhere” assumption. If we run a containerized workflow in 20 Cloudflare locations, how does Unimog know which locations, and which metals, it runs in? You might say “just bring your own <a href="https://developers.cloudflare.com/reference-architecture/architectures/load-balancing/"><u>load balancer</u></a>” — but then what happens when you make scheduling decisions to migrate a workload between locations, scale up, or scale down?</p><p>Anycast is foundational to how we build fast and simple products on our network, and we needed a way to keep building new types of products this way — where a team can <a href="https://www.cloudflare.com/developer-platform/solutions/hosting/">deploy an application</a>, get back a single IP address, and rely on the platform to balance traffic, taking load, container health, and latency into account, without extra configuration. We started letting teams use the container platform without solving this, and it was painfully clear that we needed to do something about it.</p><p>So we started integrating directly into our networking stack, building a sidecar service to Unimog. We’ll call this the Global State Router. Here’s how it works:</p><ul><li><p>An eyeball makes a request to a virtual IP address issued by Cloudflare</p></li><li><p>Request sent to the best location as determined by BGP routing. This is anycast routing.</p></li><li><p>A small <a href="https://ebpf.io/what-is-ebpf/"><u>eBPF</u></a> program sits on the main networking interface and ensures packets bound to a virtual IP address are handled by the Global State Router.</p></li><li><p>The main Global State Router program contains a mapping of all anycast IPs addresses to potential end destination container IP addresses. It updates this mapping based on container health, readiness, distance, and latency. Using this information, it picks a best-fit container.</p></li><li><p>Packets are forwarded at the L4 layer.</p></li><li><p>When a target container’s server receives a packet, its own Global State Router program intercepts the packet and routes it to the local container.</p></li></ul>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/8ZICyfggbTEpkxki0OnYd/13a427a65b1e9d0c5d591468e3f10612/BLOG-2573_5.png" />
          </figure><p>This might sound like just a lower level networking detail, disconnected from developer experience. But by integrating directly with Unimog, we can let developers:</p><ol><li><p>Push a containerized application to Cloudflare.</p></li><li><p>Provide constraints, health checks, and load metrics that describe what the application needs.</p></li><li><p>Delegate scheduling and scaling many containers across Cloudflare’s network.</p></li><li><p>Get back a single IP address that can be used everywhere.</p></li></ol><p>We’re actively working on this, and are excited to continue building on Cloudflare’s anycast capabilities, and pushing to keep the simplicity of running “everywhere” with new categories of workloads.</p>
    <div>
      <h2>Low latency &amp; global — Remote Browser Isolation &amp; Browser Rendering</h2>
      <a href="#low-latency-global-remote-browser-isolation-browser-rendering">
        
      </a>
    </div>
    <p>Our container platform actually started because of a very specific challenge, running <a href="https://www.cloudflare.com/zero-trust/products/browser-isolation/"><u>Remote Browser Isolation</u></a> across our network. Remote Browser Isolation provides Chromium browsers that run on Cloudflare, in containers, rather than on the end user’s own computer. Only the rendered output is sent to the end user. This provides a layer of protection against zero-day browser vulnerabilities, phishing attacks, and ransomware.</p><p>Location is critical — people expect their interactions with a remote browser to feel just as fast as if it ran locally. If the server is thousands of miles away, the remote browser will feel slow. Running across <a href="https://www.cloudflare.com/network/"><u>Cloudflare’s network of over 330 locations</u></a> means the browser is nearly always as close to you as possible.</p><p>Imagine a user in Santiago, Chile, if they were to access a browser running in the same city, each interaction would incur negligible additional latency. Whereas a browser in Buenos Aires might add 21 ms, São Paulo might add 48 ms, Bogota might add 67 ms, and Raleigh, NC might add 128 ms. Where the container runs significantly impacts the latency of every user interaction with the browser, and therefore the experience as a whole.</p><p>It’s not just browser isolation that benefits from running near the user: WebRTC servers stream video better, multiplayer games have less lag, online advertisements can be served faster, financial transactions can be processed faster. Our container platform lets us run anything we need to near the user, no matter where they are in the world.</p>
    <div>
      <h2>Using spare compute — “off-peak” jobs for Workers CI/CD builds</h2>
      <a href="#using-spare-compute-off-peak-jobs-for-workers-ci-cd-builds">
        
      </a>
    </div>
    <p>At any hour of the day, Cloudflare has many CPU cores that sit idle. This is compute power that could be used for something else.</p><p>Via anycast, most of Cloudflare’s traffic is handled as close as possible to the eyeball (person) that requested it. Most of our traffic originates from eyeballs. And the eyeballs of (most) people are closed and asleep between midnight and 5:00 AM local time. While we use our compute capacity very efficiently during the daytime in any part of the world, overnight we have spare cycles. Consider what a map of the world looks like at nighttime in Europe and Africa:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/CtkNFxhvusjLx1khXh9gs/25b5a6579d8abd47bcaa15ba28eddc61/BLOG-2573_6.png" />
          </figure><p>As shown above, we can run containers during “off-peak” in Cloudflare locations receiving low traffic at night. During this time, the CPU utilization of a typical Cloudflare metal looks something like this:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6sXsQ3p19bFecokbR0X1yj/04419589e4f6796fe297435cd1029b5a/Screenshot_2024-09-27_at_2.20.50_PM.png" />
          </figure><p>We have many “background” compute workloads at Cloudflare. These are workloads that don’t actually need to run close to the eyeball because there is no eyeball waiting on the request. The challenge is that many of these workloads require running untrusted code — either a dependency on open-source code that we don’t trust enough to run outside of a sandboxed environment, or untrusted code that customers deploy themselves. And unlike <a href="https://developers.cloudflare.com/workers/configuration/cron-triggers/"><u>Cron Triggers</u></a>, which already make a best-effort attempt to use off-peak compute, these other workloads can’t run in <a href="https://developers.cloudflare.com/workers/reference/how-workers-works/#isolates"><u>v8 isolates</u></a>.</p><p>On Builder Day 2024, we <a href="https://blog.cloudflare.com/builder-day-2024-announcements"><u>announced Workers Builds in open beta</u></a>. You connect your Worker to a git repository, and Cloudflare builds and deploys your Worker each time you merge a pull request. Workers Builds run on our containers platform, using otherwise idle “off-peak” compute, allowing us to offer lower pricing, and hold more capacity for unexpected spikes in traffic in Cloudflare locations during daytime hours when load is highest. We preserve our ability to serve requests as close to the eyeball as possible where it matters, while using the full compute capacity of our network.</p><p>We developed a purpose-built API for these types of jobs. The Workers Builds service has zero knowledge of where Cloudflare has spare compute capacity on its network — it simply schedules an “off-peak” job to run on the containers platform, by defining a scheduling policy:</p><p><code>scheduling_policy: "off-peak"</code></p>
    <div>
      <h2>Making off-peak jobs faster with prewarmed images</h2>
      <a href="#making-off-peak-jobs-faster-with-prewarmed-images">
        
      </a>
    </div>
    <p>Just because a workload isn’t “eyeball-facing” doesn’t mean speed isn’t relevant. When a build job starts, you still want it to start as soon as possible.</p><p>Each new build requires a fresh container though, and we must avoid reusing containers to provide strong isolation between customers. How can we keep build job start times low, while using a new container for each job without over-provisioning? </p><p>We prewarm servers with the proper image. </p><p>Before a server becomes eligible to receive an “off peak” job, the container platform instructs it to download the correct image. Once the image is downloaded and cached locally, new containers can start quickly in a Firecracker VM after receiving a request for a new build. When a build completes, we throw away the container, and start the next build using a fresh container based on the prewarmed image.</p><p>Without prewarming, pulling and unpacking our Workers Build images would take roughly 75 seconds. With prewarming, we’re able to spin up a new container in under 10 seconds. We expect this to get even faster as we introduce optimizations like pre-booting images before new runs, or <a href="https://github.com/firecracker-microvm/firecracker/blob/main/docs/snapshotting/snapshot-support.md"><u>Firecracker snapshotting</u></a>, which can restore a VM in under 200ms.</p>
    <div>
      <h2>Workers and containers, better together</h2>
      <a href="#workers-and-containers-better-together">
        
      </a>
    </div>
    <p>As more of our own engineering teams rely on our containers platform in production, we’ve noticed a pattern: they want a deeper integration with Workers.</p><p>We plan to give it to them. </p><p>Let’s take a look at a project deployed on our container platform already, <a href="https://blog.cloudflare.com/key-transparency/"><u>Key Transparency</u></a>. If the container platform were highly integrated with Workers, what would this team’s experience look like?</p><p>Cloudflare regularly audits changes to public keys used by WhatsApp for encrypting messages between users. Much of the architecture is built on Workers, but there are long-running compute-intensive tasks that are better suited for containers.</p><p>We don’t want our teams to have to jump through hoops to deploy a container and integrate with Workers. They shouldn’t have to pick specific regions to run in, figure out scaling, expose IPs and handle IP updates, or set up Worker-to-container auth.</p><p>We’re still exploring many different ideas and API designs, and we want your feedback. But let’s imagine what it might look like to use Workers, Durable Objects and Containers together.</p><p>In this case, an outer layer of Workers handles most business logic and ingress, a specialized Durable Object is configured to run alongside our new container, and the platform ensures the image is loaded on the right metals and can scale to meet demand.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5GsXRuYvVELJY1syOmfq3z/47d3481116670de291be86438777fbc0/BLOG-2573_8.png" />
          </figure><p>I add a containerized app to the <a href="https://developers.cloudflare.com/workers/wrangler/configuration/"><code><u>wrangler.toml </u></code><u>configuration file</u></a> of my Worker (or <a href="https://blog.cloudflare.com/automatically-generating-cloudflares-terraform-provider"><u>Terraform</u></a>):</p>
            <pre><code>[[container-app]]
image = "./key-transparency/verifier/Dockerfile"
name = "verifier"

[durable_objects]
bindings = { name = "VERIFIER", class_name = "Verifier", container = "verifier" } }
</code></pre>
            <p>Then, in my Worker, I call the runVerification <a href="https://developers.cloudflare.com/workers/runtime-apis/rpc/"><u>RPC method</u></a> of my Durable Object:</p>
            <pre><code>fetch(request, env, ctx) {
  const id = new URL(request.url).searchParams.get('id')
  const durableObjectId = env.VERIFIER.idFromName(request.params.id);
  await env.VERIFIER.get(durableObjectId).runVerification()
  //...
}
</code></pre>
            <p>From my Durable Object I can boot, configure, mount storage buckets as directories, and make HTTP requests to the container:</p>
            <pre><code>class Verifier extends DurableObject {
  constructor(state, env) {
    this.ctx.blockConcurrency(async () =&gt; {

      // starts the container
      await this.ctx.container.start();

      // configures the container before accepting traffic
      const config = await this.state.storage.get("verifierConfig");
      await this.ctx.container.fetch("/set-config", { method: "PUT", body: config});
    })
  }

  async runVerification(updateId) {
    // downloads &amp; mounts latest updates from R2
    const latestPublicKeyUpdates = await this.env.R2.get(`public-key-updates/${updateId}`);
    await this.ctx.container.mount(`/updates/${updateId}`, latestPublicKeyUpdates);

    // starts verification via HTTP call 
    return await this.ctx.container.fetch(`/verifier/${updateId}`);
  }
}
</code></pre>
            <p>And… that’s it.</p><p>I didn’t have to worry about placement, scaling, service discovery authorization, and I was able to leverage integrations into other services like KV and R2 with just a few lines of code. The container platform took care of routing, placement, and auth. If I needed more instances, I could call the binding with a new ID, and the platform would scale up containers for me.</p><p>We are still in the early stages of building these integrations, but we’re excited about everything that containers will bring to Workers and vice versa.</p>
    <div>
      <h2>So, what do you want to build?</h2>
      <a href="#so-what-do-you-want-to-build">
        
      </a>
    </div>
    <p>If you’ve read this far, there’s a non-zero chance you were hoping to get to run a container yourself on our network. While we’re not ready (quite yet) to open up the platform to everyone, now that we’ve built a few GA products on our container platform, we’re looking for a handful of engineering teams to start building, in advance of wider availability in 2025. And we’re <a href="https://job-boards.greenhouse.io/cloudflare/jobs/5547603"><u>continuing to hire engineers</u></a> to work on this.</p><p>We’ve told you about our use cases for containers, and now it’s your turn. If you’re interested, <a href="https://forms.gle/msrkBLBYNFFYRaqY8"><u>tell us here</u></a> what you want to build, and why it goes beyond what’s possible today in <a href="https://developers.cloudflare.com/workers/"><u>Workers</u></a> and on our <a href="https://developers.cloudflare.com/products/?product-group=Developer+platform"><u>Developer Platform</u></a>. What do you wish you could build on Cloudflare, but can’t yet today?</p> ]]></content:encoded>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[AI]]></category>
            <category><![CDATA[Durable Objects]]></category>
            <guid isPermaLink="false">5KJejYd85inMDIZUZ6z17D</guid>
            <dc:creator>Brendan Irvine-Broque</dc:creator>
            <dc:creator>Thomas Lefebvre</dc:creator>
            <dc:creator>Mike Nomitch</dc:creator>
        </item>
        <item>
            <title><![CDATA[More NPM packages on Cloudflare Workers: Combining polyfills and native code to support Node.js APIs]]></title>
            <link>https://blog.cloudflare.com/more-npm-packages-on-cloudflare-workers-combining-polyfills-and-native-code/</link>
            <pubDate>Mon, 09 Sep 2024 21:00:00 GMT</pubDate>
            <description><![CDATA[ Workers now supports more NPM packages and Node.js APIs using an overhauled hybrid compatibility layer. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today, we are excited to announce a preview of <a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/"><u>improved Node.js compatibility</u></a> for Workers and Pages. Broader compatibility lets you use more NPM packages and take advantage of the JavaScript ecosystem when writing your Workers.</p><p>Our newest version of Node.js compatibility combines the best features of our previous efforts. <a href="https://workers.cloudflare.com/"><u>Cloudflare Workers</u></a> have supported Node.js in some form for quite a while. We first announced polyfill support in <a href="https://blog.cloudflare.com/node-js-support-cloudflare-workers"><u>2021</u></a>, and later <a href="https://blog.cloudflare.com/workers-node-js-asynclocalstorage"><u>built-in support for parts of the Node.js API</u></a> that has <a href="https://blog.cloudflare.com/workers-node-js-apis-stream-path"><u>expanded</u></a> over time.</p><p>The latest changes make it even better:</p><ul><li><p>You can use far more <a href="https://en.wikipedia.org/wiki/Npm"><u>NPM</u></a> packages on Workers.</p></li><li><p>You can use packages that do not use the <code>node</code>: prefix to import Node.js APIs</p></li><li><p>You can use <a href="https://workers-nodejs-compat-matrix.pages.dev/"><u>more Node.js APIs on Workers</u></a>, including most methods on <a href="https://nodejs.org/docs/latest/api/async_hooks.html"><code><u>async_hooks</u></code></a>, <a href="https://nodejs.org/api/buffer.html"><code><u>buffer</u></code></a>, <a href="https://nodejs.org/api/dns.html"><code><u>dns</u></code></a>, <a href="https://nodejs.org/docs/latest/api/os.html"><code><u>os</u></code></a>, and <a href="https://nodejs.org/docs/latest/api/events.html"><code><u>events</u></code></a>. Many more, such as <a href="https://nodejs.org/api/fs.html"><code><u>fs</u></code></a> or <a href="https://nodejs.org/docs/latest/api/process.html"><code><u>process</u></code></a> are importable with mocked methods.</p></li></ul><p>To give it a try, add the following flag to <code>wrangler.toml</code>, and deploy your Worker with <a href="https://developers.cloudflare.com/workers/wrangler/"><u>Wrangler</u></a>:</p><p><code>compatibility_flags = ["nodejs_compat_v2"]</code></p><p>Packages that could not be imported with <code>nodejs_compat</code>, even as a dependency of another package, will now load. This includes popular packages such as <a href="https://www.npmjs.com/package/body-parser">body-parser</a>, <a href="https://www.npmjs.com/package/jsonwebtoken">jsonwebtoken</a>, {}<a href="https://www.npmjs.com/package/got">got</a>, <a href="https://www.npmjs.com/package/passport">passport</a>, <a href="https://www.npmjs.com/package/md5">md5</a>, <a href="https://www.npmjs.com/package/knex">knex</a>, <a href="https://www.npmjs.com/package/mailparser">mailparser</a>, <a href="https://www.npmjs.com/package/csv-stringify">csv-stringify</a>, <a href="https://www.npmjs.com/package/cookie-signature">cookie-signature</a>, <a href="https://www.npmjs.com/package/stream-slice">stream-slice</a>, and many more.</p><p>This behavior will soon become the default for all Workers with the <a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/"><u>existing nodejs_compat compatibility flag</u></a> enabled, and a <a href="https://developers.cloudflare.com/workers/configuration/compatibility-dates/"><u>compatibility date</u></a> of 2024-09-23 or later. As you experiment with improved Node.js compatibility, share your feedback by <a href="https://github.com/cloudflare/workers-sdk/issues/new?assignees=&amp;labels=bug&amp;projects=&amp;template=bug-template.yaml&amp;title=%F0%9F%90%9B+BUG%3A"><u>opening an issue on GitHub</u></a>.</p>
    <div>
      <h3>Workerd is not Node.js</h3>
      <a href="#workerd-is-not-node-js">
        
      </a>
    </div>
    <p>To understand the latest changes, let’s start with a brief overview of how the Workers runtime differs from <a href="https://nodejs.org/"><u>Node.js</u></a>.</p><p>Node.js was built primarily for services run directly on a host OS and pioneered server-side JavaScript. Because of this, it includes functionality necessary to interact with the host machine, such as <a href="https://nodejs.org/api/process.html"><u>process</u></a> or <a href="https://nodejs.org/api/fs.html"><u>fs</u></a>, and a variety of utility modules, such as <a href="https://nodejs.org/api/crypto.html"><u>crypto</u></a>.</p><p>Cloudflare Workers run on an open source JavaScript/Wasm runtime called <a href="https://github.com/cloudflare/workerd"><u>workerd</u></a>. While both Node.js and workerd are built on <a href="https://v8.dev/"><u>V8</u></a>, workerd is <a href="https://blog.cloudflare.com/cloud-computing-without-containers"><u>designed to run untrusted code in shared processes</u></a>, exposes <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/"><u>bindings</u></a> for interoperability with other Cloudflare services, including <a href="https://blog.cloudflare.com/javascript-native-rpc"><u>JavaScript-native RPC</u></a>, and uses <a href="https://blog.cloudflare.com/introducing-the-wintercg"><u>web-standard APIs</u></a> whenever possible.</p><p>Cloudflare <a href="https://blog.cloudflare.com/introducing-the-wintercg/"><u>helped establish</u></a> <a href="https://wintercg.org/"><u>WinterCG</u></a>, the Web-interoperable Runtimes Community Group to improve interoperability of JavaScript runtimes, both with each other and with the web platform. You can build many applications using only web-standard APIs, but what about when you want to import dependencies from NPM that rely on Node.js APIs?</p><p>For example, if you attempt to import <a href="https://www.npmjs.com/package/pg"><u>pg</u></a>, a PostgreSQL driver, without Node.js compatibility turned on…</p>
            <pre><code>import pg from 'pg'</code></pre>
            <p>You will see the following error when you run <a href="https://developers.cloudflare.com/workers/wrangler/commands/#dev"><u>wrangler dev</u></a> to build your Worker:</p>
            <pre><code>✘ [ERROR] Could not resolve "events"
    ../node_modules/.pnpm/pg-cloudflare@1.1.1/node_modules/pg-cloudflare/dist/index.js:1:29:
      1 │ import { EventEmitter } from 'events';
        ╵                              ~~~~~~~~
  The package "events" wasn't found on the file system but is built into node.</code></pre>
            <p>This happens because the pg package imports the <a href="https://nodejs.org/api/events.html"><u>events module</u></a> from Node.js, which is not provided by workerd by default.</p><p>How can we enable this?</p>
    <div>
      <h3>Our first approach – build-time polyfills</h3>
      <a href="#our-first-approach-build-time-polyfills">
        
      </a>
    </div>
    <p>Polyfills are code that add functionality to a runtime that does not natively support it. They are often added to provide modern JavaScript functionality to older browsers, but can be used for server-side runtimes as well.</p><p>In 2022, we <a href="https://github.com/cloudflare/workers-sdk/pull/869"><u>added functionality to Wrangler</u></a> that injected polyfill implementations of some Node.js APIs into your Worker if you set <code>node_compat = true</code> in your wrangler.toml. For instance, the following code would work with this flag, but not without:</p>
            <pre><code>import EventEmitter from 'events';
import { inherits } from 'util';</code></pre>
            <p>These polyfills are essentially just additional JavaScript code added to your Worker by <a href="https://developers.cloudflare.com/workers/wrangler/"><u>Wrangler</u></a> when deploying the Worker. This behavior is enabled by <a href="https://www.npmjs.com/package/@esbuild-plugins/node-globals-polyfill"><code><u>@esbuild-plugins/node-globals-polyfill</u></code></a> which in itself uses <a href="https://github.com/ionic-team/rollup-plugin-node-polyfills/"><code><u>rollup-plugin-node-polyfills</u></code></a>.</p><p>This allows you to import and use some NPM packages, such as pg. However, many modules cannot be polyfilled with fast enough code or cannot be polyfilled at all.</p><p>For instance, <a href="https://nodejs.org/api/buffer.html"><u>Buffer</u></a> is a common Node.js API used to handle binary data. Polyfills exist for it, but JavaScript is often not optimized for the operations it performs under the hood, such as <code>copy</code>, <code>concat</code>, substring searches, or transcoding. While it is possible to implement in pure JavaScript, it could be far faster if the underlying runtime could use primitives from different languages. Similar limitations exist for other popular APIs such as <a href="https://nodejs.org/api/crypto.html"><u>Crypto</u></a>, <a href="https://nodejs.org/api/async_context.html"><u>AsyncLocalStorage</u></a>, and <a href="https://nodejs.org/api/stream.html"><u>Stream</u></a>.</p>
    <div>
      <h3>Our second approach – native support for some Node.js APIs in the Workers runtime</h3>
      <a href="#our-second-approach-native-support-for-some-node-js-apis-in-the-workers-runtime">
        
      </a>
    </div>
    <p>In 2023, we <a href="https://blog.cloudflare.com/workers-node-js-asynclocalstorage"><u>started adding</u></a> a subset of Node.js APIs directly to the Workers runtime. You can enable these APIs by adding the <a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/"><u>nodejs_compat compatibility flag</u></a> to your Worker, but you cannot use polyfills with <code>node_compat = true</code> at the same time.</p><p>Also, when importing Node.js APIs, you must use the <code>node</code>: prefix:</p>
            <pre><code>import { Buffer } from 'node:buffer';</code></pre>
            <p>Since these Node.js APIs are built directly into the Workers runtime, they can be <a href="https://github.com/cloudflare/workerd/blob/main/src/workerd/api/node/buffer.c%2B%2B"><u>written in C++</u></a>, which allows them to be faster than JavaScript polyfills. APIs like <a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/asynclocalstorage/"><u>AsyncLocalStorage</u></a>, which cannot be polyfilled without safety or performance issues, can be provided natively.</p><p>Requiring the <code>node: </code>prefix made imports more explicit and aligns with modern Node.js conventions. Unfortunately, existing NPM packages may import modules without <code>node:</code>. For instance, revisiting the example above, if you import the popular package <code>pg</code> in a Worker with the <code>nodejs_compat</code> flag, you still see the following error:</p>
            <pre><code>✘ [ERROR] Could not resolve "events"
    ../node_modules/.pnpm/pg-cloudflare@1.1.1/node_modules/pg-cloudflare/dist/index.js:1:29:
      1 │ import { EventEmitter } from 'events';
        ╵                              ~~~~~~~~
  The package "events" wasn't found on the file system but is built into node.</code></pre>
            <p>Many NPM packages still didn’t work in Workers, even if you enabled the <code>nodejs_compat</code> compatibility flag. You had to choose between a smaller set of performant APIs, exposed in a way that many NPM packages couldn’t access, or a larger set of incomplete and less performant APIs. And APIs like <code>process</code> that are exposed as globals in Node.js could still only be accessed by importing them as modules.</p>
    <div>
      <h3>The new approach: a hybrid model</h3>
      <a href="#the-new-approach-a-hybrid-model">
        
      </a>
    </div>
    <p>What if we could have the best of both worlds, and it just worked?</p><ul><li><p>A subset of Node.js APIs implemented directly in the Workers Runtime </p></li><li><p>Polyfills for the majority of other Node.js APIs</p></li><li><p>No <code>node</code>: prefix required</p></li><li><p>One simple way to opt-in</p></li></ul><p>Improved Node.js compatibility does just that.</p><p>Let’s take a look at two lines of code that look similar, but now act differently under the hood when <code>nodejs_compat_v2</code> is enabled:</p>
            <pre><code>import { Buffer } from 'buffer';  // natively implemented
import { isIP } from 'net'; // polyfilled</code></pre>
            <p>The first line imports <code>Buffer</code> from a <a href="https://github.com/cloudflare/workerd/blob/main/src/node/internal/internal_buffer.ts"><u>JavaScript module</u></a> in workerd that is backed by <a href="https://github.com/cloudflare/workerd/blob/main/src/workerd/api/node/buffer.c%2B%2B"><code><u>C++ code</u></code></a>. Various other Node.js modules are similarly implemented in a combination of Typescript and C++, including <a href="https://github.com/cloudflare/workerd/blob/main/src/workerd/api/node/async-hooks.h"><code><u>AsyncLocalStorage</u></code></a> and <a href="https://github.com/cloudflare/workerd/blob/main/src/workerd/api/node/crypto.h"><code><u>Crypto</u></code></a>. This allows for highly performant code that matches Node.js behavior.</p><p>Note that the <code>node:</code> prefix is not needed when importing <code>buffer</code>, but the code would also work with <code>node:buffer</code>.</p><p>The second line imports <code>net</code> which Wrangler automatically polyfills using a library called <a href="https://github.com/unjs/unenv"><u>unenv</u></a>. Polyfills and built-in runtime APIs now work together.</p><p>Previously, when you set <code>node_compat = true</code>, Wrangler added polyfills for every Node.js API that it was able to, even if neither your Worker nor its dependencies used that API. When you enable the <a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/"><u>nodejs_compat_v2 compatibility flag</u></a>, Wrangler only adds polyfills for Node.js APIs that your Worker or its dependencies actually use. This results in small Worker sizes, even with polyfills.</p><p>For some Node.js APIs, there is not yet native support in the Workers runtime nor a polyfill implementation. In these cases, unenv “mocks” the interface. This means it adds the module and its methods to your Worker, but calling methods of the module will either do nothing or will throw an error with a message like:</p><p><code>[unenv] &lt;method name&gt; is not implemented yet!</code></p><p>This is more important than it might seem. Because if a Node.js API is “mocked”, NPM packages that depend on it can still be imported. Consider the following code:</p>
            <pre><code>// Package name: my-module

import fs from "fs";

export function foo(path) {
  const data = fs.readFileSync(path, 'utf8');
  return data;
}

export function bar() {
  return "baz";
}
</code></pre>
            
            <pre><code>import { bar } from "my-module"

bar(); // returns "baz"
foo(); // throws readFileSync is not implemented yet!
</code></pre>
            <p>Previously, even with the <a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/"><u>existing nodejs_compat compatibility flag</u></a> enabled, attempting to import my-module would fail at build time, because the <code>fs</code> module could not be resolved. Now, the <code>fs</code> module can be resolved, methods that do not rely on an unimplemented Node.js API work, and methods that do throw a more specific error – a runtime error that a specific Node.js API method is not yet supported, rather than a build-time error that the module could not be resolved.</p><p>This is what enables some packages to transition from “doesn’t even load on Workers” to, “loads, but with some unsupported methods”.</p>
    <div>
      <h3>Still missing an API from Node.js? Module aliasing to the rescue</h3>
      <a href="#still-missing-an-api-from-node-js-module-aliasing-to-the-rescue">
        
      </a>
    </div>
    <p>Let’s say you need an NPM package to work on Workers that relies on a Node.js API that isn’t yet implemented in the Workers runtime or as a polyfill in unenv. You can use <a href="https://developers.cloudflare.com/workers/wrangler/configuration/#module-aliasing"><u>module aliasing</u></a> to implement just enough of that API to make things work.</p><p>For example, let’s say the NPM package you need to work calls <a href="https://nodejs.org/api/fs.html#fsreadfilepath-options-callback"><u>fs.readFile</u></a>. You can alias the fs module by adding the following to your Worker’s wrangler.toml:</p>
            <pre><code>[alias]
"fs" = "./fs-polyfill"</code></pre>
            <p>Then, in the fs-polyfill.js file, you can define your own implementation of any methods of the fs module:</p>
            <pre><code>export function readFile() {
  console.log("readFile was called");
  // ...
}
</code></pre>
            <p>Now, the following code, which previously threw the error message “[unenv] readFile is not implemented yet!”, runs without errors:</p>
            <pre><code>import { readFile } from 'fs';

export default {
  async fetch(request, env, ctx) {
    readFile();
    return new Response('Hello World!');
  },
};
</code></pre>
            <p>You can also use module aliasing to provide an implementation of an NPM package that does not work on Workers, even if you only rely on that NPM package indirectly, as a dependency of one of your Worker's dependencies.</p><p>For example, some NPM packages, such as <a href="https://www.npmjs.com/package/cross-fetch"><u>cross-fetch</u></a>, depend on <a href="https://www.npmjs.com/package/node-fetch"><u>node-fetch</u></a>, a package that provided a polyfill of the <a href="https://developers.cloudflare.com/workers/runtime-apis/fetch/"><u>fetch() API</u></a> before it was built into Node.js. The node-fetch package isn't needed in Workers, because the fetch() API is provided by the Workers runtime. And node-fetch doesn't work on Workers, because it relies on currently unsupported Node.js APIs from the <a href="https://nodejs.org/api/http.html"><u>http</u></a> and <a href="https://nodejs.org/api/https.html"><u>https</u></a> modules.</p><p>You can alias all imports of node-fetch to instead point directly to the fetch() API that is built into the Workers runtime using the popular <a href="https://github.com/SukkaW/nolyfill"><u>nolyfill</u></a> package:</p>
            <pre><code>[alias]
"node-fetch" = "./fetch-nolyfill"</code></pre>
            <p>All your replacement module needs to do in this case is to re-export the fetch API that is built into the Workers runtime:</p>
            <pre><code>export default fetch;</code></pre>
            
    <div>
      <h3>Contributing back to unenv</h3>
      <a href="#contributing-back-to-unenv">
        
      </a>
    </div>
    <p>Cloudflare is actively contributing to unenv. We think unenv is solving the problem of cross-runtime compatibility the right way — it adds only the necessary polyfills to your application, based on what APIs you use and what runtime you target. The project supports a variety of runtimes beyond workerd and is already used by other popular projects including <a href="https://nuxt.com/"><u>Nuxt</u></a> and <a href="https://nitro.unjs.io/"><u>Nitro</u></a>. We want to thank <a href="https://github.com/pi0"><u>Pooya Parsa</u></a> and the unenv maintainers and encourage others in the ecosystem to adopt or contribute.</p>
    <div>
      <h3>The path forward</h3>
      <a href="#the-path-forward">
        
      </a>
    </div>
    <p>Currently, you can enable improved Node.js compatibility by setting the <code>nodejs_compat_v2</code> flag in <code>wrangler.toml</code>. We plan to make the new behavior the default when using the <code>nodejs_compat</code> flag on September 23rd. This will require updating your <a href="https://developers.cloudflare.com/workers/configuration/compatibility-dates/"><code><u>compatibility_date</u></code></a>.</p><p>We are excited about the changes coming to Node.js compatibility, and encourage you to try it today. <a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/"><u>See the documentation</u></a> on how to opt-in for your Workers, and please send feedback and report bugs <a href="https://github.com/cloudflare/workers-sdk/issues/new?assignees=&amp;labels=bug&amp;projects=&amp;template=bug-template.yaml&amp;title=%F0%9F%90%9B+BUG%3A"><u>by opening an issue</u></a>. Doing so will help us identify any gaps in support and ensure that as much of the Node.js ecosystem as possible runs on Workers.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Node.js]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[JavaScript]]></category>
            <guid isPermaLink="false">3zICVbgdxrLByG4g2Dsddy</guid>
            <dc:creator>James M Snell</dc:creator>
            <dc:creator>Igor Minar</dc:creator>
            <dc:creator>James Culveyhouse</dc:creator>
            <dc:creator>Mike Nomitch</dc:creator>
        </item>
    </channel>
</rss>