Monolith is the kind of project that makes the Cloudflare ecosystem feel less like a collection of primitives and more like an application platform.

It is not another static site generator. It is a dynamic blog/CMS designed around Cloudflare Pages, Workers, D1, R2, and an admin console.

That makes it a useful follow-up to the Cloudflare Workers, D1, KV, and R2 serverless stack post and the CloudflareBase post. Monolith is narrower than CloudflareBase, but more immediately usable if your goal is simply: “I want a blog with a dashboard, media, comments, backups, and analytics on Cloudflare.”

What is Monolith?

Monolith is an open-source edge-native blog system.

Its README describes the philosophy as:

content first, edge native, immersive reading, everything configurable from the console

The practical feature set is closer to a small CMS than a static blog:

  • Markdown editor with live preview;
  • KaTeX math;
  • syntax highlighting and code-copy buttons;
  • imports from WordPress, Ghost, Hexo, Hugo, Jekyll, and Halo;
  • drafts, scheduled posts, pinned posts, series, custom pages, comments, guestbook, friend links, and reactions;
  • admin console for settings, theme, snippets, SEO, backups, media, comments, pages, and analytics;
  • RSS, sitemap, robots.txt, JSON-LD, OpenGraph, and Twitter cards;
  • PWA offline support;
  • built-in D1 analytics plus optional Cloudflare Analytics Engine dashboards.

The project is MIT licensed.

The Important Self-Hosting Detail

Monolith is self-hostable, but not in the classic Docker Compose sense.

Self-hosting here means:

deploy the Pages frontend, Workers backend, D1 database, and R2 bucket into your own Cloudflare account

There is no docker-compose.yml in the repository, and that is not an accident. The app is written for Cloudflare bindings and edge deployment.

So the decision is simple:

If you want… Pick…
A static Markdown site with Git-based publishing Hugo, Jekyll, Astro, or another SSG
A traditional server CMS with a normal database WordPress, Ghost, Directus, Strapi
A Cloudflare-native blog CMS with admin UI and edge APIs Monolith

Architecture Overview

The stack is cleanly split:

Layer Monolith Uses
Frontend React 19 SPA, Vite, Tailwind CSS 4
Hosting Cloudflare Pages
Frontend proxy Cloudflare Pages Functions
Backend API Hono on Cloudflare Workers
ORM Drizzle
Default database Cloudflare D1
Database alternatives Turso/libSQL, PostgreSQL
Default object storage Cloudflare R2
Object storage alternative S3-compatible storage
PWA vite-plugin-pwa
Bot protection Optional Cloudflare Turnstile
Email Optional Resend
Advanced analytics Optional Cloudflare Analytics Engine

