Take a Deep
Breath
and
Count to Four
by Arik Devens
Nov 22nd, 2021

Custom HTTPS Subdomains for My Home Server

The problem seemed simple. I run a couple services on a home Mac mini, and I wanted access to them from outside my local network. The solution I came up with was definitely not simple, but I'm really happy with it. A friend asked me to write it up, which was a great idea because I will almost certainly forget how I did this. Thus a blog was born.

I'll describe the end state first, so you know if it's something you're interested in. This site runs on danieltiger.com, and now I've added a few subdomains that I'm using to access my home server. One is for things like ssh or vnc, and one is to use with my Komga server. More importantly, it's using https, which is required to for Panels.app to connect to Komga from my iPad thanks to App Transport Security. This is how I did it.

Caveat: I'm using an Eero mesh router network and their Eero Secure+ service. I think all these instructions should work with any router that supports DDNS, but YMMV. Now on to the good stuff.

A while back Eero added DDNS to their Secure+ service. What this means, is that you can get a domain from Eero, that will always map back to your home network. Using that you can port forward whatever you want, to whatever machines you want, and access them from anywhere. That's great, and at first it seemed like a perfect solution, but there are two important gotchas.

The first is that Eero assigns you the domain. You might not care about that, but I wasn't that excited to have to memorize something like r1560134.eero.online. I own a domain already and I wanted to use that. The second issue is that Eero gives you a domain that only supports http by default. They mention that you can do https, but then they link you to a page on the Let's Encrypt website that I think you have to have built Let's Encrypt to understand.

Ok, so I have two things I want to do. Forward my own https subdomains to my Eero DDNS, and make the Eero DDNS connect over https for Komga, and any other web services I want to run in the future.

The first task was pretty easy. I host this site on Netlify and they provide wildcard TLS by default. All I had to do was add a CNAME record pointing from home.danieltiger.com to r1560134.eero.online, and that part was done.

What that means is that now I can ssh into home.danieltiger.com and that will automatically forward to r1560134.eero.online over port 22, which is already forwarded via the Eero app to the Mac mini server. A bit circuitous I suppose, but it works great. The bigger problem comes when trying to use things that connect over https.

Getting that working was a lot less easy, but mostly because I am not a Docker expert. If I was I think this would likely have taken way, way less time. If you're also setting things up on a Mac, go and install Docker Desktop, which comes with Docker Compose. It makes it way easier to setup the containers you'll be running.

I was already running Komga as a Docker container. What I did was setup another container, to run a service called Caddy. Caddy is a web server that handles https automatically, and supports reverse proxying. What that means is, I can use Caddy to manage the process of getting and refreshing my Let's Encrypt certificate, route all my web traffic over https, and forward to Komga.

Setting up Caddy is pretty easy, but I'm going to assume you have a similar lack of experience with Docker as I do. The first thing to do is to create a directory to store the Caddy data. Mine is under ~/Docker/caddy. Inside you'll also need to make a few subdirectories.

$ mkdir data
$ mkdir config

Next up is to add a docker-compose.yml file, which will tell Docker how to spin up your Caddy container. You can copy mine.

version: "3"

services:
    caddy:
        image: caddy:2-alpine
        restart: unless-stopped
        ports:
            - "80:80"
            - "443:443"
        volumes:
            - ~/Docker/caddy/Caddyfile:/etc/caddy/Caddyfile
            - ~/Docker/caddy/data:/data
            - ~/Docker/caddy/config:/config

Next you need to add a Caddyfile, which will tell Caddy how to proxy your requests. This is what mine looks like, using the example names from earlier.

{
    # Global options block. Entirely optional, https is on by default
    # Optional email key for lets encrypt
    email youremail@yourdomain.com
    # Optional staging lets encrypt for testing. Comment out for production.
    # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
r1560134.eero.online {
    reverse_proxy http://r1560134.eero.online:8080
}
komga.danieltiger.com {
    reverse_proxy http://r1560134.eero.online:8080
}

One important note: that optional staging line is super important to use while testing. Let's Encrypt has pretty aggresive rate limiting and you don't want to accidentally trigger it. Comment it out once you have everything up and running.

Start Caddy with docker-compose up -d and load whatever you used instead of https://komga.danieltiger.com, to connect to your service. Your browser will likely complain about the certificate being invalid, but if you look at it, you'll see it's because it's a staging certificate from Let's Encrypt. If you view the site anyway you'll see your service, and you'll know you're ready to comment out that staging line.

So there you have it. Now I can connect to any of my services, from anywhere in the world, via my own domain, and all over https.