flare-stack-blog is a blog CMS that runs entirely on Cloudflare
A modern full-stack blog CMS driven by Cloudflare. | 基于 Cloudflare 生态的全栈现代化博客 CMS。
At a glance
- What is it?
- flare-stack-blog is a full stack blog and CMS built on the Cloudflare edge stack, with React 19 and TanStack Start on the front, Workers, D1, R2, KV, Queues and Durable Objects behind it, and a Drizzle schema for the database. It targets people who have already decided to run serverless, and it is a TypeScript codebase with a Bun toolchain.
- Who is it for?
- flare-stack-blog suits a solo writer or a small team that wants a blog with an admin panel, comments, full text search and a webhook friendly API, and that is willing to have its data live in D1 and its media in R2 rather than on a VPS. It is the wrong choice if you need WordPress plugin compatibility, a self hosted MySQL database, or any runtime that is not a Worker.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A CMS that has no server of its own
flare-stack-blog is a full stack blog and CMS built on the Cloudflare edge ecosystem, using Workers, D1, R2, KV and Queues for a fully serverless architecture with no traditional server to operate. The project is written in TypeScript, licensed under GPL-3.0, and its version is 3.6.1 with tags v3.6.0 and v3.5.0 just before it.
What that means in practice is that every layer has a Cloudflare counterpart. The edge runtime is Cloudflare Workers, which handles the server side rendering. D1 is the relational database and it also carries the full text search index, KV is the edge key value cache, and R2 is object storage for media. Cloudflare Queues handles the asynchronous work, and Durable Objects do the request rate limiting.
The front end is React 19 with Tailwind CSS 4, and the visual design is credited to the Fuwari open source blog theme by saicaca. The full stack framework is TanStack Start with Router and Query, the API layer is oRPC with Drizzle ORM for the SQL side, and authentication is Better Auth.
The interface is bilingual, with the public reading pages and the admin panel both supporting a full Chinese and English switch. A live demo is at blog.dukda.com.
One boundary is stated up front: the project is designed specifically for the Cloudflare Workers edge runtime. That is not a detail, it is the architecture.
Writing, versioning and search, all in D1
The content side is where most of the work in a blog CMS goes, and the feature list covers the usual ground in unusual detail.
The editor is built on TipTap and Shiki, so code gets syntax highlighting and there is native support for mathematical formula layout, media insertion at several sizes, and a cover image for the article. Publishing takes an automatic snapshot, so you can compare any two published versions by date and restore to an arbitrary point in the history in one action. Articles carry a single category and multiple tags, and there is a site wide full text search over published content, indexed with the D1 FTS index across titles, summaries, categories, tags and the body itself.
The reader side covers the interaction features a small community needs. Comments nest to arbitrary depth, and there is anti-spam protection for guests, soft deletion for comments, and a mute option for offending accounts. Friend links have a front end application form and a back end one-click approval with a rejection reason and an instant email notification, rather than a hand-edited list.
There is also built in access analysis with Umami statistics and automatic article popularity tracking, and out of the box support for Canonical tags, Schema.org, RSS, Atom and JSON Feed, Sitemap and Robots.
The API story is worth noting separately. The admin panel can issue API keys, and those keys drive normalised OpenAPI and oRPC endpoints, so a third party tool or a publishing script can talk to the blog directly.
Queues, Durable Objects and the edge storage split
The infrastructure decisions are the ones that make this a Cloudflare project rather than a blog with a CDN in front of it, and the README is explicit about the role of each piece.
Notification emails and webhook dispatch go through Cloudflare Queues, so the API response does not block on sending. The list of webhook targets is long: Telegram, Discord, Lark, DingTalk, WeCom, Bark, ntfy, plus a generic signed JSON payload and custom templates, with a separate document at docs/webhook.md for the details. That range of targets is a Chinese market signal in itself.
Durable Objects do the rate limiting, which is the one piece that genuinely needs a single threaded stateful actor, since rate limit counters have to be serialised per key.
Media goes to R2 buckets and is served through Cloudflare Image Resizing for on the fly scaling and format optimisation, so an uploaded image is not stored pre-rendered at every size. KV sits in front of the hot paths, and the README describes it as carrying high frequency cache traffic.
The result is a design where the expensive, rare, stateful work is in Durable Objects and D1, the bursty fan out work is in Queues, and the read path is Workers plus KV plus R2 plus the CDN in front of the assets.
Local development with Bun and Miniflare
The local development path is short, and it assumes Bun.
The requirement is Bun 1.3 or later, and the README says the configuration needs Node 24 or newer, pointing at the .node-version file for the pinned value. The setup is three commands:
git clone https://github.com/du2333/flare-stack-blog.git
cd flare-stack-blog
bun installfollowed by copying the two configuration templates:
cp .env.example .env
cp .dev.vars.example .dev.varsThe local environment runs Miniflare, which emulates the D1, KV and R2 storage services on your machine, and the Worker configuration lives in cloudflare.config.ts. If the deployment variables are not set, the project starts with placeholder values, which is why the clone and install steps work before you have a Cloudflare account.
The dev server is then started with:
bun devand the browser opens at http://localhost:3000. The dev script is vite dev on port 3000, so the port is fixed rather than chosen by the framework.
Two local behaviours are documented and both are surprises on first contact. In local development, registering an account does not actually send an external email, and the verification link is printed straight to the terminal console, where clicking it activates the account. And the first account registered locally is automatically given administrator rights.
The scripts tell you what has to stay consistent
The package.json scripts describe a project with several generated artifacts that have to agree with each other, and the check script is the one to run before committing.
The full check pipeline is:
"check": "bun run orpc:contract && cf workers types && oxlint && oxfmt --check && ./node_modules/typescript/bin/tsc --noEmit",So a commit runs the oRPC contract generation, then cf workers types to regenerate the Worker type bindings, then oxlint, then the oxfmt format check, then the TypeScript compiler with no emit. The oRPC contract step itself compiles the translations first, because the generated API contract carries the message keys:
"orpc:contract": "bun run i18n:compile && bun scripts/generate-orpc-contract.ts && oxfmt src/lib/orpc/contract.generated.json",The translation tooling is Paraglide JS, compiling from the inlang project directory into src/paraglide, and there is a separate verify script that checks the message key completeness between the Chinese and English locales, a prune script for unused keys, and a verify script exposed as bun i18n:verify.
The database scripts are Drizzle, with drizzle-kit studio for the visual database manager, push and generate for schema changes, and cf d1 migrations apply for applying migrations, with a local variant that applies a fixed migration id with the local flag and persists state to the .wrangler/state directory.
The test story is split in two, with vitest run for the main suite and a node config for the tests that need the Node runtime rather than the Workers runtime.
Deployment runs through Cloudflare Builds
The deployment guide is a separate document at docs/deployment.md, and the README says to use GitHub with Cloudflare Workers Builds, with the guide covering first deployment, optional configuration and later updates.
The .env.example file is where the deployment shape becomes concrete, and its comments are worth reading carefully because they describe a distinction that surprises people. The file is for build time and local tool variables, and it says the values are not injected as Worker secrets. The .env file is the local copy, and in production these values go into the build variables of Cloudflare Builds.
The required set is small and explicit:
WORKER_NAME=my-blog
QUEUE_NAME=blog-queue
DOMAIN=blog.example.com
D1_DATABASE_ID=your-d1-database-id
KV_NAMESPACE_ID=your-kv-namespace-id
BUCKET_NAME=your-r2-bucket-nameThe comment on those lines matters more than the values: cloudflare.config.ts reads real environment variables and does not read the .env file, so they must be set as Cloudflare Builds build variables. There is an optional route mode for Workers Routes instead of a custom domain, enabled with a ROUTE flag, with the zone name derived from the domain by default.
There is a separate block for the local Drizzle tooling, with the account id, database id and API token, and those explicitly do not go into the Worker. A further optional block injects front end variables at build time, such as a Turnstile site key and an Umami website id.
What the project is not, and where it fits
The comparison that matters here is with the other way to run a blog, and the difference is not incremental.
A conventional self hosted blog means a VPS, a database you administer, a process supervisor, a reverse proxy, TLS renewal and a backup plan. flare-stack-blog replaces all of that with a Cloudflare account and a set of build variables, which is a genuine operational win for a single author and a genuine constraint for anyone whose data residency or compliance requirements forbid a platform dependency.
The constraint cuts the other way too. The schema lives in D1, the media in R2, the cache in KV, the queue in Queues. Every one of those is a Cloudflare product with its own limits on size, request count and cost, and none of those limits are documented in the repository. The project is a good fit precisely where the workload is a blog, because a blog does not push those limits, and a bad fit for anything else.
Two smaller signals are worth reading. The repository has a .claude directory, an .agents directory, an AGENTS.md and a CLAUDE.md, plus a .husky directory and a .release-it.ts file, so the release process is automated and the project is developed with agent tooling in the loop. There is a GLOSSARY.md and a docs directory, and the project asks contributions to go through CONTRIBUTING.md and a Telegram group.
The last push was on 2026-10-20, the same day as the v3.6.1 tag, so the project is moving quickly.
Editorial conclusion
flare-stack-blog suits a solo writer or a small team that wants a blog with an admin panel, comments, full text search and a webhook friendly API, and that is willing to have its data live in D1 and its media in R2 rather than on a VPS. It is the wrong choice if you need WordPress plugin compatibility, a self hosted MySQL database, or any runtime that is not a Worker. Before deploying it, read the deployment guide, set the required build variables in Cloudflare Builds, and run bun check locally to catch contract, lint, format and type problems before you deploy.
Frequently asked questions
How do I run flare-stack-blog locally?
You need Bun 1.3 or later and Node 24 or newer. Clone the repository, run bun install, copy .env.example to .env and .dev.vars.example to .dev.vars, then run bun dev and open http://localhost:3000. Miniflare emulates the D1, KV and R2 services locally.
What does flare-stack-blog use for its database and file storage?
Cloudflare D1 is the relational database and also carries the full text search index, R2 stores media files and serves them through Cloudflare Image Resizing, KV is the edge cache, Queues handles notification emails and webhook dispatch, and Durable Objects do the request rate limiting.
How do I deploy flare-stack-blog?
The README points to the separate deployment guide at docs/deployment.md for using GitHub with Cloudflare Workers Builds. The required build variables are WORKER_NAME, QUEUE_NAME, DOMAIN, D1_DATABASE_ID, KV_NAMESPACE_ID and BUCKET_NAME, and cloudflare.config.ts reads real environment variables rather than the .env file, so they must be set as Cloudflare Builds build variables.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/du2333-flare-stack-blog)Was this useful?