The frontend calls same-origin paths such as /api/*, /cdn/*, /rss.xml, /sitemap.xml, and /robots.txt.

Pages Functions then proxy those requests to the Worker backend through the API_BASE binding. That keeps the public site domain clean and avoids making the browser talk directly to a separate API domain.

What Runs on Cloudflare?

The default Cloudflare deployment uses:

Cloudflare Primitive What Monolith Uses It For
Pages Serves the React/Vite frontend
Pages Functions Proxies frontend routes to the Worker API
Workers Runs the Hono backend API
D1 Stores posts, pages, settings, comments, reactions, traffic stats, versions, guestbook messages, and links
R2 Stores uploaded media and backup files
Analytics Engine Optional advanced visitor analytics
Turnstile Optional login bot protection
Wrangler secrets Admin password, JWT secret, Resend key, Turnstile secret, Analytics token
Cron triggers Scheduled Worker tasks

That puts Monolith close to the pure Cloudflare serverless model.

The caveat is that Monolith also has adapters:

  • DB_PROVIDER=turso can move the database to Turso/libSQL;
  • DB_PROVIDER=postgres can move the database to PostgreSQL;
  • STORAGE_PROVIDER=s3 can move object storage to an S3-compatible provider.

Those adapters are good for portability, but the default deployment path is Cloudflare Pages + Workers + D1 + R2.

Console and Content Workflow

The admin console is the main reason to look at Monolith instead of a plain SSG.

From the code and UI routes, the console covers:

  • dashboard;
  • post editor;
  • post version restore;
  • comments moderation;
  • guestbook moderation;
  • friend links;
  • media upload and deletion;
  • public pages;
  • backup export/restore;
  • SEO configuration;
  • site settings;
  • Cloudflare Analytics Engine status and dashboard.

Behavior settings are stored in the database and cached briefly, so many changes can be made from the console without redeploying the site.

That is the practical CMS tradeoff:

SSG = simpler deployment, Git is the editor
Monolith = more moving parts, browser admin console

Security and Privacy Notes

Monolith uses an admin-password login with JWT sessions.

Important security pieces in the repo:

  • ADMIN_PASSWORD and JWT_SECRET are required secrets;
  • optional TURNSTILE_SECRET protects the login form when enabled;
  • rate limits exist for login, friend-link submissions, and guestbook/comment style flows;
  • Markdown rendering blocks dangerous URI schemes;
  • DOMPurify is used for user-provided rich text;
  • SSRF protection blocks private, loopback, and link-local target ranges;
  • the Worker sets security headers such as HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy;
  • commenter emails are not returned in the public comments API;
  • third-party script injection is gated by consent logic.

The privacy model is also Cloudflare-centered. Normal traffic data and content state live in the edge database/object storage you configure, while optional third-party scripts and Resend email are separate integrations.

Deployment Notes

The README’s Cloudflare deploy path is:

git clone https://github.com/one-ea/Monolith.git
cd Monolith
npm ci
npx wrangler login
npm run deploy:cloudflare

That deploy script is not just a frontend upload. It orchestrates:

  • remote D1 migrations;
  • D1 schema reconciliation;
  • Worker secrets for ADMIN_PASSWORD and JWT_SECRET;
  • Worker deployment;
  • Pages API_BASE secret;
  • frontend build;
  • Pages deployment.

For daily production updates, the GitHub Actions workflow is more conservative. It deploys through a no-secret-overwrite path, so existing business secrets such as ADMIN_PASSWORD, JWT_SECRET, and REACTION_SALT are not touched during a normal release.

Local Trial Notes

I did not deploy Monolith to Cloudflare from this machine because that would require real account bindings and secrets.

I did validate the code path that can be tested safely:

npx -y -p node@22 node -v
npx -y -p node@22 -p npm@11 npm ci
NODE22_DIR=$(dirname "$(npx -y -p node@22 which node)") && PATH="$NODE22_DIR:$PATH" npm run build
NODE22_DIR=$(dirname "$(npx -y -p node@22 which node)") && PATH="$NODE22_DIR:$PATH" npm run check

Results:

Check Result
Install Passed in a temporary clone
Frontend production build Passed
PWA generation Passed; dist/sw.js and Workbox assets generated
Workspace type checks Passed for client and server
Local doctor Failed because server/.dev.vars was intentionally missing

One repo detail to notice: .nvmrc currently says 20, while the root package.json requires node >=22, and the GitHub Actions deployment uses Node 22. I would use Node 22 for installs and CI parity.

npm ci also reported nine audit findings at the time of testing. I did not run npm audit fix, because that would mutate dependency resolution beyond the scope of reviewing the project.

Homelab Note

I added a Home-Lab note for Monolith, but not a Docker Compose stack:

That folder is intentionally documentation-only.

For Monolith, a fake compose file would be misleading. The correct local/private deployment path is:

Node 22 + Wrangler + Cloudflare account + D1 + R2 + Pages + Workers

If you want a blog/CMS that runs as a single local container, use a different project.

How I Would Use It

I would consider Monolith for:

  • a personal technical blog where I want browser-based editing;
  • a project journal with comments and simple reactions;
  • a Cloudflare-first publishing stack;
  • a blog migration from WordPress, Ghost, Hugo, Jekyll, Hexo, or Halo;
  • a site where global edge latency matters but I do not want to run a VPS.

I would not choose it for:

  • a pure static docs site;
  • a WordPress plugin ecosystem;
  • multi-author editorial workflows with mature role management;
  • a homelab-only Docker stack;
  • environments where Cloudflare platform coupling is unacceptable.

FAQ

Prompt Used for the Cover

Text-free editorial cover for a Cloudflare-native blog CMS called Monolith. Show a polished publishing console, markdown document panels, media bucket, edge worker node, small database cylinder, and global edge arcs. Clean high-end technical illustration, dark neutral background with orange, cyan, white, and green accents, no logos, no readable text, no fake UI labels, no watermarks.