At Firmhouse we have a product called Airstrip, which lets people quickly build a website to test and launch their new business proposition. In our current product sprint, we're improving a feature that allows people to automatically map a custom domain to the website they build with Airstrip. This post explains how we're doing that.
We've had a custom domain mapping feature in there for quite a while now, but it wasn't properly automated yet. How it would work is that we would instruct people to get in touch with our support team. We would then tell them to point their DNS records to our IP or CNAME and we would then manually configure their domain by adding a site to our Dokku instance and re-running
$ dokku letsencrypt.
This way of working is long and requires quite some manual work. There's also no "instant gradification" for our user. It's quite the bummer if you have to wait more than a day before your custom domain is fully working!
We've also hit the limitations of using the Dokku Letsencrypt plugin for this. Mainly: if one of the domains added to our Airstrip app on Dokku has the wrong DNS records set up, then all domains would fail to renew the certificate.
So what's our new plan?
After some researching and consideration, we're going to add Traefik to our infrastructure. Traefik is a "Cloud Native Edge Router" (it says so on their website). Now I'm not fully sure what that means but it turns out it's pretty awesome as a thin layer in front of our current infrastructure for custom domain mapping and automatically LetsEncrypt'ing those domains at the same time! Everything we need for now.
Traefik will be put in front of our current infrastructure that is run on DigitalOcean droplets. The web and app server droplets are provisioned and maintained via Ansible and Intercity. The droplets run Dokku for app deployment and webserver configuration. We'll add another droplet that runs just Traefik.
We'll configure Traefik with a frontend per custom domain added by our users. Whenever someone adds their custom domain in the Airstrip user interface, we'll fire an API call to the Traefik REST api endpoint. All of the frontends that are created this way, are then connected to a single backend: the Airstrip app server we already have in place. Traefik will then take care of requesting a new LetsEncrypt certificate for every custom domain added to Traefik's configuration.
To illustrate, here's a Treafik configuration file to indicate how this would work:
# Backends [backends] [backends.backend1] [backends.backend1.servers] [backends.backend1.servers.server1] url = "https://airstrip.firmhouse.com:443" weight = 1 # Frontends [frontends] [frontends.frontend1] backend = "backend1" passHostHeader = true [frontends.frontend1.routes] [frontends.frontend1.routes.route0] rule = "Host:traefik.firmhouse.com" [frontends.frontend2] backend = "backend1" passHostHeader = true [frontends.frontend2.routes] [frontends.frontend2.routes.route0] rule = "Host:traefik2.firmhouse.com"
It's pretty awesome that Traefik can take all of this off our hands. We were initially considering writing some custom code to dynamically add/remove domains to Dokku or updating our webserver and LetsEncrypt configurations via other methods. Setting up a small Traefik droplet in front of our infrastructure is only a minor effort compared to that.
Next up is looking into how we can attach a Key/Value store to Traefik so that our dynamic configration doesn't get lost every time we restart the Treafik server or we need to upgrade the droplet.