Skip to content
Second Brain Chronicles
Go back

The Build Is a Server

The Build Is a Server

I had seven Astro sites running on a VPS. Each one deployed the same way — rsync the build output, nginx serves it, Let’s Encrypt handles TLS. It works. It’s also maintenance I don’t want to think about.

Cert renewals. Nginx configs. SSH keys. Disk space. The VPS isn’t expensive, but it’s needy. Every few weeks something needs attention — a cert that didn’t auto-renew, a config that drifted, a deploy script that assumes a path that moved.

So I decided to try something: move everything to Cloudflare Pages. Connect a GitHub repo. Push to main. Walk away.

Sixty-Five Minutes, Five Sites

The first five went fast. Connect repo, set NODE_VERSION=24 in the environment, push, wait for the build. Green checkmarks across the board. DNS was already on Cloudflare, so custom domains pointed over in seconds. Two of them didn’t even have GitHub repos yet — they’d been living as local directories deployed via rsync. I created repos, pushed, and connected Pages. Five sites live in about an hour, with no nginx, no certs, no rsync.

Then the Sixth Site Broke

Site number six — a project with anime.js and Three.js for some 3D eye candy — built fine on my laptop. On Cloudflare Pages, Rollup choked. Client-side imports that resolve perfectly in a local astro build failed in the Pages build environment.

The fix was a vite.ssr.noExternal config to tell the bundler “don’t try to resolve these server-side, they’re client-only.” Which makes sense in hindsight. The build step runs in a Node environment on Cloudflare’s servers. It’s not a browser. When Rollup encounters import * as THREE from 'three', it tries to resolve that server-side — and some packages don’t play along.

What tripped me up was thinking of these as static sites — no server involved. But that’s wrong. The build is a server. It runs in Node, on someone else’s machine, with different module resolution rules than your laptop. “Works locally” and “builds in CI” are two different statements, even for a site that ships zero JavaScript to the server at runtime.

The Ghost Commit

The seventh site hit a weirder problem. I pushed a fix to main. Cloudflare Pages built — but it built an old commit. I pushed again. Same old commit. I deleted the entire Pages project and recreated it. Connected the repo fresh. It still built the old commit.

The GitHub App integration was caching the commit SHA from the initial connection. Somewhere between GitHub’s webhook and Cloudflare’s build queue, the reference was stuck — not documented, not obvious, the kind of thing where you question your own sanity for ten minutes before realising the platform is lying to you.

I deferred both stragglers. Five out of seven shipped. The two that didn’t are real blockers, not excuses — one needs a build config rethink, the other needs the webhook cache to unstick from GitHub’s side, and I’m not yet sure whether that clears on its own or whether I have to disconnect and reconnect the repo manually.

”Static Site” Is a Description of the Output

“Static site” describes what gets served, not what gets built. The build step is a full server-side process with its own module resolution, its own Node version, its own filesystem — every “works on my machine” instinct applies. If you’re moving from rsync-to-VPS to git-push-to-platform, budget for the gap between your local build and their remote one. Same class of bug as “works locally, breaks in CI,” just wearing a friendlier hat.

The five that moved cleanly retired their nginx configs and Let’s Encrypt renewals along with them. That’s the part I keep underestimating — not the minutes saved on any single deploy, but the small ongoing attention tax that disappears when the platform takes over the boring bits.


The VPS isn’t going to zero. It’s still the right tool for compute and bucket storage — what changed is that I was carrying static sites on it for no good reason. The deeper lesson is the one I keep relearning: ask your tools what’s possible given the setup you already have. Most of the time the answer is “more than you thought.”


Share this post on:

Previous Post
AI Over LoRa
Next Post
Twenty-Two Pages, One Evening