Self Hosting Foundations

Self Hosting Foundations

I've always had a passion for free and open source software. Naturally this has evolved into a self-hosting obsession. Some softwares that would otherwise be gated behind large upfront costs or subscription fees have perfectly serviceable free alternatives, at the cost of having to administer them yourself.

I have a couple of machines running Ubuntu that function as servers. They live in a home network, and while one could forward ports and expose them to the WAN, I choose not to do that out of an abundance of caution. Instead I use what I call a "gateway machine".

The gateway machine is actually a VM run on the cloud, specifically the cheapest one I could find that I liked. I've used DigitalOcean as a cloud provider for work and found them reliable and reasonably priced, and their wide range of tutorials is also fantastic and has guided me through many processes. The gateway is a single core "droplet" instance with minimal memory and costs $4 USD per month.

The main function of the gateway is to have a static, public IP address that my domain (lchapman.dev) is pointed to. Ports are opened as needed, but I try to avoid exposing any more ports than are strictly necessary, so 443 is the main one. ".DEV" domains are HSTS domains (HTTP Strict Transport Security) which means the browser will reject any connections that aren't utilising TLS (Transport Layer Security), so all traffic must be properly encrypted. Encrypted web traffic conventially uses port 443.

Your best friend for encrypting web traffic and facilitating HTTPS is Let'sEncrypt, the free and open certificate authority. The method I learned first for achieving HTTPS traffic was using certbot to generate certificates, then using Nginx to encrypt and serve content with those certificates. Nginx is as old as the hills and all powerful but there are modern alternatives that I find much more comfortable - Nginx is still the car you take to the track to race, but not my preference for a daily driver.

Caddy Server (specifically Caddy 2) does everything I need and does it in a human-friendly way, so I'm very happy to have discovered it.

Each VM or machine that I run that serves any kind of content runs Caddy which listens to incoming traffic and handles them based on rules. My gateway machine receives all incoming traffic and Caddy handles it all, routing it wherever it needs to go.

Here's a snippet from the gateway Caddyfile (config):

www.lchapman.dev, lchapman.dev {
        redir https://blog.lchapman.dev
}

blog.lchapman.dev {
        reverse_proxy 10.8.0.2
}

It's short, simple and readable. All traffic to www.lchapman.dev or lchapman.dev is redirected to blog.lchapman.dev. Requests to blog.lchapman.dev are passed to 10.8.0.2, a machine on a local network. Any further domains that need to be routed are done so with similar directives.

Using a reverse proxy to send traffic to 10.8.0.2 means that clients only know they are interacting with blog.lchapman.dev and nothing more, but the actual blog is hosted elsewhere. Ghost is the blog software and is hosted on a machine connected to a VPN.

The only software running on the gateway machine besides Caddy is an OpenVPN server. All machines that I host softwares on are connected as clients to that VPN. One of the benefits of this configuration are that I can pick up my machines and move them to a new location without having to reconfigure my network - they reconnect to the VPN and everything works as before. This flexibility is very handy for self-hosted machines.

All that remains to discuss here are the servers that host my softwares. Currently I don't run them as a cluster but I plan to down the track. Each machine receives requests that are handled by Caddy. Here's an example:

http://blog.lchapman.dev {
        reverse_proxy localhost:2368 {
                trusted_proxies private_ranges
        }
}

Note how HTTP is specified: HTTPS is terminated at the gateway and all of my self-hosted services don't need to worry about TLS or certificates. I do need to turn off TLS for some softwares, e.g. GitLab, but that's usually provided as a config option.

The unencrypted request is received and passed on to Ghost on port 2368. Without specifying "trusted_proxies private_ranges", Ghost will try to redirect requests to https and there will be a request loop, but that's a story for another post.

Check out Traffic Management with Caddy for a full writeup about how I use Caddy!