Skip to content

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.

Written by

Faiz Kasman

Software engineer in Kuala Lumpur. Payments, multi-tenant SaaS, and inventory infrastructure. Currently building the Shell Malaysia ParkEasy app.

Keep reading