How I Built a $0/Month Blog Stack From My Home Lab

How I Built a $0/Month Blog Stack From My Home Lab

I opened my Cloudflare billing page last week. The number at the bottom was $0.00. Not “we will charge you on the 30th” zero. Actual zero.

My blog served 50,000 sessions in the previous month. 1.2 TB of traffic. Two cron jobs a day. Never once sent me a bill. I have been running some version of this stack for two years, and I have never paid a cent in infrastructure.

The catch: I am writing this from Jakarta. The “free” part is only free because the server lives in my living room.

The whole thing runs on a Lenovo ThinkCentre M720q Tiny — bought second-hand for Rp 2,200,000 two years ago. Smaller than a textbook. 15 watts when idle. Not turned off since the day I installed Debian. If you have ever considered building a home lab server, this is the post I wish I had read before I started.

The stack, top to bottom

At the very top: the visitor. Usually on a phone, often on a flaky Indonesian mobile connection (yes, even in Jakarta the signal drops in malls), and they want a page in under two seconds.

Below them: Cloudflare. Doing five things at once — DNS, CDN, caching, security, and SSL. I am on the free plan: unlimited DNS, unlimited CDN, basic caching, basic WAF, and free SSL. None of those are negotiable. They are the bones of the stack. They are why a home server with a 100 Mbps connection can serve 50,000 sessions a month without breaking a sweat.

Below Cloudflare: the origin. A Lenovo ThinkCentre M720q Tiny on a shelf in my living room. Intel i5-8400T, 32 GB RAM, 512 GB SSD for the system, 2 TB HDD for backups. Connected to my home internet on a static IP. The box cost Rp 2,200,000. The extra power cost is around Rp 25,000 a month.

Below the M720q is the application. WordPress, with Rank Math SEO, a custom theme, and a handful of utility plugins I wrote myself. WordPress is famous for being heavy. The version of this blog that was on a $5 DigitalOcean droplet in 2019 was, in fact, a slideshow. The current version is not. The reason is the cache layer — Cloudflare configured to cache HTML, not just assets.

The database is MariaDB. The runtime is PHP 8.3. The OS is Debian 12. No Docker. No Kubernetes. No service mesh. One process supervisor (systemd), one web server (nginx), one database, one PHP runtime. That is the whole application tier.

The same box runs a few other personal services: a small AI node I wrote about here and the homelab budget breakdown on teknologinow.

The actual cost

Let me be precise, because this is the part most “self-host for free” posts skip.

The capital cost is the M720q itself. I bought mine used from a friend upgrading to a Mac Mini. The going rate is about Rp 2,000,000 to Rp 2,500,000 for a clean unit. Budget Rp 2,800,000 if buying today. One-time cost. It does not recur.

The recurring cost is electricity. The M720q averages 25 watts. At PLN’s residential tariff of around Rp 1,400 per kWh, that is about Rp 25,000 a month — roughly one Grab ride to the airport.

The other recurring cost is the home internet. I pay an extra Rp 50,000 a month for a static IP on IndiHome. If your ISP blocks standard ports, use Cloudflare Tunnel instead.

Total monthly cost for the blog: Rp 75,000 a month. If you start from zero, the one-time capital is Rp 2,800,000. Domain registration is about Rp 180,000 a year — but that is name registration, not infrastructure.

Why Cloudflare, not just nginx

The most important architectural decision is the cache. Without Cloudflare, a home server on a 100 Mbps connection can serve maybe 5,000 page views a day before choking. With Cloudflare, that same box serves 50,000 sessions a month without breaking a sweat. Cloudflare serves 95 percent of traffic without ever touching the origin.

This matters even more for a home lab than a data center. My IndiHome connection is 200 Mbps down but only 50 Mbps up. Without a CDN, every visitor would hammer my upload. With a CDN, only the 5 percent cache misses reach my living room.

Here is the rule I set: cache everything that looks like a static page, ignore query parameters and non-auth cookies. WordPress emits Cache-Control: no-cache by default — the most expensive thing it does. I overrode that at the nginx level. Now Cloudflare sees a page it can cache for an hour. The next 10,000 requests never hit the origin.

The part I almost got wrong: cache invalidation. If you cache for an hour and then publish a new post, the home page stays stale for up to an hour. Two fixes: the lazy way is to wait (most blogs publish once a day anyway), and the correct way is a small WordPress plugin that purges the cache on publish. I do both. The home page is never more than a few seconds stale.

The other thing I got right: security isolation. A dedicated VLAN for the server, a strict firewall allowing only ports 80 and 443 from the public internet, and Cloudflare Tunnel for admin access. Do the VLAN now. Thank yourself later.

What runs on the box

Nginx in front of PHP-FPM. MariaDB with a few custom buffers. I followed the standard WordPress on nginx recipe and stopped optimizing once it handled 50 concurrent connections.

Two cron jobs: a site health monitor and a GA4 analyzer. Both Python scripts. Both scheduled via cron.d. Both write JSON output. A Telegram bot reads the latest JSON and sends me a summary each morning.

The M720q has been up for 217 days. The last time I logged in to debug something was two months ago — a misconfigured cron schedule.

The reason it scales is not the architecture. The architecture is fine. The reason it scales is that I do not have to touch it. The same box runs my home AI experiments, a file server, and a few side projects. None have asked me for anything in months.

The gotchas

Three warnings.

First: not every home ISP lets you host a public service. Check your specific plan. If your ISP blocks ports 80 or 443, use Cloudflare Tunnel — it only needs outbound connectivity.

Second: home power is not data center power. If the power goes out, the blog goes down. A small UPS gives me about 30 minutes — enough for short blackouts in my area.

Third: the WordPress admin is uncached. Every time you log in to write a post, you hit the origin. I do not have a clever answer, I just live with it.

What I would not recommend

I would not host a high-traffic production service on a home lab. If you are running a SaaS that real customers depend on, you need the SLA of a real data center.

I would not put a global CDN in front of WordPress without understanding what you are caching. Cache a logged-in user’s page by accident and they see someone else’s session. Read Cloudflare’s caching docs. You will be fine. Skip it and you will have a bad day.

I would not use a Raspberry Pi for this job. ARM means recompiling every PHP extension from source. The SD card will eventually fail under database write load. The M720q is x86 — every binary you apt-install just works.

The total cost, in one line

A self-hosted blog that serves 50,000 sessions a month. Cloudflare free tier for CDN, DNS, SSL. A paid box that costs less than a Grab ride per month. A one-time hardware investment.

Rp 75,000 a month if starting from zero with a static IP. Zero if you have an existing setup to stack it on.


Discover more from Susiloharjo

Subscribe to get the latest posts sent to your email.

Leave a Comment

Discover more from Susiloharjo

Subscribe now to keep reading and get access to the full archive.

Continue reading