Ben Price

Introducing DashHost: Managed Hosting for EmDash

· 13 min read · Building in Public

Introducing DashHost: Managed Hosting for EmDash

I have a new project I have been quietly building: DashHost.

DashHost is a managed hosting platform for EmDash, Cloudflare’s new open-source CMS. You sign up, pick a template, name your site, and a minute or two later you have a working EmDash site on the internet. No Cloudflare dashboard, no terminal, no config files.

As of this week, DashHost is the first platform to offer managed hosting for EmDash. I announced it on the official EmDash GitHub discussions here, and on X here. The reception so far has been genuinely encouraging.

This post is the longer version: why I built it, what it actually is, how it is put together, and where things stand today.


Why I started building it

On April 1, 2026, Cloudflare launched EmDash.

If you missed it: EmDash is a serverless, TypeScript-based CMS built on Cloudflare Workers. It is positioned as a modern successor to WordPress, with sandboxed plugins, built-in SEO, a WordPress import flow, passkey auth, AI-native content tooling, and an architecture that actually fits the edge instead of fighting it. It picked up momentum almost immediately: over a thousand GitHub stars in the first twenty-four hours, a public endorsement from Joost de Valk of Yoast, and a growing pile of people wanting to try it.

I was one of them, and I have stayed one of them. I have been actively contributing back to the EmDash project itself alongside building DashHost on top of it. The two efforts inform each other. Things I learn from running EmDash as a hosting provider feed back into EmDash contributions, and things I see in the EmDash codebase shape the platform I am building on it.

I see where this is going

The honest core of the motivation is this: I think EmDash is going to matter.

Not in a vague “modern stack is better” way, but in a very specific way. WordPress has been the default CMS for two decades, and the reason it is still the default is not that it is good. It is that it is the path of least resistance for a huge population of users and agencies. The cost is well-known: security issues, plugin rot, performance problems, a deeply dated admin experience, and a hosting industry built around mitigating all of that.

EmDash is the first project I have seen in a long time that has a credible shot at changing the default. It starts from the modern web platform instead of bolting modern ideas onto an older one. The plugin sandbox is a real security model, not a promise. The edge-native runtime means fast sites are the baseline, not a performance optimization you pay extra for. The WordPress import path lowers the cost of migration. Cloudflare is behind it.

I believe in the direction enough that I wanted to build inside that world, not just watch it from the outside.

The managed hosting gap, and the race to fill it

EmDash launching without a managed hosting option was an obvious gap.

EmDash today is very much a developer tool. The path to running a site is npm create emdash@latest, or a Deploy-to-Cloudflare button that drops you into the Cloudflare dashboard to stitch together a Worker, a D1 database, an R2 bucket, and a KV namespace. If you are comfortable in that environment, it is genuinely nice. If you are not, it is a wall.

That is the same gap WordPress had for most of its life, and it is the gap that WordPress managed hosting filled. WP Engine, Kinsta, Flywheel, and friends grew into a $5B-plus market by answering one question for normal humans: “How do I actually run this thing without becoming a sysadmin?”

The moment EmDash launched, I knew that gap would exist, and I knew someone was going to fill it. I also thought I had a real shot at being first. I already had the Cloudflare stack in my head from other projects. I already understood Workers for Platforms as a tenant-isolation primitive. I had a decent read on the product shape a managed EmDash host would need.

So I moved fast.

A few days of focused work later, DashHost had a landing page, a signup flow, an end-to-end provisioning pipeline, custom domains, billing, usage metering, and an admin console. This week it went public, and it is the first managed hosting platform for EmDash on the market.

Being first to market is not a moat. It is a head start. But it is a meaningful one in a category where the incumbents (WordPress managed hosting) are slow to react and the ecosystem (EmDash users looking for a home) is growing quickly. I intend to use that head start to build something worth staying on once other options show up.


A thin platform layer, on purpose

Here is the thing that shaped the whole product: EmDash is already really good at the CMS work.

