bitdoze.com · review + tutorial walkthrough
EM—DASH
Cloudflare's WordPress successor.
1.0 is out.
Shipped Apr 1, 2026 → 1.0 late Sep
MIT · TypeScript
Astro-native
$0 to try · no cloud account
Two articles, one story: what EmDash is after six months — and your first site in 30 minutes.
01 · What is EmDash
One deployment.
Everything inside.
CMS + site
Astro site, admin at /_emdash/admin, REST API, media library — one app, one deploy
Portable Text
Structured JSON, not HTML blobs. TipTap editor. WordPress Gutenberg converter included
Your DB
Kysely adapters: D1 · SQLite · libSQL · Postgres. Media on R2, any S3, or local disk
Plus an MCP server in every install (~60 agent tools) and passkey-first auth. Not a page builder — Astro components own the HTML.
02 · The architecture question
Where does content live?
In the database. Queried live.
File-based collections
EmDash Live Collections
Markdown in your repo, resolved at build time
Content in DB, queried at request time
Editor publishes → rebuild (minutes)
Editor publishes → live on next request
Site is static output
Site is server-rendered — the WordPress model, on Astro
0 rebuilds on edit
output: "server" is what makes it work
Prerendered pages stay frozen until rebuild
03 · Six-month health check · Sep 2026
The 0.x beta is gone.
This is a real project now.
0
GitHub stars · 1.2k forks
0
contributors · 800+ on Discord
0
signed releases · npm provenance · SLSA
25 UI languages
04 · Production proof
The Cloudflare Blog runs on it.
Since August 12.
0 RPS
steady state · spikes above 5,000 RPS
0 RPS
DDoS absorbed at the edge — not by EmDash
~0
cache hit rate · 99.5% of static files
0
pageviews · 28 posts in 9 days (Agents Week)
Honest caveat: their stack adds KV object cache + Workers Cache + Hyperdrive. Your blog will not need that.
05 · The genuinely novel part
96% of WordPress security issues
start in plugins. EmDash's answer:
1Capability manifests — capabilities: ["content:read", "email:send"] · you approve at install, like an app store
2Sandboxed runtime — workerd child process · 128 MB · 30 s wall time · no network to undeclared hosts
3Signed decentralized registry — built on AT Protocol · same protocol as Bluesky · no one can silently rewrite a plugin's history
4Physical enforcement — not honor-system · hook timeout? logged, request continues without the plugin
06 · The honest cons
What April's critics got right.
Ecosystem catch-22registry is real but the catalog is tinyvs 60k+ WordPress plugins · still the core risk
You own the databasebackups, upgrades, media storage — the docs say so in plain wordsthe ops burden is the product
Astro-onlycontent-in-git users are explicitly not the targethard requirement
Admin UX is youngCloudflare's own migration logged editor bugs in publicbetter than v0.1, still maturing
07 · Two deploy roads
Cloudflare all-in, or a €4 VPS.
$0
Workers Free
site + admin + D1 + R2 free allowances
no sandboxed plugins
~$5/mo
Workers Paid
Worker Loader → sandboxed plugins + registry installs
most platform coupling
€4–5/mo
Your VPS
Node 22 + SQLite + Docker + S3 backups
full control, boring ownership
Official Dockerfile + compose ship in the docs — drops straight into Dokploy/Coolify. Verify pricing before it goes in a client proposal.
08 · Ops footguns · straight from the docs
Six things that will bite you.
Forget react() in configthe admin is a React app — packages alone aren't enoughadmin hangs on "Loading EmDash…"
workerd crash loopsandboxed plugins on Node5 crashes / 60 s → sandbox gives up, site keeps serving
Restore DB without the keyEMDASH_ENCRYPTION_KEY lives outside backups, on purposeplugin secrets unreadable
Migrations auto-applydefault mode runs them on first request after deployrun npx emdash migrate --check in CI first
Scheduled publishingdid not work at all until v0.19.0CF found it mid-migration — "1.0 ≠ mature"
A green /healthproves Astro serves requests — nothing morenot a deploy gate
02
Build your first site.
15–30 minutes
$0 — no cloud account
Node.js ≥ 22.16
SQLite on your laptop
Blank terminal → published post → your first getEmDashCollection("posts") query.
09 · Scaffold
Five prompts. That's the whole setup.
my-emdash-site — zsh
$ npm create emdash@latest
◇ Project name? my-emdash-site
◇ Where will you deploy? Node.js ← SQLite + local uploads (Cloudflare = Part 3)
◇ Which template? Blog ← seeds Posts, Pages, sample content
◇ Package manager? npm
◇ Install dependencies? Yes · 750 packages · wrote EMDASH_ENCRYPTION_KEY to .env
— E M D A S H — v1.0.1
› Admin UI http://127.0.0.1:4321/_emdash/admin
› MCP server http://127.0.0.1:4321/_emdash/api/mcp
curl localhost:4321 → 200
.env is generated + gitignored — never commit it
10 · The admin panel
Setup wizard: three screens.
1Site info · title + tagline · keep "include sample content" checked
2Your account · first user is always Admin · roles: Subscriber → Contributor → Author → Editor
3Register a passkey · Touch ID / Windows Hello / security key · up to 10 per user
Content · Manage · Admin — three sidebar groups
Save = private draft · Publish changes = live
Passkeys are domain-bound — register a backup
11 · The demo moment
Edit → publish → live. No rebuild.
1Open the sample Welcome post
2Rename it Hello from EmDash → Save (now a draft)
3Publish changes
4Reload localhost:4321 → it's there · dev server never restarted · astro build never ran
src/pages/index.astro
---
import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts", { limit: 7 });
// queried at request time → next page load sees the next version
---
12 · The mental model
Four files explain the whole project.
astro.config.mjsoutput: "server" · react() · emdash({ database, storage })react() is mandatory — classic footgun
src/live.config.tssix lines: defineLiveCollection({ loader: emdashLoader() })the wire between DB and Astro
seed/seed.jsoncollections, fields, sample contentthe schema lives in git — content does not
.envEMDASH_ENCRYPTION_KEY — encrypts plugin secrets at restbackup tripod: data.db + uploads/ + key
13 · Verdict
Who should actually use it?
Astro developerwanted a DB-backed CMS on Astro? this is the defaultYesMCP + npx emdash types = agent-friendly
Agencytemplates for handoff · sandbox = support blast-radius controlConditionalnew builds you control — not WP-plugin-store clients
WordPress refugeecontent imports real: WXR / REST · CPTs → collectionsConditionaltheme + plugins are a rebuild · migrate low-stakes first
Solo bloggermarkdown-in-git stays simpler until you want admin UI, media, co-editorsConditionalearns its keep only past that threshold
Bottom line: recommended for Astro teams who want a real CMS and will own a database. Not yet a WordPress replacement for plugin-dependent sites.