
<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>Wed, 15 Apr 2026 17:52:29 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Dynamic, identity-aware, and secure Sandbox auth]]></title>
            <link>https://blog.cloudflare.com/sandbox-auth/</link>
            <pubDate>Mon, 13 Apr 2026 13:00:00 GMT</pubDate>
            <description><![CDATA[ Outbound Workers for Sandboxes provide a programmable, zero-trust egress proxy for AI agents. This allows developers to inject credentials and enforce dynamic security policies without exposing sensitive tokens to untrusted code.
 ]]></description>
            <content:encoded><![CDATA[ <p>As <a href="https://www.cloudflare.com/learning/ai/what-is-large-language-model/"><u>AI Large Language Models</u></a> and harnesses like OpenCode and Claude Code become increasingly capable, we see more users kicking off sandboxed agents in response to chat messages, Kanban updates, <a href="https://www.cloudflare.com/learning/ai/ai-vibe-coding/"><u>vibe coding</u></a> UIs, terminal sessions, GitHub comments, and more.</p><p>The sandbox is an important step beyond simple containers, because it gives you a few things:</p><ul><li><p><b>Security</b>: Any untrusted end user (or a rogue LLM) can run in the sandbox and not compromise the host machine or other sandboxes running alongside it. This is traditionally (<a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/worker-loader/"><u>but not always</u></a>) accomplished with a microVM.</p></li><li><p><b>Speed</b>: An end user should be able to pick up a new sandbox quickly <i>and</i> restore the state from a previously used one quickly.</p></li><li><p><b>Control</b>: The <i>trusted</i> platform needs to be able to take actions within the <i>untrusted</i> domain of the sandbox. This might mean mounting files in the sandbox, or controlling which requests access it, or executing specific commands.</p></li></ul><p>Today, we’re excited to add another key component of control to our <a href="https://developers.cloudflare.com/sandbox/"><u>Sandboxes</u></a> and all <a href="https://developers.cloudflare.com/containers/"><u>Containers</u></a>: outbound Workers. These are programmatic egress proxies that allow users running sandboxes to easily connect to different services, add <a href="https://www.cloudflare.com/learning/performance/what-is-observability/"><u>observability</u></a>, and, importantly for agents, add flexible and safe authentication.</p>
    <div>
      <h2>How it works</h2>
      <a href="#how-it-works">
        
      </a>
    </div>
    <p>Here’s a quick look at adding a secret key to a header using an outbound Worker:</p>
            <pre><code>class OpenCodeInABox extends Sandbox {
  static outboundByHost = {
    "github.com": (request, env, ctx) =&gt; {
      const headersWithAuth = new Headers(request.headers);
      headersWithAuth.set("x-auth-token", env.SECRET);
      return fetch(request, { headers: headersWithAuth });
    }
  }
}
</code></pre>
            <p>Any time code running in the sandbox makes a request to <code>“github.com”</code>, the request is proxied through the handler. This allows you to do anything you want on each request, including logging, modifying, or cancelling it. In this case, we’re safely injecting a secret (more on this later). The proxy runs on the same machine as any sandbox, has access to distributed state, and can be easily modified with simple JavaScript.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/RSbCJMOoqz8xLWMnPyijV/520addf1676d195be6258b427881cdde/BLOG-3199_2.png" />
          </figure><p>We’re excited about all the possibilities this adds to Sandboxes, especially around authentication for agents. Before going into details, let’s back up and take a quick tour of traditional forms of auth, and why we think there’s something better.</p>
    <div>
      <h2>Common auth for agentic workloads</h2>
      <a href="#common-auth-for-agentic-workloads">
        
      </a>
    </div>
    <p>The core issue with agentic auth is that we can’t fully trust the workload. While our LLMs aren’t nefarious (at least not yet), we still need to be able to apply protections to ensure they don’t use data inappropriately or take actions they shouldn’t.</p><p>A few common methods exist to provide auth to agents, and each has downsides:</p><p><b>Standard API tokens</b> are the most basic method of authentication, typically injected into applications via environment variables or in mounted secrets files. This is the arguably simplest method, but least secure. You have to trust that the sandbox won’t somehow be compromised or accidentally exfiltrate the token while making a request. Since you can’t fully trust the agent, you’ll need to set up token expiry and rotation, which can be a hassle.</p><p><b>Workload identity tokens</b>, such as OIDC tokens, can solve some of this pain. Rather than granting the agent a token with general permissions, you can grant it a token that attests its identity. Now, rather than the agent having direct access to some service with a token, it can exchange an identity token for a very short-lived access token. The OIDC token can be invalidated after a specific agent’s workflow completes, and expiry is easier to manage. One of the biggest downsides of workload identity tokens is the potential inflexibility of integrations. Many services don’t have first-class support for OIDC, so in order to get working integrations with upstream services, platforms will need to roll their own token-exchanging services. This makes adoption difficult.</p><p><b>Custom proxies </b>provide maximum flexibility, and can be paired with workload identity tokens. If you can pass some or all of your sandbox egress through a trusted piece of code, you can insert whatever rules you need. Maybe the upstream service your agent is communicating with has a bad RBAC story, and it can’t provide granular permissions. No problem, just write the controls and permissions yourself! This is a great option for agents that you need to lock down with granular controls. However, how do you intercept all of a sandbox’s traffic? How do you set up a proxy that is dynamic and easily programmable? How do you proxy traffic efficiently? These aren’t easy problems to solve.</p><p>With those imperfect methods in mind, what does an ideal auth mechanism look like?</p><p>Ideally, it is:</p><ul><li><p><b>Zero trust.</b> No token is ever granted to an untrusted user for any amount of time.</p></li><li><p><b>Simple. </b>Easy to author. Doesn’t involve a complex system of minting, rotating, and decrypting tokens.</p></li><li><p><b>Flexible.</b> We don’t rely on the upstream system to provide the granular access we need. We can apply whatever rules we want.</p></li><li><p><b>Identity-aware.</b> We can identify the sandbox making the call and apply specific rules for it.</p></li><li><p><b>Observable.</b> We can easily gather information about what calls are being made.</p></li><li><p><b>Performant.</b> We aren’t round-tripping to a centralized or slow source of truth.</p></li><li><p><b>Transparent.</b> The sandboxed workload doesn’t have to know about it. Things just work.</p></li><li><p><b>Dynamic.</b> We can change rules on the fly.</p></li></ul><p>We believe outbound Workers for Sandboxes fit the bill on all of these. Let’s see how.</p>
    <div>
      <h2>Outbound Workers in practice</h2>
      <a href="#outbound-workers-in-practice">
        
      </a>
    </div>
    
    <div>
      <h3>Basics: restriction and observability</h3>
      <a href="#basics-restriction-and-observability">
        
      </a>
    </div>
    <p>First, we’ll look at a very basic example: logging requests and denying specific actions.</p><p>In this case, we’ll use the outbound function, which intercepts all outgoing HTTP requests from the sandbox. With a few lines of JavaScript, it’s easy to ensure only GETs are made and log then deny any disallowed methods.</p>
            <pre><code>class MySandboxedApp extends Sandbox {
  static outbound = (req, env, ctx) =&gt; {
    // Deny any non-GET action and log
    if (req.method !== 'GET') {
      console.log(`Container making ${req.method} request to: ${req.url}`);
      return new Response('Not Allowed', { status: 405, statusText: 'Method Not Allowed'});
    }

    // Proceed if it is a GET request
    return fetch(req);
  };
}
</code></pre>
            <p>This proxy runs on Workers and runs on the same machine as the sandboxed VM. Workers were built for quick response times, often sitting in front of cached CDN traffic, so additional latency is extremely minimal.</p><p>Because this is running on Workers, we get observability out of the box. You can view logs and outbound requests <a href="https://developers.cloudflare.com/workers/observability/logs/workers-logs/"><u>in the Workers dashboard</u></a> or <a href="https://developers.cloudflare.com/workers/observability/logs/logpush/"><u>export them</u></a> to your application performance monitoring tool of choice.</p>
    <div>
      <h3>Zero trust credential injection</h3>
      <a href="#zero-trust-credential-injection">
        
      </a>
    </div>
    <p>How would we use this to enforce a <a href="https://www.cloudflare.com/learning/security/glossary/what-is-zero-trust/"><u>zero trust environment</u></a> for our agent? Let’s imagine we want to make a request to a private GitHub instance, but we never want our LLM to access a private token.</p><p>We can use <code>outboundByHost</code> to define functions for specific domains or IPs. In this case, we’ll inject a protected credential if the domain is “my-internal-vcs.dev”. The sandboxed agent <i>never has access</i> to these credentials.</p>
            <pre><code>class OpenCodeInABox extends Sandbox {
  static outboundByHost = {
    "my-internal-vcs.dev": (request, env, ctx) =&gt; {
      const headersWithAuth = new Headers(request.headers);
      headersWithAuth.set("x-auth-token", env.SECRET);
      return fetch(request, { headers: headersWithAuth });
    }
  }
}
</code></pre>
            <p>It is also easy to conditionalize the response based on the identity of the container. You don’t have to inject the same tokens for every sandbox instance.</p>
            <pre><code> static outboundByHost = {
  "my-internal-vcs.dev": (request, env, ctx) =&gt; {
    // note: KV is encrypted at rest and in transit
    const authKey = await env.KEYS.get(ctx.containerId);

    const requestWithAuth = new Request(request);
    requestWithAuth.headers.set("x-auth-token", authKey);
    return fetch(requestWithAuth);
  }
}
</code></pre>
            
    <div>
      <h3>Using the Cloudflare Developer Platform</h3>
      <a href="#using-the-cloudflare-developer-platform">
        
      </a>
    </div>
    <p>As you may have noticed in the last example, another major advantage of using outbound Workers is that it makes integration into the Workers ecosystem easier. Previously, if a user wanted to access <a href="https://www.cloudflare.com/developer-platform/products/r2/"><u>R2</u></a>, they would have to inject an R2 credential, then make a call from their container to the public R2 API. Same for <a href="https://developers.cloudflare.com/kv/"><u>KV</u></a>, <a href="https://developers.cloudflare.com/agents/"><u>Agents</u></a>, other <a href="https://developers.cloudflare.com/containers/"><u>Containers</u></a>, other <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/"><u>Worker services</u></a>, <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/"><u>etc</u></a>.</p><p>Now, you just call <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/"><u>any binding</u></a> from your outbound Workers.</p>
            <pre><code>class MySandboxedApp extends Sandbox {
  static outboundByHost = {
    "my.kv": async (req, env, ctx) =&gt; {
      const key = keyFromReq(req);
      const myResult = await env.KV.get(key);
      return new Response(myResult);
    },
    "objects.cf": async (req, env, ctx) =&gt; {
      const prefix = ctx.containerId
      const path = pathFromRequest(req);
      const object = await env.R2.get(`${prefix}/${path}`);
      const myResult = await env.KV.get(key);
      return new Response(myResult);
    },
  };
}
</code></pre>
            <p>Rather than parsing tokens and setting up policies, we can easily conditionalize access with code and whatever logic we want. In the R2 example, we also were able to use the sandbox’s ID to further scope access with ease.</p>
    <div>
      <h3>Making controls dynamic</h3>
      <a href="#making-controls-dynamic">
        
      </a>
    </div>
    <p>Networking control should also be dynamic. On many platforms, config for Container and VM networking is static, looking something like this:</p>
            <pre><code>{
  defaultEgress: "block",
  allowedDomains: ["github.com", "npmjs.org"]
}
</code></pre>
            <p>This is better than nothing, but we can do better. For many sandboxes, we might want to apply a policy on start, but then override it with another once specific operations have been performed.</p><p>For instance, we can boot a sandbox, grab our dependencies via NPM and Github, and then lock down egress after that. This ensures that we open up the network for as little time as possible.</p><p>To achieve this, we can use <code>outboundHandlers</code>, which allows us to define arbitrary outbound handlers that can be applied programmatically using the <code>setOutboundHandler</code> method. Each of these also takes params, allowing you to customize behavior from code. In this case, we will allow some hostnames with the custom “<code>allowHosts</code>” policy, then turn off HTTP. </p>
            <pre><code>class MySandboxedApp extends Sandbox {
  static outboundHandlers = {
    async allowHosts(req, env, { params }) {
     const url = new URL(request.url);
     const allowedHostname = params.allowedHostnames.includes(url.hostname);

      if (allowedHostname) {
        return await fetch(newRequest);
      } else {
        return new Response(null, { status: 403, statusText: "Forbidden" });
      }
    }
    
    async noHttp(req) {
      return new Response(null, { status: 403, statusText: "Forbidden" });
    }
  }
}

async setUpSandboxes(req, env) {
  const sandbox = await env.SANDBOX.getByName(userId);
  await sandbox.setOutboundHandler("allowHosts", {
    allowedHostnames: ["github.com", "npmjs.org"]
  });
  await sandbox.gitClone(userRepoURL)
  await sandbox.exec("npm install")
  await sandbox.setOutboundHandler("noHttp");
}
</code></pre>
            <p>This could be extended even further. Your agent might ask the end user a question like “Do you want to allow POST requests to <code>cloudflare.com</code>?” based on whatever tools it needs at that time. With dynamic outbound Workers, you can easily modify the sandbox rules on the fly to provide this level of control.</p>
    <div>
      <h2>TLS support with MITM Proxying</h2>
      <a href="#tls-support-with-mitm-proxying">
        
      </a>
    </div>
    <p>To do anything useful with requests beyond allowing or denying them, you need to have access to the content. This means that if you’re making HTTPS requests, they need to be decrypted by the Workers proxy.</p><p>To achieve this, a unique ephemeral certificate authority (CA) and private key are created for each Sandbox instance, and the CA is placed into the sandbox. By default, sandbox instances will trust this CA, while standard container instances can opt into trusting it, for instance by calling <code>sudo update-ca-certificates</code>.</p>
            <pre><code>export class MyContainer extends Container {
  interceptHttps = true;
}

MyContainer.outbound = (req, env, ctx) =&gt; {
  // All HTTP(S) requests will trigger this hook.
  return fetch(req);
};

</code></pre>
            <p>TLS traffic is proxied by a Cloudflare isolated network process by performing a TLS handshake. It creates a leaf CA from an ephemeral and unique private key and uses the SNI extracted in the ClientHello. It will then invoke in the same machine the  configured Worker to handle the HTTPS request.</p><p>Our ephemeral private key and CA will never leave our container runtime sidecar process, and is never shared across other container sidecar processes.</p><p>With this in place, outbound Workers act as a truly transparent proxy. The sandbox doesn't need any awareness of specific protocols or domains — all HTTP and HTTPS traffic flows through the outbound handler for filtering or modification.</p>
    <div>
      <h2>Under the hood</h2>
      <a href="#under-the-hood">
        
      </a>
    </div>
    <p>To enable the functionality shown above in both <a href="https://github.com/cloudflare/containers"><code><u>Container</u></code></a> and <a href="https://github.com/cloudflare/sandbox-sdk"><code><u>Sandbox</u></code></a>, we added new methods to the <a href="https://developers.cloudflare.com/durable-objects/api/container/"><code><u>ctx.container</u></code></a> object: <code>interceptOutboundHttp and interceptOutboundHttps</code>, which intercept outgoing requests on specific hostnames (with basic glob matching), IP ranges, and it can be used to intercept all outbound requests. These methods are called with a <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/rpc/"><u>WorkerEntrypoint</u></a>, which gets set up as the front door to the outbound Worker.</p>
            <pre><code>export class MyWorker extends WorkerEntrypoint {
 fetch() {
   return new Response(this.ctx.props.message);
 }
}

// ... inside your container DurableObject ...
this.ctx.container.start({ enableInternet: false });
const outboundWorker = this.ctx.exports.MyWorker({ props: { message: 'hello' } });
await this.ctx.container.interceptOutboundHttp('15.0.0.1:80', outboundWorker);

// From now on, all HTTP requests to 15.0.0.1:80 return "hello"
await this.waitForContainerToBeHealthy();

// You can decide to return another message now...
const secondOutboundWorker = this.ctx.exports.MyWorker({ props: { message: 'switcheroo' } });
await this.ctx.container.interceptOutboundHttp('15.0.0.1:80', secondOutboundWorker);
// all HTTP requests to 15.0.0.1 now show "switcheroo", even on connections that were
// open before this interceptOutboundHttp

// You can even set hostnames, CIDRs, for both IPv4 and IPv6
await this.ctx.container.interceptOutboundHttp('example.com', secondOutboundWorker);
await this.ctx.container.interceptOutboundHttp('*.example.com', secondOutboundWorker);
await this.ctx.container.interceptOutboundHttp('123.123.123.123/23', secoundOutboundWorker);</code></pre>
            <p>All proxying to Workers happens locally on the same machine that runs the sandbox VM. Even though communication between container and Worker is “authless”, it is secure.</p><p>These methods can be called at any time, before or after starting the container, even while connections are still open. Connections that send multiple HTTP requests will automatically pick up a new entrypoint, so updating outbound Workers will not break existing TCP connections or interrupt HTTP requests.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/21utJcR5UpS7c0YNj8Y385/4a47d83acad42c5708ca23a7b04342f3/BLOG-3199_3.png" />
          </figure><p>Local development with <a href="https://developers.cloudflare.com/workers/wrangler/commands/#dev"><u>wrangler dev</u></a> also has support for egress interception. To make it possible, we automatically spawn a sidecar process inside the local container’s network namespace. We called this sidecar component <a href="https://github.com/cloudflare/proxy-everything"><i><u>proxy-everything</u></i></a>. Once <i>proxy-everything </i>is attached, it applies the appropriate TPROXY nftable rules, routing matching traffic from the local Container to <a href="https://github.com/cloudflare/workerd"><u>workerd</u></a>, Cloudflare’s open source JavaScript runtime, which runs the outbound Worker. This allows the local development experience to mirror what happens in prod, so testing and development remain simple.</p>
    <div>
      <h2>Giving outbound Workers a try</h2>
      <a href="#giving-outbound-workers-a-try">
        
      </a>
    </div>
    <p>If you haven’t tried Cloudflare Sandboxes, check out the <a href="https://developers.cloudflare.com/sandbox/get-started/"><u>Getting Started guide</u></a>. If you are a current user of <a href="https://developers.cloudflare.com/containers/"><u>Containers</u></a> or <a href="https://developers.cloudflare.com/sandbox/"><u>Sandboxes</u></a>, start using outbound Workers now by <a href="https://developers.cloudflare.com/containers/platform-details/outbound-traffic/"><u>reading the documentation</u></a> and upgrading to <code>@cloudflare/containers@0.3.0</code> or <code>@cloudflare/sandbox@0.8.9</code>.</p>
    <div>
      <h4>Watch on Cloudflare TV</h4>
      <a href="#watch-on-cloudflare-tv">
        
      </a>
    </div>
    <div>  </div><p></p> ]]></content:encoded>
            <category><![CDATA[Containers]]></category>
            <category><![CDATA[Sandbox]]></category>
            <category><![CDATA[Agents]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <guid isPermaLink="false">3j8ROSAUomMNPrmru3f2U9</guid>
            <dc:creator>Mike Nomitch</dc:creator>
            <dc:creator>Gabi Villalonga Simón</dc:creator>
        </item>
        <item>
            <title><![CDATA[Containers are available in public beta for simple, global, and programmable compute]]></title>
            <link>https://blog.cloudflare.com/containers-are-available-in-public-beta-for-simple-global-and-programmable/</link>
            <pubDate>Tue, 24 Jun 2025 16:00:22 GMT</pubDate>
            <description><![CDATA[ Cloudflare Containers are now available in public beta. Deploy simple, global, and programmable containers alongside your Workers.  ]]></description>
            <content:encoded><![CDATA[ <p>We’re excited to announce that <a href="https://blog.cloudflare.com/cloudflare-containers-coming-2025/"><u>Cloudflare Containers</u></a> are now available in beta for all users on paid plans.</p><p>You can now run new kinds of applications alongside your Workers. From media and data processing at the edge, to backend services in any language, to CLI tools in batch workloads — Containers open up a world of possibilities.</p><p>Containers are tightly integrated with Workers and the rest of the developer platform, which means that:</p><ul><li><p>Your workflow stays <b>simple</b>: just define a Container in a few lines of code, and run<code> wrangler deploy</code>, just like you would with a Worker.</p></li><li><p>Containers are <b>global: </b>as with Workers, you just deploy to Region:Earth. No need to manage configs across 5 different regions for a global app.</p></li><li><p>You can <b>use the right tool for the job</b>: routing requests between Workers and Containers is easy. Use a Worker when you need to be ultra light-weight and scalable. Use a Container when you need more power and flexibility.</p></li><li><p>Containers are <b>programmable</b>: container instances are spun up on-demand and controlled by Workers code. If you need custom logic, just write some JavaScript instead of spending time chaining together API calls or writing Kubernetes operators.</p></li></ul><p>Want to try it today? Deploy your first Container-enabled Worker:</p><a href="https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/containers-template"><img src="https://deploy.workers.cloudflare.com/button" /></a>
<p></p>
    <div>
      <h2>A tour of Containers</h2>
      <a href="#a-tour-of-containers">
        
      </a>
    </div>
    <p>Let’s take a deeper look at Containers, using an example use case: code sandboxing.</p><p>Let’s imagine that you want to run user-generated (or AI-generated) code as part of a platform you’re building. To do this, you want to spin up containers on demand. Each user needs their own isolated container, the users are distributed globally, and you need to start each container quickly so the users aren’t waiting.</p><p>You can set this up easily on Cloudflare Containers.</p>
    <div>
      <h4>Configuring a Container</h4>
      <a href="#configuring-a-container">
        
      </a>
    </div>
    <p>In your Worker, use the <a href="https://github.com/cloudflare/containers"><u>Container class</u></a> and <a href="https://developers.cloudflare.com//workers/wrangler/configuration/#containers"><u>wrangler.jsonc</u></a> to declare some basic configuration, such as your Container’s default port, a sleep timeout, and which image to use, then route to it via the Worker.</p><p>For each unique ID passed to the Container’s binding, Cloudflare will spin up a new Container instance and route requests to it. When a new instance is requested, Cloudflare picks the best location across our global network where we’ve pre-provisioned a ready-to-go container. This means that you can deploy a container close to an end user no matter where they are. And the initial container start takes just a few seconds. You don’t have to worry about routing, provisioning, or scaling.</p><p>This example Worker will route requests to a unique container instance for each sandbox ID given at the path <code>/sandbox/ID</code> and will be handled by standard Worker JavaScript otherwise:</p>
            <pre><code>export class MyContainer extends Container {
  defaultPort = 8080; // The default port for the container to listen on
  sleepAfter = '5m'; // Sleep the container if no requests are made in this timeframe
}

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

    // handle request with an on-demand container instance
    if (pathname.startsWith('/sandbox/')) {
      const sessionId = pathname.split("/")[2]
      const containerInstance = getContainer(env.CONTAINER_SANDBOX, sessionId)
      return await containerInstance.fetch(request);
    }

    // handle request with my Worker code otherwise
    return myWorkerRequestHandler(request);
  },
};</code></pre>
            
    <div>
      <h4>Familiar and easy development workflow with <code>wrangler dev</code></h4>
      <a href="#familiar-and-easy-development-workflow-with-wrangler-dev">
        
      </a>
    </div>
    <p>To configure which container image to use, you can provide an image URL in wrangler config or a path to a local Dockerfile. </p><p>This config tells wrangler to use a locally defined image:</p>
            <pre><code>"containers": [
  {
    "class_name": "ContainerSandbox",
    "image": "./Dockerfile",
    "max_instances": 80,
    "instance_type": "basic"
  }
]</code></pre>
            <p>When developing your application, you just run <code>wrangler dev</code> and the container image will be automatically built and routable via your local Worker. This makes it easy to iterate on container code while making changes to your Worker at the same time. When you want to rebuild your image, just press “R” on your keyboard from your terminal running <code>wrangler dev</code>, and the Container is rebuilt and restarted.</p>
    <div>
      <h4>Shipping your Container-enabled Worker to production with <code>wrangler deploy</code></h4>
      <a href="#shipping-your-container-enabled-worker-to-production-with-wrangler-deploy">
        
      </a>
    </div>
    <p>When it’s time to deploy, just run <code>wrangler deploy</code>. Wrangler will push your image to your account, then it will be provisioned in various locations across Cloudflare’s global network.</p><p>You don’t have to worry about “artifact management”, or distribution, or auth, or jump through hoops to integrate your container with Workers. You just write your code and deploy it.</p>
    <div>
      <h4>Observability is built-in</h4>
      <a href="#observability-is-built-in">
        
      </a>
    </div>
    <p>Once your Container is in production, you have the visibility you need into how things are going, with end-to-end <a href="https://www.cloudflare.com/developer-platform/products/workers-observability/">observability</a>. </p><p>From the Cloudflare dashboard, you can easily track status and resource usage across Container instances with built-in metrics:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1cMDL8qejpD9TnaVYgKeLD/cf42573c1315f0364c0d225bb77a76af/image4.png" />
          </figure><p>
And if you need to dive deeper, you can dig into logs, which will be retained in the Cloudflare UI for seven days or <a href="https://developers.cloudflare.com/logs/about/"><u>pushed to an external sink</u></a> of your choice:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2AusbmokynDaErssVFyu4A/3bb617ff9c2e488cdaee20612cb6bd15/image3.png" />
          </figure>
    <div>
      <h4>Try it yourself</h4>
      <a href="#try-it-yourself">
        
      </a>
    </div>
    <p>Want to give it a shot? Check out <a href="https://github.com/craigsdennis/sandboxing-day-containers"><u>this example Worker for running sandboxed code</u></a> in a Container, and deploy it with one click.</p><p>Or better yet, if you have an image sitting around that you’ve been dying to deploy to Cloudflare, you can get started with <a href="https://developers.cloudflare.com//containers/get-started/"><u>our docs here</u></a>. </p>
    <div>
      <h2>A world of possibilities</h2>
      <a href="#a-world-of-possibilities">
        
      </a>
    </div>
    <p>We’re excited about all the new types of applications that are now possible to build on Workers. We’ve heard many of you tell us over the years that you would love to run your entire application on Cloudflare, if only you could deploy this one piece that needs to run in a container.</p><p>Today, you can run libraries that you couldn't run in Workers before. For instance, try <a href="https://github.com/megaconfidence/wifski/"><u>this Worker that uses FFmpeg to convert video to a GIF</u></a>. </p><p>Or you can <a href="https://github.com/mikenomitch/cron-container"><u>run a container as part of a cron job</u></a>. Or deploy a <a href="https://github.com/mikenomitch/static-frontend-container-backend"><u>static frontend with a containerized backend</u></a>. Or even run a <a href="https://github.com/ghostwriternr/claude-code-containers"><u>Cloudflare Agent that uses a Container to run Claude Code</u></a> on your behalf.</p><p>The <a href="https://blog.cloudflare.com/cloudflare-containers-coming-2025/#integrating-with-more-of-cloudflares-developer-platform"><u>integration with the rest of the Developer Platform</u></a> makes Containers even more powerful: <a href="https://blog.cloudflare.com/cloudflare-containers-coming-2025/#durable-objects-as-programmable-sidecars"><u>use Durable Objects</u></a> for state management, <a href="https://developers.cloudflare.com/workflows/"><u>Workflows</u></a>, <a href="https://developers.cloudflare.com/queues/"><u>Queues</u></a>, and <a href="https://agents.cloudflare.com/"><u>Agents</u></a> to compose complex behaviors, <a href="https://developers.cloudflare.com/r2/"><u>R2</u></a> to store Container data or media, and more.</p>
    <div>
      <h2>Pricing and packaging</h2>
      <a href="#pricing-and-packaging">
        
      </a>
    </div>
    <p>As with the rest of our Cloudflare developer products, we wanted to apply the same principles to our developer platform with transparent pricing that scales up and down with your usage.</p><p>Today, you can select from the following instances at launch (and yes, we plan to add larger instances over time):  </p><table><tr><td><p><b>Name</b></p></td><td><p><b>Memory </b></p></td><td><p><b>CPU</b></p></td><td><p><b>Disk</b></p></td></tr><tr><td><p>dev</p></td><td><p>256 MiB</p></td><td><p>1/16 vCPU</p></td><td><p>2 GB</p></td></tr><tr><td><p>basic</p></td><td><p>1 GiB</p></td><td><p>1/4 vCPU</p></td><td><p>4 GB</p></td></tr><tr><td><p>standard</p></td><td><p>4 GiB</p></td><td><p>1/2 vCPU</p></td><td><p>4 GB</p></td></tr></table><p>You only pay for what you use — charges start when a request is sent to the container or when it is manually started. Charges stop after the container instance goes to sleep, which can happen automatically after a timeout. This makes it easy to scale to zero, and allows you to get high utilization even with bursty traffic.</p><p>Containers are billed for every 10ms that they are actively running at the following rates, with monthly amounts included in <i>Workers Standard</i>:</p><ul><li><p><b>Memory</b>: $0.0000025 per GiB-second, with 25 GiB-hours included</p></li><li><p><b>CPU</b>: $0.000020 per vCPU-second, with 375 vCPU-minutes included</p></li><li><p><b>Disk</b> $0.00000007 per GB-second, with 200 GB-hours included</p></li></ul><p>Egress from Containers is priced at the following rates, with monthly amounts included in <i>Workers Standard</i>:</p><ul><li><p>North America and Europe: $0.025 per GB with 1 TB included</p></li><li><p>Australia, New Zealand, Taiwan, and Korea: $0.050 per GB with 500 GB included</p></li><li><p>Everywhere else: $0.040 per GB with 500 GB included</p></li></ul><p>
See <a href="https://blog.cloudflare.com/cloudflare-containers-coming-2025/#pay-for-what-you-use-and-use-the-right-tool"><u>our previous blog post</u></a> for a more in-depth look into pricing with an example app.</p>
    <div>
      <h2>What’s coming next?</h2>
      <a href="#whats-coming-next">
        
      </a>
    </div>
    <p>With today’s release, we’ve only just begun to scratch the surface of what Containers will do on Workers. This is the first step of many towards our vision of a simple, global, and highly programmable Container platform.</p><p>We’re already thinking about what’s next, and wanted to give you a preview:</p><ul><li><p><b>Higher limits and larger instances</b> – We currently limit your concurrent instances to 40 total GiB of memory and 40 total vCPU. This is enough for some workloads, but many users will want to go higher —  a lot higher. Select customers are already scaling well into the thousands of concurrent containers, and we want to bring this ability to more users. We will be raising our limits over the coming months to allow for more total containers and larger instance sizes.</p></li><li><p><b>Global autoscaling and latency-aware routing</b> – Currently, containers are addressed by ID and started on-demand. For many use cases, users want to route to one of many stateless container instances deployed across the globe, then autoscale live instances automatically. Autoscaling will be activated with a single line of code, and will enable easy routing to the nearest ready instance.</p></li></ul>
            <pre><code>class MyBackend extends Container {
  defaultPort = 8080;
  autoscale = true; // global autoscaling on - new instances spin up when memory or CPU utilization is high
}

// routes requests to the nearest ready container and load balance globally
async fetch(request, env) {
  return getContainer(env.MY_BACKEND).fetch(request);
}</code></pre>
            <ul><li><p><b>More ways to communicate between Containers and Workers</b> – We will be adding more ways for your Worker to communicate with your container and vice versa. We will add an <code>exec</code> command to run shell commands in your instance and handlers for HTTP requests <i>from</i> the container to Workers. This will allow you to more easily extend your containers with functionality from the entire developer platform, reach out to other containers, and programmatically set up each container instance.</p></li></ul>
            <pre><code>class MyContainer extends Container {
  // sets up container-to-worker communication with handlers
  handlers = {
    "example.cf": "handleRequestFromContainer"
  };

  handleRequestFromContainer(req) {
    return new Response("You are responding from Workers to a Container request to a specific hostname")
  }

  // use exec to run commands in your container instance
  async cloneRepo(repoUrl) {
    let command = this.exec(`git clone ${repoUrl}`)
    await command.print()
  }  
}</code></pre>
            <ul><li><p><b>Further integrations with the Developer Platform</b> – We will continue to integrate with the developer platform with first-party APIs for our various services. We want it to be dead simple to mount <a href="https://developers.cloudflare.com/r2/"><u>R2 buckets</u></a>, reach <a href="https://developers.cloudflare.com/hyperdrive/"><u>Hyperdrive</u></a>, access <a href="https://developers.cloudflare.com/kv/"><u>KV</u></a>, and more.</p></li></ul><p>And we are just getting started. Stay tuned for more updates this summer and over the course of the entire year.</p>
    <div>
      <h2>Try Containers today</h2>
      <a href="#try-containers-today">
        
      </a>
    </div>
    <p>The first step is to deploy your own container. Run <code>npm create cloudflare@latest -- --template=cloudflare/templates/containers-template</code> or click the button below to deploy your first Container to Workers.</p><a href="https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/containers-template"><img src="https://deploy.workers.cloudflare.com/button" /></a>
<p></p>
<p>
We’re excited to see all the ways you will use Containers. From new languages and tools, to simplified Cloudflare-only architectures, to advanced programmatic control over container creation, you now have the ability to do even more on the Developer Platform. It is just a wrangler deploy away.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Containers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[AI]]></category>
            <guid isPermaLink="false">5StUs8TMNjY9aDG2TzFJ06</guid>
            <dc:creator>Gabi Villalonga Simón</dc:creator>
            <dc:creator>Ivan Chernetsky</dc:creator>
            <dc:creator>Josh Seba</dc:creator>
            <dc:creator>Nafeez Nazer</dc:creator>
            <dc:creator>Thomas Lefebvre</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>
    </channel>
</rss>