{"id":98109,"date":"2026-10-14T19:50:00","date_gmt":"2026-10-14T23:50:00","guid":{"rendered":"https:\/\/overcentral.com\/en\/?p=98109"},"modified":"2026-09-29T08:04:42","modified_gmt":"2026-09-29T12:04:42","slug":"emdash-cms-billing-surprise-bills-98109","status":"publish","type":"post","link":"https:\/\/overcentral.com\/en\/emdash-cms-billing-surprise-bills-98109\/","title":{"rendered":"EmDash CMS: Why CPU Time Limits Won&#8217;t Save You From Surprise Bills"},"content":{"rendered":"<p>Everyone says the same thing about serverless billing: set CPU time limits, configure rate limiting, and you&#8217;re safe.<\/p>\n<p>That advice is dangerously incomplete for EmDash.<\/p>\n<p>CPU time limits cap how long <em>one<\/em> request can run. They do nothing about how <em>many<\/em> requests hit your site. And EmDash, built on <a href=\"https:\/\/workers.cloudflare.com\/\" target=\"_blank\" rel=\"noopener noreferrer\" data-iacss-external=\"1\">Cloudflare Workers<\/a>, meters every single interaction \u2014 page views, admin clicks, API calls, plugin executions \u2014 across multiple billing dimensions simultaneously. A single page view can trigger Workers compute, D1 database reads, R2 storage operations, and KV lookups. Each one bills separately.<\/p>\n<p>The common advice treats serverless cost control as a single-lever problem. It&#8217;s not. EmDash&#8217;s architecture demands a fundamentally different optimization strategy.<\/p>\n<h2>How EmDash Billing Actually Works<\/h2>\n<p>EmDash runs on Cloudflare Workers using V8 isolates. Every plugin executes inside a dynamic worker \u2014 a sandboxed environment that spins up in milliseconds and spins down when idle. The paid plan starts at $5 per month and includes 10 million requests. Beyond that, you pay $0.30 per additional million requests plus CPU time.<\/p>\n<p>But here&#8217;s what most guides skip: CPU time is only one of five billing meters EmDash can hit per request.<\/p>\n<ul>\n<li><strong>Workers<\/strong>: billed per request and per CPU millisecond<\/li>\n<li><strong>D1<\/strong>: billed per database read and write<\/li>\n<li><strong>R2<\/strong>: billed per storage operation<\/li>\n<li><strong>KV<\/strong>: billed per read and write<\/li>\n<li><strong>Workers AI<\/strong>: billed per inference call (if used)<\/li>\n<\/ul>\n<p>A plugin that sends an email notification on post publish might trigger a Workers invocation, a D1 read (checking post metadata), and an external API call. That&#8217;s three meters from one action.<\/p>\n<p>CPU time limits control only the Workers compute duration. They don&#8217;t prevent a bot attack from firing 10,000 requests <a href=\"https:\/\/overcentral.com\/en\/meta-launches-zgateway-proxy-handles-1-billion-ops-per-second\/\" title=\"Meta Launches ZGateway Proxy, Handles 1 Billion Ops Per Second\" data-iacss-internal=\"1\">per second<\/a>. Cloudflare offers WAF rate limiting, but that&#8217;s per-IP, not a global cap. A distributed attack from thousands of IPs goes right through it.<\/p>\n<p>Based on documented Cloudflare forum reports, a basic DDoS simulation of 10,000 IPs making one request per second each can generate 26 billion billable requests in a month. At $0.30 per million overage, that&#8217;s roughly $7,800 in Workers charges alone \u2014 before database, storage, or KV costs. No spending cap exists.<\/p>\n<h2>The Real Optimization Levers<\/h2>\n<h3>1. Reduce Invocation Frequency, Not Just Duration<\/h3>\n<p>Most tutorials focus on making each request faster. That helps, but the bigger lever is reducing how many requests happen at all.<\/p>\n<p><strong>Cache aggressively.<\/strong> EmDash themes built on Astro can use static site generation (SSG). Every page that renders at build time eliminates a Workers invocation. For blog content, portfolio pages, and marketing sites, SSG should be the default. Only use server-side rendering (SSR) for genuinely dynamic pages.<\/p>\n<p><strong>Batch database operations.<\/strong> A plugin that checks post status on every save should read once, cache the result, and reuse it. Each redundant D1 read costs money.<\/p>\n<p><strong>Audit plugin hooks.<\/strong> Every registered hook fires a dynamic worker. If a plugin registers a hook on <code>post:save<\/code>codecodecode but only needs to act on <code>post:publish<\/code>codecodecode, you&#8217;re paying for invocations that do nothing. Remove unnecessary hook registrations.<\/p>\n<h3>2. Scope Plugin Capabilities Tightly<\/h3>\n<p>EmDash&#8217;s sandbox model requires plugins to declare capabilities in a manifest. A plugin that declares <code>read content<\/code>codecodecode and <code>email sent<\/code>codecodecode can literally do nothing else.<\/p>\n<p>This isn&#8217;t just a security feature \u2014 it&#8217;s a cost control mechanism. A plugin with narrowly scoped capabilities cannot accidentally trigger expensive operations it doesn&#8217;t need. If a comment moderation plugin declares <code>read content<\/code>codecodecode but not <code>write content<\/code>codecodecode, it cannot make database writes. That caps its billing impact.<\/p>\n<p>Review every plugin&#8217;s capability manifest. If a plugin requests capabilities it doesn&#8217;t use, remove them. Each unneeded capability is a potential billing vector.<\/p>\n<h3>3. Use the Free Tier for Non-Critical Paths<\/h3>\n<p>EmDash on Cloudflare&#8217;s free tier supports Workers, D1, R2, and KV \u2014 but with lower limits and no dynamic workers. For non-critical plugins that don&#8217;t need sandbox isolation, running them in-process on the free tier is viable.<\/p>\n<p>The caveat: dynamic workers (the sandbox feature) require the $5\/month paid plan. If a plugin doesn&#8217;t handle sensitive data or critical operations, consider whether it needs the sandbox at all. A simple analytics plugin that only reads content and logs page views can run in-process without meaningful security risk.<\/p>\n<h3>4. Monitor the Right Metrics<\/h3>\n<p>Cloudflare&#8217;s dashboard shows Workers invocations and CPU time. But <a href=\"https:\/\/overcentral.com\/en\/wardogs-recon-myth-busted-81574\/\" title=\"The WARDOGS Recon Myth You Need to Stop Believing\" data-iacss-internal=\"1\">you need to<\/a> track per-plugin costs, not just aggregate numbers.<\/p>\n<p><strong>Enable Workers Trace.<\/strong> This logs every invocation with duration, status, and metadata. Review it weekly to identify plugins with high invocation counts or abnormal CPU time.<\/p>\n<p><strong>Set CPU time alerts.<\/strong> Cloudflare allows per-worker CPU time limits. Set them at the individual plugin level, not globally. A plugin that consistently hits its limit needs optimization or removal.<\/p>\n<p><strong>Watch D1 query counts.<\/strong> Database reads are a hidden cost multiplier. A plugin that queries D1 on every page load will dominate your bill even if its CPU time is negligible.<\/p>\n<h2>The Edge Case Where Standard Advice Fails<\/h2>\n<p>The most common advice \u2014 &#8220;set CPU time limits and use rate limiting&#8221; \u2014 fails completely for plugins that make external API calls.<\/p>\n<p>Consider a plugin that fetches weather data for a location page. It declares <code>read content<\/code>codecodecode and <code>external network access<\/code>codecodecode. The CPU time per request might be 5 milliseconds. But the external API call takes 200 milliseconds. Workers bill CPU time at the worker level, not wall-clock time. So the CPU time limit doesn&#8217;t constrain the API call&#8217;s latency cost.<\/p>\n<p>Worse: if the external API is slow or unresponsive, the plugin may retry. Each retry is a new Workers invocation. CPU time limits won&#8217;t stop retry storms.<\/p>\n<p>The fix: implement explicit timeouts and retry limits inside the plugin code. EmDash&#8217;s plugin API doesn&#8217;t enforce these by default. You have to write them.<\/p>\n<h2>What a Production EmDash Cost Strategy Actually Looks Like<\/h2>\n<p>For a small blog on EmDash&#8217;s free tier, monthly costs stay near zero \u2014 roughly $15\/year for a domain name. For a site with 100,000 daily visits on the $5 plan, Workers requests typically consume 3-4% of the monthly allowance. That&#8217;s sustainable.<\/p>\n<p>But for sites with high plugin usage, complex content types, or external API dependencies, costs scale non-linearly. Each plugin adds a billing dimension. Each hook adds invocation surface area.<\/p>\n<p>The production strategy:<\/p>\n<ol>\n<li>Default to SSG for all content pages<\/li>\n<li>Audit plugin capability manifests quarterly<\/li>\n<li>Cache aggressively at the Workers level<\/li>\n<li>Set per-plugin CPU time limits, not global ones<\/li>\n<li>Implement retry limits and timeouts in every plugin that makes external calls<\/li>\n<li>Monitor Workers Trace weekly for anomaly detection<\/li>\n<\/ol>\n<p>CPU time limits are one tool. They are not a strategy.<\/p>\n<h2>The Uncomfortable Truth About Serverless CMS Billing<\/h2>\n<p>EmDash&#8217;s architecture is genuinely innovative. The plugin sandbox model solves a structural security problem that WordPress has never addressed. The performance characteristics \u2014 V8 isolates starting in milliseconds, scaling to zero, paying only for what you use \u2014 are compelling.<\/p>\n<p>But the billing model shifts risk from the hosting provider to the site owner. With WordPress, a traffic spike might crash your server. Your bill stays the same. With EmDash, a traffic spike never crashes your server. Your bill climbs without a ceiling.<\/p>\n<p>That trade-off is worth making if you understand it and plan for it. The common advice \u2014 &#8220;just set CPU time limits&#8221; \u2014 treats a symptom, not the cause. The cause is architectural: EmDash meters every operation individually. Until you optimize at that level, your bill is a guess, not a budget.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Everyone says the same thing about serverless billing: set CPU time limits, configure rate limiting, and you&#8217;re safe. That advice is dangerously incomplete for EmDash. CPU time limits cap how long one request can run. They do nothing about how many requests hit your site. And EmDash, built on Cloudflare Workers, meters every single interaction [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":100539,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"fifu_image_url":"https:\/\/cards.overcentral.com\/cards\/en\/98109.png","fifu_image_alt":"EmDash CMS: Why CPU Time Limits Won't Save You From Surprise Bills","footnotes":""},"categories":[31],"tags":[],"class_list":["post-98109","post","type-post","status-publish","format-standard","has-post-thumbnail","category-technology"],"fifu_image_url":"https:\/\/cards.overcentral.com\/cards\/en\/98109.png","fifu_image_alt":"EmDash CMS: Why CPU Time Limits Won't Save You From Surprise Bills","_links":{"self":[{"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/posts\/98109","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/comments?post=98109"}],"version-history":[{"count":1,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/posts\/98109\/revisions"}],"predecessor-version":[{"id":100540,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/posts\/98109\/revisions\/100540"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/media\/100539"}],"wp:attachment":[{"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/media?parent=98109"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/categories?post=98109"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/overcentral.com\/en\/wp-json\/wp\/v2\/tags?post=98109"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}