The admin panel is there. The WordPress import is there. Sandboxed plugins are there. SEO, auth, media, search, i18n, all there. EmDash is not a project that needs me to rebuild any of that on top of it. If I tried, I would mostly just be adding drift and bugs. And given that I am also contributing upstream to EmDash, any fork-style duplication I did on top would actively make my own upstream work harder.

So DashHost is deliberately thin.

DashHost handles the boring, annoying, infrastructure-shaped problems:

  • provisioning the Worker, D1, R2, and KV behind each site
  • routing traffic to the right tenant Worker
  • custom domains and SSL
  • billing and subscriptions
  • usage metering and overage protection
  • a dashboard to see and manage your sites

And EmDash handles everything else. The CMS is the CMS.

That split is the product principle. DashHost is a platform layer, not a fork of EmDash and not a competitor to it. The goal is for EmDash users to feel like DashHost simply removes the parts of EmDash that are not fun and leaves the parts that are.


What DashHost actually is

At the user level, the flow is about as simple as I could make it:

  1. Sign up with a magic link. No credit card.
  2. Pick a template (blog, marketing, portfolio, or a blank starter).
  3. Choose a subdomain, something like your-site.dashhost.cc.
  4. Click create.
  5. Watch a progress indicator for a minute or two while DashHost provisions a full EmDash stack behind the scenes.
  6. Land on your new site, with an admin panel you can log into.

From there, on any paid plan, you can connect a custom domain, and Cloudflare handles the SSL certificate for you automatically.

The goal I set for myself on the timing was “under three minutes from signup to a working site.” In practice it has been faster than that for most of my test runs, which is nice, but three minutes is the number I am willing to commit to publicly and defend when things are under load.

The pieces a user never sees

Behind that simple flow, a lot is happening. Each site gets its own isolated stack:

ResourceWhat it isWhy it is per-tenant
WorkerThe actual EmDash runtimeSo each site runs its own code path
D1 databaseSite content, users, settingsHard data isolation between tenants
R2 bucketMedia and uploadsStorage isolation and clean usage accounting
KV namespaceSite-level cache and configFast, per-tenant lookups at the edge

No shared databases. No shared buckets. No “we isolate at the application layer, trust us.” Each tenant gets a real Cloudflare resource boundary.

That is more work to provision, and more work to meter, but it is worth it. When things go wrong, I want the blast radius to be one site, not the whole platform.


Architecture

The stack is deliberately Cloudflare-native. DashHost is a small monorepo with three Workers:

AppResponsibilityRuntime
PlatformLanding, pricing, auth, dashboard, provisioning API, billingAstro SSR on Cloudflare Workers
DispatchRouting for *.dashhost.cc, rate limits, usage metering, free-tier footer injectionCloudflare Worker + Workers for Platforms
AggregationDaily reconciliation of tenant usage against billingCron-triggered Worker

The platform is where users live. The dispatch Worker is where their visitors live. The aggregation Worker is where the billing truth gets settled.

Provisioning

When you click “create site,” the platform Worker calls the Cloudflare REST API to create a D1 database, an R2 bucket, and a KV namespace. It then deploys a pre-built EmDash template bundle into a Workers for Platforms dispatch namespace, binds the new resources to that Worker, runs the EmDash schema migration and content seed against D1, and creates your first EmDash admin user.

The step that took the most thought was the idempotency story. Provisioning has a lot of moving parts, and any of them can fail. If the D1 database is created but the R2 bucket fails, you do not want an orphaned database sitting around forever. So each step is tracked, and on failure the pipeline cleans up the resources it has already created before surfacing the error. The user sees a clean failure and a retry button, not a ghost site.

Behind the scenes, there is a small state machine that walks through the steps: pending, creating_d1, creating_r2, creating_kv, deploying_worker, binding_resources, running_init, running_seed, creating_admin_user, active. The dashboard polls this state and shows human-friendly labels. That turned out to be one of the small details I am most happy with. The progress bar is real. It is not a fake animation pretending something is happening while a single 90-second API call blocks in the background.

Routing

