Hosting Websites at Home with a Framework Mainboard, Cooler Master Case, Dokku, and Cloudflare

8 min read Original article ↗

When we at Framework came out with the Cooler Master Case last year, which can turn any old mainboard from our laptops into a full-fledged mini computer, my mind ran wild with ideas. Should I move my Home Assistant set up off of my Raspberry Pi 4 and onto my old mainboard? How about setting up a Plex instance for some of those movies I definitely ripped from DVDs I owned back in the day?

By way of background, I’m currently a Lead Software Engineer, Marketplace at Framework Computer. We’re a relatively new company, a little over 4 years old, trying to make electronics more easily serviceable and upgradeable. The idea is that you can reduce e-waste if you don’t have to throw out your entire computer every 3 – 4 years to upgrade to the latest tech.

I mostly work on the Marketplace team, a.k.a. the e-commerce side of the business at https://frame.work where you can buy our products. I also get to test out some of the new hardware we release and give my feedback to the product team. This is definitely one of my favorite perks of the job! A few months ago, I obtained one of our new Cooler Master Mainboard Cases from work to test out. 

As I brainstormed, my Digital Ocean bill arrived in my inbox for the month: another $22. Prices had been going up in the last few years to host the 2 boxes that I have on there: one for my personal server with a Dokku (“The smallest [Platform as a Service] implementation you’ve ever seen”) instance running on it for about $20 and another to run a VPN for when I need a US connection out of the country, which was only costing around $2.

The instance I had for my websites just had a bunch of hobby projects that barely saw any significant traffic. At $20/month or $240/year, even if I spent around $1000 on hardware, I could pay back the investment in time to move my apps to home hosting in about 4 years. That might seem like a lot of time, but it’s been about 11 years since I created Dial A Compliment so it wasn’t too far-fetched! Maybe I could also just try moving to a smaller VPS but why not self-host?

Gathering the materials

The materials that I used for the project from Framework* were the following:

I also used these 3rd-party products in the process:

Assembly

It was pretty easy to put this all together from a hardware perspective!

1. Put the mainboard in the case

Super easy. It comes with screws to mount it down in the perfect places. You can pop in the RAM/hard drive during this step too, which I was halfway through doing when taking this picture:

2. Add the expansion cards: 2x Ethernet, 1x USB-C to power, 1x USB-C to control dock

Installing the expansion cards is basically just as easy as on any Framework Laptop! You pop them into the 4 spaces on the left and right side of the case and they lock into place:

3. Install Ubuntu

I could’ve installed most flavors of Debian or Ubuntu, including one without a GUI, but I actually wanted a GUI. This way I could just connect a keyboard, mouse, and monitor without having to do any monkeying around in the terminal to install extra requirements and get an X session started. So I opted to just install the latest stable Ubuntu Jammy Jellyfish:

Install and Configure Dokku

I’d been using Dokku for a super long time on my Digital Ocean droplet to host my websites. Dokku is a Platform as a Service (PaaS) that works in a similar way to Heroku. You git push your projects up to your Dokku server and it uses open source buildpacks to bootstrap the application (install Ruby, Rails, nginx, etc). This mini PaaS puts all of the parts of your app in Docker containers with an nginx layer on the front to route requests to them.

The first part of setting things up was simple, I just followed Dokku’s set up instructions.

I pushed my first app up to the IP of my server on the Local Area Network (it actually had 2 IPs on my LAN due to the 2 ethernet cards!) I found out the IP from my Eero app and later on from ip addr.

The first app I pushed had a lot of trouble building. It turned out that the Ruby version and gems that I was using for https://djmuzach.com was super old. I ended up having to update a bunch of versions to get it into the new box. I could’ve probably just cloned the image from my Digital Ocean droplet to avoid this, but figured it was better to get on the latest updates, so I ported some of my apps to the latest versions of Rails. They were all hobby apps that had small codebases so this was pretty simple, just kind of annoying.

Anyway, after navigating my way through dependency hell for my old apps, I got the first app deployed, or so it seemed!

But now a problem presented itself: how can I expose my server to the open internet so that traffic can be routed to the web server Dokku provisions, and other folks on the web can access my websites, instead of just people on my Local Area Network (my wife, the cat, and the dog)?

Part 2: Getting on the Web

Enter Cloudflare Tunnels

Cloudflare Tunnels, formerly known as Argo, can be set up to route a DNS record on Cloudflare to a server that hosts your website(s). To make it happen with just an “n” of one website, I did the following:

  1. Install cloudflared locally on the Framework mainboard and authenticate with Cloudflare
  2. Add a tunnel I called “home-server” on the Cloudflare dashboard
  3. Pick a public domain name to use. I registered a new domain for this, exa.zone, just so I didn’t have to mess up my websites at DigitalOcean while experimenting. To start, each website used a subdomain of exa.zone.
  4. Add the public hostname and service IP to the tunnel on Cloudflare
  5. Cloudflare begins routing traffic to that service IP on your local machine!

Next thing I knew, first https://djmuzach.exa.zone, and shortly after the real domain https://djmuzach.com/ (all the beats fit to play), were being served from my old Framework mainboard!

This was all well and cool, but every time I re-deployed my app or restarted my box, my app ended up with a new local IP. I needed to create a more automated way to fix this that would automatically tell Cloudflare the new local IP of my app (or find a way for Dokku to keep that IP static…but I went for option 1).

Building dokku-cf-tunnel-update to expose my websites to the internet

I created a new Dokku plugin called dokku-cf-tunnel-update that would tell Cloudflare via an API call the new static IP of my websites. I’ve open sourced it so anyone can use it! I started coding it in bash but quickly realized that it would be far easier for me to build and maintain in my native Ruby, so switched to that.

If you try the plugin, be aware that it’ll wipe all current hostnames set up in your tunnel unless they are also configured in the .cf-tunnel-config file that the repo mentions in the README.

You just need to set 3 environment variables CF_ACCOUNT_UUID, CF_TUNNEL_UUID, and CF_AUTH_TOKEN in your home user and Dokku user’s shell environment (usually in ~/.bashrc or ~/.profile) depending on how your system is set up.

I’ve also added a systemd service to the repo that runs a dokku ps:restart when your system restarts, which calls the plugin, and ensures that all IPs are correct.

Now my websites are served from my house

Now my websites are served from my house! Clicking on any of the following will first route you through global DNS infrastructure, then Cloudflare, into their tunnel, into my router, then my Framework mainboard, through nginx, into the Docker container Dokku is hosting my Sinatra or Rails Ruby app on, to the Sinatra or Rails rack-based server:

https://djmuzach.com

https://dialacompliment.com

https://literallyfiftyshadesofgray.com

Neat, eh?

* Disclaimer: Although Framework provided some of the hardware for me to experiment on as an employee, I wrote this post during my own time. Hopefully folks don’t find the links to the Marketplace too self-promotional, but I figured they’d be useful if people wanted to build something similar!