Caddy vs Traefik

So for a while now, I’d been using Caddy as my initial proxy and Let’s Encrypt cert management tool. It was fairly lightweight, it auto-created Let’s Encrypt certs (who doesn’t love that?), and the config file was super easy:

    blg.robot-house.us {
      proxy / 192.168.2.45:1313 {
        transparent
      }
    }

That automagically creates a listener on 80/443 on the Caddy host, registers the SAN name with Let’s Encrypt and forwards traffic. Nice and neat. It also provided support for basic auth, which was nice for a few endpoints that weren’t easy to configure with auth on their own.

My concern started (just a little) when Caddy’s license got more restrictive. It doesn’t affect me hosting at home: that’s still free. But dual-purpose licenses feel weird. At the time I thought, “well…someday I’ll get in to this and maybe move from Caddy to something else, but it’s fine for now…” And that’s where my network sat. Running behind Caddy as a proxy, and happy.

More recently, the Raspberry Pi I was running Caddy on starting becoming a little…less stable. This wouldn’t be the first time a Pi had started crapping out on me, and I’ll probably be replacing the MicroSD card in it soon. However, this instability prompted me to consider moving the proxy off the Pi and onto a real host. And starting to plan that migration meant I’d also need to consider whether I wanted to continue using Caddy.

I had another system already running that could easily accomodate my incoming traffic (I mean…I’m the only one who uses almost all these services…if a host can’t handle 1 user, there’s serious problems…), so the migration itself should be fairly straightforward:

  1. Stop Caddy on Pi
  2. Swing port forwarding to new host
  3. Install and configure Caddy (or alternative) on new host
  4. ???

So except for step #3, everthing is easy. But the big questions were “do I still use Caddy?” and “Do I use a container to run the service?”. Question #2 was an easy “yes”; migrating services into containers has made administering my home network much less painful, overall. Question #1…?

Yeah. Why not. Let’s try something else.

At my last job, we’d leveraged Traefik for a dynamic internal routing layer (leaning on Amazon’s ECS for actually deploying containers, along with AWS AutoScaling for dynamically adding/removing hosts…it was actually pretty cool, you know?) and we’d been pretty happy with Traefik’s performance and easy of deployment/management.

So okay, let’s try Traefik. The docs were a little messy around how to use it without dynamic host entries (which was strange, I’m used to dynamic stuff being an afterthought), but it only took about 20-30 minutes to get a container up and running my new proxy, so not too shabby.

My final Traefik config:

debug = false

logLevel = "WARN"
defaultEntryPoints = ["https","http"]

[api]
  entryPoint = "traefik"
  dashboard = true
  address = ":8080"

[entryPoints]
  [entryPoints.http]
  address = ":80"
  compress = true
    [entryPoints.http.redirect]
    entryPoint = "https"
  [entryPoints.https]
  address = ":443"
  compress = true
  [entryPoints.https.tls]

[file]

[backends]
  [backends.backend1]
    [backends.backend1.servers.server0]
      url = "http://192.168.2.45:8085"
  [backends.backend2]
    [backends.backend2.servers.server0]
      url = "http://192.168.2.45:8081"
  [backends.blg]
    [backends.blg.servers.server0]
      url = "http://192.168.2.45:1313"
[frontends]
  [frontends.backend1]
    entryPoints = ["http", "https"]
    backend = "backend1"
    passHostHeader = true
    [frontends.backend1.routes.rule]
      rule = "Host:<>"
  [frontends.backend2]
    entryPoints = ["http", "https"]
    backend = "backend2"
    passHostHeader = true
    [frontends.backend2.routes.rule]
      rule = "Host:<>"
    [frontends.backend2.auth.basic]
      removeHeader = true
      usersFile = "/htpasswd"
  [frontends.blg]
    entryPoints = ["http", "https"]
    backend = "blg"
    passHostHeader = true
    [frontends.blg.routes.rule]
      rule = "Host:blg.robot-house.us"

[acme]
email = "<email>"
storage = "acme.json"
entryPoint = "https"
acmeLogging = false
onDemand = false
onHostRule = true
[acme.httpChallenge]
entryPoint = "http"
[[acme.domains]]
  sans = ["<>", "blg.robot-house.us", "<>"...]

A few notes:

  1. Yikes! That’s much longer!
  • True, but it also does some things Caddy wasn’t doing.
  1. I appreciate that I can now enable compression.
  2. It’s also nice to be able to have basic auth passwords encrypted, rather than plain text in the config file. I’m not sure if Caddy had actually addressed this…