Once a site exists, traffic to your-site.dashhost.cc hits the dispatch Worker.

The dispatch Worker does a KV lookup to figure out which tenant Worker to hand the request to, then uses Workers for Platforms to dispatch into the tenant’s isolated environment. That is where the per-tenant Worker, D1, R2, and KV bindings live.

Custom domains work through Cloudflare for SaaS. When a user adds yoursite.com, DashHost calls the Cloudflare for SaaS API to create a custom hostname, surfaces the CNAME and TXT validation records back to the user, polls verification, and lets Cloudflare handle the SSL certificate automatically. On the user’s side this feels like “paste the records into your DNS, wait a minute, done.” On my side this is one of the bigger reasons I am on Cloudflare at all.

Usage metering and guardrails

One of my hard requirements for DashHost was that users always know their maximum possible bill. I have been burned enough times by services where a spike in traffic turns into a bill of unknown size, and I have watched enough other people get burned the same way, that I did not want to ship a product with that shape.

Usage metering lives in two layers:

  • Real time. The dispatch Worker uses Durable Objects to track bandwidth and free-tier rate limits in the hot path. If you are on the free tier and you get hugged to death, the dispatch Worker notices immediately and serves a cached response, not an overage invoice. If you are on a paid plan and you have set an overage cap, the dispatch Worker enforces it at the edge.
  • Daily reconciliation. The aggregation Worker runs at 03:00 UTC on a cron trigger and reconciles Durable Object counters against Workers Analytics Engine and R2 storage size. The reconciled numbers go into D1 and get pushed to Polar as usage events for billing.

The real-time layer is what stops surprise bills. The daily layer is what makes them correct.

The Polar call

One sub-decision worth naming: billing is on Polar, not Stripe.

My original plan was Stripe, because Stripe is the default assumption. As I got closer to implementation I kept running into friction around merchant of record, VAT handling, international tax, and global pricing. Polar handles all of that as the merchant of record, which is a meaningful simplification for a small operation serving a global audience.

I will probably write a longer post at some point comparing the two for small, indie-friendly SaaS. For now it is enough to say: Polar is the billing rail, and the switch was one of the small but real quality-of-life improvements along the way.

Why Cloudflare, again

I gave a longer version of this answer in my Flags.md post, so I will keep it short here. DashHost is a workload that rewards edge execution. Requests to tenant sites need to be fast and close to users. Per-tenant isolation needs to be real. Global SSL needs to be automatic. Workers for Platforms gives me a clean tenant-isolation model that I would have had to reinvent poorly on any other stack.

And perhaps the least glamorous reason, which is still one of the biggest: Cloudflare’s cold-start story. I can ship a product where every tenant Worker responds in single-digit milliseconds at the edge without needing to keep hot instances running. For a multi-tenant hosting product, that economic model is the difference between a viable small business and a science experiment.


Pricing

Pricing for DashHost is something I thought about carefully, because this is a market where the incumbents charge quite a lot, and the whole point of building this was to make EmDash easy to run without that weight.

What I wanted it to feel like

My rough rules for myself:

  • Free tier has to be real. Actually useful for small personal sites, not just a teaser.
  • Paid plans have to be obviously cheaper than WP Engine and Kinsta for comparable sites.
  • No surprise bills. Ever. Overage caps must be real and must be enforced at the edge.
  • Custom domains are a paid feature, but available on the cheapest paid tier.
  • Users should always understand what they are paying for.

The ladder

PlanPriceSitesStorageBandwidthCustom domains
Free$011 GB5 GB / mo0
Starter$19 / mo110 GB50 GB / mo1
Growth$39 / mo550 GB250 GB / mo5
Business$79 / mo20200 GB1 TB / mo20

All paid plans include an annual billing option that works out to one month free.

Overages, handled properly

The free tier has no overages. If you hit your limit, the site goes into cache-only mode and visitors see cached content, not a bill. That is the guarantee.

On paid plans, overages exist, but they are strictly capped:

  • Bandwidth: $0.40 per GB beyond plan
  • Storage: $0.75 per GB per month beyond plan

