Five apps, one server, no backups
My one backup script had been failing in silence for three days. Here's how I rebuilt the whole thing: backed up, recoverable, deployable with a git push, and somehow costing £0 a month.
Every side project I’d ever shipped lived on one small server. Five apps, one box, one disk: a couple of homepages, a waitlist, a compliance tool, an internal dashboard. It ran fine, which was exactly the problem. It ran fine for so long that I stopped looking at it, and it quietly turned into the thing I could least afford to lose.
Then one morning I went to check the backups and found out they’d been failing for three days. No error. No email. The script had simply stopped working after a folder got renamed, and nothing told me. If that disk had died that week it would have taken everything with it: real signups, subscriber records, every secret key that existed nowhere else on earth.
That was the wake-up call. So I tore the whole setup down and rebuilt it properly. The twist is the bill at the end. It’s zero.
The backup that was lying to me
Before any of the fun, the unglamorous thing that actually matters. I rewrote the backups to do what backups are supposed to do: snapshot every database every night, check each one isn’t corrupt, and ship the copies off the box to Cloudflare R2. A backup sitting on the same disk as the thing it’s protecting isn’t a backup, it’s a feeling.
Then the part I’d been missing entirely: an alarm. If a backup fails now, I get an email that night, not three days later by accident. A week after I wired it up, a refactor deleted an entire app by mistake, and this time the system told me the same evening. That alert turned out to be worth more than the backup.
And I did the thing nobody does: I actually tested a restore. An untested backup is just a folder you feel good about.
Make the server worthless
Why was losing the box so frightening? Because it was a hoarder. The databases lived as files on its local disk, existing nowhere else. So I moved all of that to Neon, a serverless Postgres that costs nothing when it’s idle.
The moment the data left, the box stopped mattering. It went from “irreplaceable” to “I could delete this and lose nothing.” That sounds like a downgrade. It’s the entire point. A server you can throw away is a server that can’t hurt you.
Send production somewhere else
With nothing precious left on it, I shipped each app to where it actually belonged.
The static sites, including this one, went to Cloudflare Pages: no server at all, just
files on a global CDN, free HTTPS, deploy on git push. I’d been running a Node process to
hand out HTML files, which in hindsight is like hiring a chauffeur to push a parked car.
The real apps became Docker containers on Google Cloud Run. Each one is now an image. Cloud Run runs it, scales it to zero the second nobody’s using it, and lets me roll back to any previous version in seconds. The part I love: I build the image once, test it, and promote that exact image to production. What I tested is what ships, down to the byte. No more “worked on staging, broke on prod.”
Push, watch, done
Not everything needs ceremony, so I sized it to the job. A static homepage gets two stops: edit, then live. An app with real users gets three: dev, staging, prod.
The day-to-day is almost boring, in the best way. I edit on my machine and watch it change
instantly on a dev. URL. I git push to deploy. For the apps, I click one button to
promote the tested image to production. Secrets never go near the code; they live in a vault
and get handed to the container at runtime.
The server’s demotion
Here’s my favourite part. The box that used to be production now runs no production at all. It’s just my workshop, the place I build and preview before shipping. Every real visitor is served by Cloud Run or Cloudflare’s edge. I could drop the server in a lake tomorrow and not one user would notice.
The bill that still doesn’t feel real
Now the number I keep re-checking because it looks like a mistake. The entire live setup, five apps, databases, nightly backups, monitoring, full CI/CD, costs this much:
- Cloudflare Pages, DNS, TLS: free, unlimited bandwidth
- Google Cloud Run: generous free tier, scales to zero, so idle costs nothing
- Neon Postgres: free serverless database
- Cloudflare R2: free backup storage, no egress fees
- GitHub Actions: free CI minutes
- Secret Manager, Resend, Cloud Scheduler: all within free tiers
£0 a month. The only thing I pay for is the domain names. I wired up a billing alarm in case I’m wrong, and so far it has never made a sound. On a “normal” cloud setup this is a real recurring bill; here it rounds to nothing, because the free tiers are genuinely generous if you build for them.
One more thing
I didn’t do this alone. I paired with Claude (Anthropic’s Claude Code) for most of it. It
planned the migration order, wrote the Dockerfiles and CI pipelines, talked me through the
DNS and certificate headaches, and rewrote the very backup system that set this whole thing
off. It was less like using a tool and more like working with a tireless engineer who’d
actually read the docs. This post was the last thing we shipped together, the same way as
everything else: one git push.
If I could go back
Do the backups first, before any of the exciting parts. Move your data off the box early, because everything gets easier once the box is disposable. And fight the urge to over-build: a homepage doesn’t need a staging environment, and a side project with one user doesn’t need Kubernetes. Pick the smallest tool that fits, lean on free tiers that are actually good, and keep the old server around as a comfortable place to work instead of a thing that keeps you up at night.