I have been building apps since 2011. Worked with multiple cloud providers and architecture paradigms over the years. 

This is what my current approach looks like:

  1. JAMStack + Cloudflare whenever possible
  2. SSG with NextJS
  3. Netlify, Vercel, Render, and Caprover for deployment
  4. Docker Containers, Serverless Functions (rare)
  5. Hetzner, Digital Ocean, AWS clouds
  6. React, Svelte, Prisma, Postgres, Typescript

I'm mentioning all these because I don't want to hear "BuT WHy YOU do ThIS?". Please read the entire article and linked resources.

Context

I've been building Superblog (a JAMStack blogging platform focused on SEO and Speed) for the last 2 years and 10 months as a solo indiehacker. The dashboard where customers login and write posts is built with NextJS and deployed as a node-express app. You can read why I did this instead of deploying it like a serverless app here. 

In short, due to:

  1. Connection pooling issues
  2. Cold start times

But since then Prisma released data proxy and reduced cold start times by 5x.

Anyway, I could not deploy to Netlify or Vercel back then and used my favorite/love Caprover as always. 

How does Caprover work?

Caprover is similar to Dokku (if you are familiar) but also with a GUI. Install caprover on any server with a minimum of 1GB and we can connect our GitHub repo to setup CI/CD. Caprover uses docker swarm to auto-scale the deployed app. It involves setting up a wildcard A record for automatic SSL (with lets encrypt). You can also deploy apps via CLI, tar-ball, docker images, and one-click apps!

The Catch

We cannot use Cloudflare to mask it because the free plan doesn't support masking wildcard records. It requires $200/mo. Using caprover alone gives away the IP address of the server. Which can be used to launch a DDoS attack. But who does that? Who would want to attack a poor indiehacker earning less than $5K MRR, right? Right?

Initial Setup

Due to the above reason, I skipped Cloudflare and just installed Caprover on the Digital Ocean droplet and deployed the Superblog dashboard during the launch. Digital Ocean because I used the managed Postgres from them. Just wanted to keep everything in one place (Hetzner doesn't offer managed databases.). $12/mo for VPS (2GB, 1vCPU) and $15/mo for the Postgres database. Everything was good. 

Migration to AWS

I got free AWS credits with 2 years of validity. So, I migrated everything to AWS. Lightsail VPS (16GB, 4vCPU :P) and RDS Postgres Database (2GB RAM, 20GB Storage). Again, using Caprover without Cloudflare proxy.

Why migrate to Render?

Those were the days of DDoS attacks. DDoS attacks on indiehackers. Many indiehackers that I know personally were attacked. I had a hunch that I'll be next. However, I researched, tried, and tested an alternative in case of an attack. That alternative was Render.

I was traveling to the USA from India in April 2023 but not anyone outside of my immediate circle knew this. Well, except for one cute girl I started talking to 4 days ago. I'm pretty sure she is sweet, kind, and a good person. She didn't have anything to do with what happened next.

When I was close to Dubai, I received a downtime alert for the dashboard. I knew this was an attack because AWS typically doesn't go down (lol). I decided to add Cloudflare Shield but the inflight WiFi wasn't so great. I planned every single step to do after getting out of the flight beforehand.

I had around 2 hours of time in layover where I could get the airport WiFi to do this. But I also have to help my sister with my nephew during transit. Additionally, the airline staff messed up my boarding passes. I had to get that fixed AND take care of Superblog. It was like a high-tense thriller. I usually like such situations. My brain goes into overdrive mode. Love it!

So, moving to Cloudflare could be a bad idea with such less time especially since it involves changing nameservers. There would be downtime and I'd be over the Atlantic Ocean. What's the next best thing? Render. 

Render because they use Cloudflare internally. It means I can deploy the Superblog dashboard to Render and let their Cloudflare protection act as my shield. it barely took a few minutes. I deployed the Superblog dashboard by connecting the Github repo to Render and added a simple CNAME in DNS records. In a few minutes, propagation was done and SSL was generated. The attack died down over the next few days!

Why migrate from Render to Hetzner?

It turns out that using 2GB RAM, 1vCPU ($25/mo) plan wasn't enough for the superblog dashboard. So, I upgraded to the next plan which is $85/mo (such a jump) which gives 4GB RAM, 2vCPU. Turned out, I had $500 credits in my render account because I listed superblog on Betalist the previous year. Win! :D

So, I've been using Superblog on Render since April 2023. My AWS credits ran out in May. Render credits exhaust in September. So, I started looking towards my good old buddy - Hetzner. I did not choose Digital Ocean because their SMS 2FA sucks. I've been locked out because their SMSes never arrive most of the time. I have to use recovery codes to login. This means no more managed database on Digital Ocean as well. 

I'm using the AWS Lightsail Postgres database for $15/mo. And the Hetzner VPS of $8/mo with 8GB RAM, 4vCPU. Obviously, build time is much much lesser when compared to Render. This time, I'm using Cloudflare Proxy in front of my Hetzner. 

Using Caprover with Cloudflare Proxy

Generally, you setup a wildcard record like *.something.example.com in your DNS. Then captain.something.example.com will be your caprover dashboard. app1.something.example.com, app2.something.example.com will be the domains for your apps. You can then map a custom domain to your apps on caprover. For more detailed explanation of regular usage, read the caprover docs.

This approach doesn't work because Cloudflare doesn't mask wildcard records in the free plan.

Steps to make it work

1. Migrate your domain to Cloudflare.

2. Disable domain verification in caprover.

echo  "{\"skipVerifyingDomains\":\"true\"}" >  /captain/data/config-override.json

docker service update captain-captain --force

3. In Cloudflare, Add A Record => captain.example.com => Server IP address (disable proxy). Enable SSL in caprover. After some time, enable the Cloudflare proxy. 

4. Run caprover serversetup in your local machine.

5. Enter the root domain as example.com. This is mandatory because Cloudflare doesn't do SSL for 4-level deep in the free plan. 

6. Use the desired subdomain name as the app name when deploying because you can't add the same custom domain later (which is part of the root domain) due to caprover restrictions. Caprover then generates myapp.example.com as the subdomain. If you want to avoid this situation, deploy caprover on a different root domain than your app's actual domain. 

For example: 

Root domain: example.net

Caprover dashboard: captain.example.net

App deployment URL: myapp.example.net

Custom domain: dashboard.example.com

7. In Cloudflare, Add A Record => dashboard.example.com => Server IP address (disable proxy). Enable SSL in caprover. After some time, enable the Cloudflare proxy. 

8. Set SSL encryption to "Full" in Cloudflare.

8. That's it, you now have your own Render/Heroku at a super low cost with Cloudflare Protection. 

How I felt after figuring out Caprover + Cloudflare
Me after figuring out how to make caprover work with cloudflare (proxy enabled)

You can follow me on Twitter for such in-depth tech takes. 

Caution: you might also see some random memes and useless thought experiments. 

Also, people might think, why all this hassle to save a couple of hundred dollars? The current major cost to run Superblog is $23/mo (VPS and Database). How cool is it that a $5000 MRR SaaS is run with a $23/mo stack? How cool is it that we can run a $25000 MRR SaaS with the same $25/mo stack? Isn't it cool?