One VPS, three side projects, zero cloud bill
Docker Compose, Nginx, a 2GB Hetzner box, and the indie-infra setup I use when I do not want a cloud invoice on my personal card.
· 5 min read
Every few months a new managed service launches that could host my side projects for "free". Free until the rate-limit tier, free until the egress fees, free until a quiet pricing change buried in a docs diff. At some point I got tired of playing the dance.
So my side-project infra is a single 2GB Hetzner VPS in Singapore. Four Euros a month. One of the best RM-to-happiness trades in my life.
The setup
Docker Compose on the host. Nginx in front as a reverse proxy. Certbot for Let's Encrypt. Each side project is a compose stack: app container, a Postgres container, optionally a Redis container. Compose networks keep them isolated. Nginx does TLS termination and routes by hostname.
For Lunara alone, the stack is: NestJS backend, Next.js frontend, Postgres 15, Redis 7, Nginx, Certbot. Six containers, one YAML file, one systemctl restart after docker compose pull.
Why not a managed platform
Mostly because I want my side projects to survive me forgetting about them for six months. On a VPS, forgetting costs me four Euros a month. On a managed platform, forgetting costs me a surprise invoice when traffic spikes and I did not check the notification.
Also: I learn more. I know exactly where my data sits, what the backup pipeline is, and what happens when Nginx throws a 502. On Fly or Render or whatever-is-trendy, there is always a layer I cannot see.
What breaks
Memory. Two gigabytes is not a lot. The first time I loaded JomJual + Lunara + a Next.js build on the same box, the kernel OOM-killed Postgres. I learned to constrain containers with mem_limit, add a swap file, and not do production builds on the host.
Certificate renewals. Certbot is reliable but not automatic on a minimal VPS. I keep a monthly systemd timer that runs certbot renew --quiet and restarts Nginx. The first time I forgot this, the cert expired at 02:00 on a public holiday.
Backups. Postgres pg_dump to a mounted volume, then rsync to a Backblaze B2 bucket. Three dollars a month. I have needed the backups twice. Both times I was very grateful.
The numbers
Total cost per month: RM 18 for the VPS, RM 13 for Backblaze, RM 0 for a domain I already owned. Thirty-one ringgit to host three side projects indefinitely.
Will this scale to a thousand paying users? No. But by the time I have a thousand paying users I will happily pay for a managed platform, and the migration is two days of work.
Until then, the VPS is the best-behaved teenager in my life.
Keep reading
- sreobservability
Why Your Error Budget Is Lying to You (And How Observability Actually Fixes It)
Error budgets sound clean in theory but fail silently when you can't see where the unreliability actually is. Here's how to build observability that makes them real.
- databaseapi
When Your Team Stops Talking to the Database Directly — Why That Matters
Direct database access feels fast until someone deletes a year of records by accident. Here's what happens when you add an API layer, what it costs, and the exact warning signs your business has outgrown the shortcut.
- aiworkflow
Claude Code, but for backend people
Not a demo reel. How I actually use Claude Code for queue workers, schema migrations, and payment code where wrong answers cost RM.