{"id":18139,"date":"2026-07-13T13:03:32","date_gmt":"2026-07-13T06:03:32","guid":{"rendered":"https:\/\/www.cipher.co.th\/blogs\/cloudflare-compute-services-explained-2\/"},"modified":"2026-07-13T13:03:32","modified_gmt":"2026-07-13T06:03:32","slug":"cloudflare-compute-services-explained","status":"publish","type":"post","link":"https:\/\/www.cipher.co.th\/en\/blogs\/cloudflare-compute-services-explained\/","title":{"rendered":"Compute on Cloudflare: Workers, Durable Objects, Containers, Workflows and Browser Rendering"},"content":{"rendered":"<p><strong>Quick answer:<\/strong> Use Workers as the default for stateless request-response work such as APIs, SSR and middleware. Use Durable Objects when you need a single authoritative point of consistency per key, for example a chat room, a stock counter or an exact rate limiter. Use Containers when you need a binary or library that cannot run inside a Worker. Use Workflows when a process has many steps and each step must survive a crash. Use Browser Rendering, which Cloudflare documentation now calls Browser Run, when you need a real browser, for example to turn HTML into a PDF or to crawl a JavaScript-rendered page.<\/p>\n<p>Most engineers arriving at Cloudflare assume Workers is the compute product, and try to force everything into it. What follows is code fighting the runtime limits: 128 MB of memory, no filesystem, and a CPU budget. The truth is that Cloudflare has five compute primitives, and they are not interchangeable.<\/p>\n<p>This article is not a re-documentation of Cloudflare. Its only job is to help you pick the right primitive for the job in front of you. Every section ends with a clear use-this-when and do-not-use-this-when. All limits and prices are from the official Cloudflare documentation as of July 2026.<\/p>\n<h2>Workers: the default<\/h2>\n<p>A Worker is an isolate running your JavaScript or WebAssembly. There is no container-style cold start, and it is the best fit for anything that answers an HTTP request quickly.<\/p>\n<h3>The billing model changes how you design<\/h3>\n<p>Workers Paid is 5 USD per month and includes 10 million requests and 30 million CPU-milliseconds. Overage is 0.30 USD per million requests and 0.02 USD per million CPU-milliseconds. The single most important fact is that Workers bills CPU time, not wall-clock time. The time your code spends waiting on a database, an upstream API or any other I\/O is not billed. If your mental model of serverless cost is still duration multiplied by memory, as it is on Lambda, this changes the architecture, not just the invoice. We covered the details in <a href=\"https:\/\/www.cipher.co.th\/en\/blogs\/cloudflare-workers-vs-aws-lambda\/\">Cloudflare Workers vs AWS Lambda<\/a>.<\/p>\n<h3>Limits you must know before you write a line of code<\/h3>\n<p>Memory is 128 MB per isolate, identical on Free and Paid, and it cannot be increased. There is no filesystem. A Worker can hold a maximum of 6 simultaneous outgoing connections. Bundle size after gzip is 3 MB on Free and 10 MB on Paid. CPU per invocation defaults to 30 seconds and can be raised to a maximum of 5 minutes on Paid; on Free it is 10 milliseconds.<\/p>\n<p>The limit that catches most teams is a different one. A Worker serving an HTTP request has no hard wall-time limit while the client stays connected. But Cron Triggers, Queue consumers and Durable Object alarm handlers all cap out at 15 minutes of wall time per invocation. The nightly batch job you were going to schedule with a Cron Trigger will simply be cut off. Move that work into a Workflow or a Queue instead.<\/p>\n<p><strong>Use this when<\/strong> the work is request-response and holds no state between calls: API endpoints, server-side rendering, routing, auth checks, image transformation, or short CPU-bound computation.<\/p>\n<p><strong>Do not use this when<\/strong> you need more than 128 MB of memory, you need to write to a filesystem, you need to run a native binary, you need more than 6 concurrent outgoing connections, or you need state that is authoritative across requests.<\/p>\n<h2>Durable Objects: one authoritative point of consistency<\/h2>\n<p>The whole idea of a Durable Object is a guarantee: one key gets exactly one live instance globally. Every request addressed to that key routes to the same instance. In practice this means you get serialisation without an external lock, without Redis, and without a read-then-write race.<\/p>\n<p>That is what makes a genuinely exact rate limiter possible, along with a stock counter that cannot oversell, a chat room, a collaboratively edited document, or a real-time game session. If you are currently using Redis for one of these, we compared the two in <a href=\"https:\/\/www.cipher.co.th\/en\/blogs\/cloudflare-durable-objects-vs-redis\/\">Durable Objects vs Redis<\/a>.<\/p>\n<h3>WebSocket Hibernation changes the economics<\/h3>\n<p>Durable Objects bill compute duration in wall-clock time while the object is actively running or is idle in memory but unable to hibernate. That means holding a WebSocket open naively bills you for the whole time it is connected. The WebSocket Hibernation API solves this by letting the object hibernate while the connection stays open, and Cloudflare documents two pricing examples that differ sharply. An application holding WebSockets open without hibernation is estimated at around 138 USD per month; a broadly similar application using the Hibernation API is estimated at around 10 USD per month. If you are building anything with persistent connections, this is not an optimisation, it is the design.<\/p>\n<p>One scaling limit to plan for: a single object handles traffic up to a soft limit of roughly 1,000 requests per second. If one of your keys is hotter than that, you must shard the key yourself. Durable Objects will not do it for you.<\/p>\n<p><strong>Use this when<\/strong> state must be written and then read back as authoritative, per key: chat rooms, inventory counters per SKU, per-user rate limiters, game sessions, or stateful agents.<\/p>\n<p><strong>Do not use this when<\/strong> the workload is read-heavy and eventual consistency is fine. Routing every read through a single object creates an unnecessary bottleneck. Use KV or R2 instead.<\/p>\n<h2>Containers: for code that cannot run in a Worker<\/h2>\n<p>Containers is the escape hatch. It is where ffmpeg, Pandas, LaTeX, ImageMagick or any native binary that does not compile to WebAssembly goes. You package an image and call it from a Worker.<\/p>\n<p>Billing is per 10 milliseconds that the container is actively running, and it stops when the instance goes to sleep, so scaling to zero is real. On Workers Paid, the included allowances are 25 GiB-hours of memory, 375 vCPU-minutes, and 200 GB-hours of disk, with published per-second rates for overage. Memory and disk are billed on the provisioned resources of the instance type you pick, while CPU is billed on active usage only.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Instance type<\/th>\n<th>vCPU<\/th>\n<th>Memory<\/th>\n<th>Disk<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>lite<\/td>\n<td>1\/16<\/td>\n<td>256 MiB<\/td>\n<td>2 GB<\/td>\n<\/tr>\n<tr>\n<td>basic<\/td>\n<td>1\/4<\/td>\n<td>1 GiB<\/td>\n<td>4 GB<\/td>\n<\/tr>\n<tr>\n<td>standard-1<\/td>\n<td>1\/2<\/td>\n<td>4 GiB<\/td>\n<td>8 GB<\/td>\n<\/tr>\n<tr>\n<td>standard-2<\/td>\n<td>1<\/td>\n<td>6 GiB<\/td>\n<td>12 GB<\/td>\n<\/tr>\n<tr>\n<td>standard-3<\/td>\n<td>2<\/td>\n<td>8 GiB<\/td>\n<td>16 GB<\/td>\n<\/tr>\n<tr>\n<td>standard-4<\/td>\n<td>4<\/td>\n<td>12 GiB<\/td>\n<td>20 GB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>The ceiling is what people miss. The largest instance is standard-4 at 4 vCPU, 12 GiB of memory and 20 GB of disk, and custom instance types are constrained to the same maxima. That is not a machine for running a large language model yourself, or for in-memory analytics that wants tens of gigabytes of RAM. Total container image storage per account is 50 GB. And each container has its own Durable Object behind it, which means you pay for Workers and Durable Objects usage on top of the container itself.<\/p>\n<p><strong>Use this when<\/strong> your code needs a filesystem, a native binary, or more than 128 MB of memory: video transcoding, large file processing, or a Python service with heavy dependencies.<\/p>\n<p><strong>Do not use this when<\/strong> the code is already JavaScript and would run fine on Workers. Moving it to a Container makes it more expensive and reintroduces cold starts for nothing.<\/p>\n<h2>Workflows: durable execution for processes that must not die halfway<\/h2>\n<p>Workflows is the answer when a process has many steps, any step can fail, and you need it to resume from the failed step rather than restart the whole thing. Each step persists its result, retries on its own, and continues from where it stopped.<\/p>\n<p>The numbers that shape the design: Workers Paid gives 10,000 steps per instance by default, configurable up to 25,000. A non-stream step result is capped at 1 MiB, and the event payload is also 1 MiB. State persisted per instance can reach 1 GB on Paid. And step.sleep can pause an instance for up to 365 days.<\/p>\n<p>The most useful property is that instances in a waiting state, whether sleeping, waiting for a retry, or waiting for an event, do not count towards concurrency limits. You can have millions of instances waiting simultaneously; only running instances count against the concurrency ceiling, which is 50,000 on Workers Paid.<\/p>\n<h3>Billing is about to change<\/h3>\n<p>Workflows has been billed exactly like Workers: requests and CPU time, plus storage at 1 GB-month included and 0.20 USD per additional GB-month. On 7 July 2026 Cloudflare announced that Workflows pricing now adds per-step billing, and that step and storage billing will begin no earlier than 10 August 2026. On Workers Paid the allowance is 500,000 steps per month included, then 0.80 USD per additional 100,000 steps; on Workers Free it is 3,000 steps per day.<\/p>\n<p>That changes an idiom. Splitting work into many tiny steps for tidiness starts to have a real cost from August 2026 onward. Choose step boundaries by what genuinely needs to be retried independently, not by what reads nicely.<\/p>\n<p><strong>Use this when<\/strong> a process is multi-step, cannot restart from the beginning, must retry, or must wait a long time between steps: a checkout saga, a customer onboarding sequence, or an ETL pipeline.<\/p>\n<p><strong>Do not use this when<\/strong> the job is a single call finished in one request, or a fire-and-forget task where losing it is acceptable. A plain Worker or a Queue is simpler and cheaper.<\/p>\n<h2>Browser Rendering (now documented as Browser Run)<\/h2>\n<p>When you need a real Chromium browser, this is the product: rendering HTML to PDF, capturing a screenshot, generating an OG image, or crawling a page that only exists after JavaScript runs.<\/p>\n<p>There are two modes with different billing. Quick Actions, the endpoints for screenshot, PDF, markdown and similar, are charged for browser hours only. Browser Sessions, where you drive Puppeteer, Playwright or CDP yourself, are charged for both browser hours and concurrent browsers.<\/p>\n<p>On Workers Paid you get 10 browser hours per month, then 0.09 USD per additional hour, and 10 concurrent browsers averaged monthly, then 2.00 USD per additional browser, with a ceiling of 120 concurrent browsers per account. A browser times out after 60 seconds of inactivity by default, extendable to 10 minutes with the keep_alive option.<\/p>\n<p>The single most common cause of an unexpected bill is failing to call browser.close(). A session that is not explicitly closed keeps running until it times out, and that time is billed as browser hours. Wrap the browser code in try\/finally.<\/p>\n<p><strong>Use this when<\/strong> you genuinely need a rendered DOM: invoices and receipts as PDF from an HTML template, or scraping pages built with client-side JavaScript.<\/p>\n<p><strong>Do not use this when<\/strong> plain HTML would do. Fetching and parsing with a Worker is dramatically faster and cheaper. Launching a full browser to read a static page is burning money.<\/p>\n<h2>Limits that actually matter (as of July 2026)<\/h2>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Service<\/th>\n<th>Billed on<\/th>\n<th>Key limit<\/th>\n<th>Time limit<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Workers<\/td>\n<td>Requests plus CPU time<\/td>\n<td>128 MB memory, 6 outgoing connections, no filesystem<\/td>\n<td>CPU 30 s default, up to 5 min<\/td>\n<\/tr>\n<tr>\n<td>Durable Objects<\/td>\n<td>Requests plus wall-clock duration<\/td>\n<td>One instance per key, soft limit around 1,000 req\/s per object<\/td>\n<td>HTTP and RPC unlimited, alarms 15 min<\/td>\n<\/tr>\n<tr>\n<td>Containers<\/td>\n<td>Every 10 ms actively running<\/td>\n<td>Max 4 vCPU, 12 GiB memory, 20 GB disk<\/td>\n<td>Sleeps when idle (scale to zero)<\/td>\n<\/tr>\n<tr>\n<td>Workflows<\/td>\n<td>Requests, CPU, storage, and steps from Aug 2026<\/td>\n<td>10,000 steps per instance, 1 MiB per step result<\/td>\n<td>Unlimited per step, sleep up to 365 days<\/td>\n<\/tr>\n<tr>\n<td>Browser Rendering<\/td>\n<td>Browser hours plus concurrent browsers<\/td>\n<td>10 hours and 10 concurrent browsers included on Paid<\/td>\n<td>60 s inactivity timeout<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<h2>Decision matrix<\/h2>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>If you need<\/th>\n<th>Use<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>A fast API endpoint or SSR<\/td>\n<td>Workers<\/td>\n<\/tr>\n<tr>\n<td>A counter or decision that must not double-count<\/td>\n<td>Durable Objects<\/td>\n<\/tr>\n<tr>\n<td>WebSocket rooms or presence<\/td>\n<td>Durable Objects with WebSocket Hibernation<\/td>\n<\/tr>\n<tr>\n<td>Rate limiting that has to be exact<\/td>\n<td>Durable Objects<\/td>\n<\/tr>\n<tr>\n<td>To run ffmpeg, Pandas, LaTeX or a native binary<\/td>\n<td>Containers<\/td>\n<\/tr>\n<tr>\n<td>More than 128 MB of memory or a filesystem<\/td>\n<td>Containers<\/td>\n<\/tr>\n<tr>\n<td>A multi-step process that retries and resumes<\/td>\n<td>Workflows<\/td>\n<\/tr>\n<tr>\n<td>To wait hours or days between steps<\/td>\n<td>Workflows (step.sleep)<\/td>\n<\/tr>\n<tr>\n<td>A scheduled job that finishes inside 15 minutes<\/td>\n<td>Cron Trigger on a Worker<\/td>\n<\/tr>\n<tr>\n<td>A scheduled job that runs longer than 15 minutes<\/td>\n<td>Cron Trigger that starts a Workflow<\/td>\n<\/tr>\n<tr>\n<td>PDFs or screenshots<\/td>\n<td>Browser Rendering<\/td>\n<\/tr>\n<tr>\n<td>To crawl a JavaScript-rendered page<\/td>\n<td>Browser Rendering<\/td>\n<\/tr>\n<tr>\n<td>To fetch plain HTML at volume<\/td>\n<td>Workers with fetch (do not launch a browser)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<h2>A worked example: order fulfilment for an e-commerce platform<\/h2>\n<p>Suppose you are building order fulfilment with several moving parts. Here is how it maps onto all five primitives.<\/p>\n<p><strong>One, accepting the order.<\/strong> A plain Worker. It validates auth, checks the payload, and hands off. This runs in a few milliseconds of CPU time and needs nothing else.<\/p>\n<p><strong>Two, decrementing stock.<\/strong> A Durable Object per SKU. This is the part that goes wrong most often. If you decrement in D1 or Postgres without the right lock, you will oversell on the day traffic actually spikes. A Durable Object per SKU funnels every request for that SKU through one queue, so the decrement is exactly correct by construction, not by careful SQL.<\/p>\n<p><strong>Three, the fulfilment process.<\/strong> A Workflow. Charge the card, issue the invoice, generate the shipping label, notify the warehouse, send an email, and one day later send a satisfaction survey. That last step is a step.sleep of one day, which a 15-minute Cron Trigger simply cannot express, and an instance that is sleeping does not consume a concurrency slot.<\/p>\n<p><strong>Four, the invoice PDF.<\/strong> Browser Rendering. Render the HTML template you already maintain, export it as PDF, and put it in R2.<\/p>\n<p><strong>Five, product image processing.<\/strong> A Container. Run the team&#8217;s existing image library, which is written in Python and does not compile to WebAssembly.<\/p>\n<p>Notice that no primitive does another primitive&#8217;s job well. If you try to write the whole saga inside a single Worker with setTimeout, you will hit the CPU limit or lose all your state the moment the Worker is evicted.<\/p>\n<h2>When another platform wins<\/h2>\n<p>There are cases where Cloudflare compute is not the best answer, and it is worth being honest about them.<\/p>\n<p>If your job is CPU-heavy and runs longer than 5 minutes per invocation, or it needs a GPU for training a model, it belongs on EC2, GKE or your own cluster. Cloudflare Containers cap at 4 vCPU and 12 GiB, which is smaller than a mid-sized instance from any of the large clouds.<\/p>\n<p>If your team already runs Kubernetes well, understands it, and has tooling built around it, moving to Cloudflare purely for latency may not be worth the migration cost, and you should be honest that you would be trading one host lock-in for another.<\/p>\n<p>And if your work is analytics over large volumes of data that already sit in a warehouse, moving the compute to where the data is beats pulling the data to the edge.<\/p>\n<h2>Summary<\/h2>\n<p>The right question is not whether Cloudflare compute is better than the alternative. It is whether this specific piece of work holds state, needs a single point of consistency, runs longer than 15 minutes, and needs a native binary. Answer those four and the primitive picks itself.<\/p>\n<p>Cipher designs and implements systems on the Cloudflare Developer Platform for businesses in Thailand, and can assess an existing architecture to say which workload belongs on which primitive.<\/p>\n<h2>Frequently Asked Questions<\/h2>\n<h3>What is the difference between Workers and Durable Objects?<\/h3>\n<p>A Worker can run any request on any machine in the network, so it is a poor fit for state that must stay consistent. A Durable Object guarantees that one key has exactly one live instance globally, so all requests touching that state are serialised automatically. That makes it the right choice for chat rooms, counters and rate limiters.<\/p>\n<h3>How is Workflows billed?<\/h3>\n<p>Workflows has been billed like Workers, on requests and CPU time, plus storage with 1 GB-month included and 0.20 USD per additional GB-month. On 7 July 2026 Cloudflare added per-step billing, with step and storage billing starting no earlier than 10 August 2026. Workers Paid includes 500,000 steps per month, then 0.80 USD per additional 100,000 steps.<\/p>\n<h3>When should I use Containers instead of Workers?<\/h3>\n<p>Use Containers when your code needs something Workers cannot provide: a filesystem, a native binary, or more than 128 MB of memory. Typical examples are ffmpeg, Pandas, LaTeX and ImageMagick. If your code is already JavaScript and runs fine on Workers, moving it to a Container only adds cost and reintroduces cold starts.<\/p>\n<h3>How long can a Cron Trigger run on Workers?<\/h3>\n<p>Cron Triggers have a maximum wall time of 15 minutes per invocation, the same ceiling as Queue consumers and Durable Object alarm handlers. If your batch job runs longer than that, use the cron to start a Workflow and let the Workflow do the actual work.<\/p>\n<h3>Is Browser Rendering the same thing as Browser Run?<\/h3>\n<p>They are the same service. Cloudflare documentation now refers to it as Browser Run. On Workers Paid you get 10 browser hours per month, then 0.09 USD per additional hour. The most common cause of an unexpected bill is failing to call browser.close(), which leaves the session running until it times out.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Quick answer: Use Workers as the default for stateless request-response work such as APIs, SSR and middleware. Use Durable Objects [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_angie_page":false,"content-type":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"page_builder":"","footnotes":""},"categories":[152],"tags":[],"class_list":["post-18139","post","type-post","status-publish","format-standard","hentry","category-technology-en"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.cipher.co.th\/en\/wp-json\/wp\/v2\/posts\/18139","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cipher.co.th\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cipher.co.th\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cipher.co.th\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cipher.co.th\/en\/wp-json\/wp\/v2\/comments?post=18139"}],"version-history":[{"count":0,"href":"https:\/\/www.cipher.co.th\/en\/wp-json\/wp\/v2\/posts\/18139\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.cipher.co.th\/en\/wp-json\/wp\/v2\/media?parent=18139"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cipher.co.th\/en\/wp-json\/wp\/v2\/categories?post=18139"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cipher.co.th\/en\/wp-json\/wp\/v2\/tags?post=18139"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}