Every site we run — this one, a stained-glass studio's shop, a restaurant, a community platform, a handful of self-serve stores — runs on the same copy of HelloWorld CMS. Fourteen sites, one core, one person on call. Here's how that works without becoming a house of cards.

One core, many content folders

There is one copy of the CMS code on the server. Each site is a folder of content next to it: pages and posts as plain JSON files, an uploads folder, a small settings file with that site's constants — where its data lives, whether it has a store, which theme it uses. The web server hands every request to the shared core with that site's settings loaded first.

The payoff is the deploy. Fix a bug in the core once, push it once, and every site has the fix. Content deploys go the other way — per site, just the files that changed — and a site's owner editing in the visual editor never touches code at all.

Isolation where it matters

Sharing code doesn't mean sharing everything. Each client site gets its own PHP worker pool (the self-serve sandbox sites share one), so a traffic spike or a crash on one site can't stall another. Since August every pool is also jailed to its own folders at the filesystem level: a client site's code physically cannot read another site's database, uploads or settings, and the shared sandbox pool is walled off from all of them. We added that after a security review, not before, and we'd rather say so than pretend it was always there.

Publishing is instant because the cache is honest

Rendered pages are cached in memory, but the cache key includes the content file's modification time. Save a page and the next request simply misses the old cache and renders fresh — there's no "clear cache" button because there's nothing to clear. The HTML that goes out is tidied on the way: the first image on a page loads eagerly, everything below the fold loads lazily, and local images are served as WebP where the browser accepts it.

Things that run while we sleep

  • Backups. Every store and site database is dumped nightly, the full site tree weekly, and both are shipped off the server to a second machine. We ran a restore drill in August and found that the weekly tarballs had never actually left the box. Fixed the same day — but that's what drills are for.
  • Monitoring. A check runs every ten minutes and sends one consolidated email only when something is wrong. No "all good" mail, ever, and the same failure isn't re-sent for six hours. If you get an email from it, it matters.
  • Store jobs. Slow store work runs on a timer as a separate background worker, so it never sits inside a customer's request.

The trade-off

A shared core means a core bug is everyone's bug. This month we found one that had blanked every blog post on our own site since June, and while clearing caches after the fix we caused a forty-second outage on every site at once by deleting a cache folder the web server expected to exist. We wrote that one up too: We shipped a blank page for three months.

We'd still make the same choice. One core is why a fix for a client takes minutes, why a new site takes an afternoon, and why one person can run fourteen of them and still answer the phone.