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
- dockernetworking
The Docker Networking Trap: Why Your Containers Appear Healthy While Traffic Disappears
Container logs show success. Health checks pass. Yet traffic between services stops at the network boundary. This is how silent DNS and port-binding failures hide in plain sight—and how to find them before they crush production.
- api designobservability
Why Your API Rate Limiter Is Costing You Real Traffic
Misconfigured rate limits don't just reject spam. They reject paying customers during traffic spikes, silently kill retries, and hide themselves behind generic 429 errors. Here's what actually happens when you measure it.
- inventory managementsupply chain
When Your Inventory Math Doesn't Match What's Actually in the Warehouse: How Rounding Errors and Manual Updates Turn Small Problems Into Supply Chain Disasters
Small inventory errors compound fast. Rounding mistakes, manual data entry delays, and systems that don't talk to each other create cascading supply chain failures that cost more to fix than the system that would prevent them.