And each plan has a user-adjustable overage cap. The default caps are modest. You can raise them if you want more headroom, but you cannot accidentally wake up to a bill ten times your normal monthly spend. When you hit your cap, the dispatch Worker flips your site into cache-only mode and the dashboard prompts you to raise the cap or upgrade. That is it. There is no path to a runaway invoice.

I also added a 60-day money-back guarantee on all paid plans, because I genuinely want people to try the product, and I would rather give a refund to someone who decides it is not for them than have them feel trapped by a subscription.

The comparison pages

I wrote out explicit comparison pages for vs/wp-engine, vs/kinsta, and vs/cloudways. These are not marketing hit pieces. They are honest comparisons of what each platform offers, what DashHost offers, and where DashHost is cheaper or more expensive. I want a user who is currently paying WP Engine $25 a month for a blog to be able to land on that page and see, in plain English, what they would get and what they would give up by switching.

Spoiler: for most small WordPress sites, the answer is “pay less, get a faster site, get a cleaner admin panel, lose WordPress plugins you are probably not using anyway.” That is the pitch.


Where things stand today

DashHost is in early access as of this week.

That means:

  • The core flows work end to end. You can sign up, create a site, connect a custom domain, hit your plan limits, and get billed.
  • The platform, dispatch, and aggregation Workers are all deployed.
  • There are 52 tests across the platform and dispatch Workers covering things like slug validation, plan constants, CSRF, admin guard, CF API, domains, rate limiting, subdomain extraction, and usage metering. Not enough. More coming.
  • The admin console (internal tooling for me to look at tenants, activity, and platform health) is running at a separate subdomain.
  • There is a landing page, pricing page, vs-competitor pages, signup, login, dashboard, site creation, custom domains, billing, and account pages.

And also:

  • It is early. Expect rough edges. I have a warning banner on the landing page that says exactly that. I am not suggesting anyone run a mission-critical production site on DashHost today. I am suggesting it is a great time to kick the tires, file bugs, and tell me what is broken.
  • Some planned features are still ahead of me. Passkey auth on the platform. WordPress import polish. Plugin marketplace surfaces. Extra-site billing add-ons.
  • EmDash itself is also in early access, and some EmDash features are still being built out. DashHost’s fate is tied to EmDash’s, and I am comfortable with that bet. I am also helping shape EmDash’s direction from the inside as a contributor, which makes the dependency feel less like a risk and more like a collaboration.

How you can help

If you want to kick the tires, the free tier exists for exactly this. Sign up, create a site, poke at the admin panel, try to break things, and tell me what you find.

If something is broken, or something is confusing, or something feels slow, I want to know. I am actively iterating on this during early access and small pieces of feedback from real users are worth more to me right now than almost anything else.

And if you are already an EmDash user who has been rolling your own Cloudflare setup, I would especially love to hear from you. You are the person I am building this for, and I want to know exactly which parts of “rolling it yourself” are the ones you would most like to stop doing.

You can also follow along on the EmDash discussion thread or on X where I posted the original launch.


Wrapping up

DashHost is a product I wanted to exist, did not see anyone else building, and moved quickly to build.

The pitch is short:

  • EmDash is really good, and I think it is going to matter.
  • EmDash is hard to run if you are not a developer, and still slightly annoying if you are.
  • DashHost fixes that, without getting in EmDash’s way.
  • Pricing is fair. Overages are real, but capped. The free tier is actually free.
  • It is first to market. It is early. It works. It will get better.

If that sounds interesting, come try it: dashhost.io.

More posts on specific pieces of this are coming. Likely next: a closer look at Workers for Platforms as a tenant-isolation primitive, and a write-up on Polar as the billing rail for an indie SaaS. Both are decisions I spent more time on than the final implementation would suggest, and both are the kind of thing I wish more building-in-public posts actually explained.

Until then, the blog revival continues.


New posts delivered to your inbox. No spam, unsubscribe anytime.