Migrating My Blog from Traefik to Caddy

Previously I wrote about how I setup the infrastructure for this blog and other websites I'm hosting, but I focused on the Terraform and DigitalOcean configuration.

Provisioning this Blog on DigitalOcean
I recently rewrote the infrastructure for this blog, which was long overdue. Myprevious server had fallen into the trap of pet vs cattle[https://cloudscaling.com/blog/cloud-computing/the-history-of-pets-vs-cattle/],so it was difficult to manage, upgrade, etc. Disclaimer: I’ve included a referra…

I never got around to posting a follow up about the software side of the configuration, but I just migrated off of Traefik so I wanted to explain the before and after.

Traefik

Traefik is a reverse proxy.  From their GitHub:

Traefik (pronounced traffic) is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy. Traefik integrates with your existing infrastructure components and configures itself automatically and dynamically.

Since I wanted to have multiple applications running on the same VM, I needed a way to route between different applications.  This is what I used Traefik for.

It has two main benefits that worked well for me:

  • Easy HTTPS with Let's Encrypt automation
  • Auto-configures itself based on Docker labels

Both of these worked great, and once I had it setup, I was able to add additional Docker apps with minimal fuss, just by adding labels to the Docker configuration.

Here are the important bits from my traefik.toml file:

################################################################
# Entrypoints configuration
################################################################

[entryPoints]
  [entryPoints.web]
    address = ":80"
  [entryPoints.web.http]
    [entryPoints.web.http.redirections]
      [entryPoints.web.http.redirections.entryPoint]
        to = "websecure"
        scheme = "https"

  [entryPoints.websecure]
    address = ":443"
    
################################################################
# Docker configuration backend
################################################################

# Enable Docker configuration backend
[providers.docker]

################################################################
# Let's Encrypt
################################################################

[certificatesResolvers.davebaumanio.acme]
  storage = "/etc/traefik/acme/acme.json"

  [certificatesResolvers.davebaumanio.acme.dnsChallenge]
    provider = "digitalocean"
    delayBeforeCheck = 0

The first section enables HTTP/HTTPS entrypoints and automatically redirects to HTTPS, and the second section enables support for Docker.

The third configures Let's Encrypt with a DNS challenge, and I pass in a DigitalOcean API token in the environment variable DO_AUTH_TOKEN and it just works.

To host this blog, I just needed to launch the Ghost Docker container with the following labels:

    labels:
      traefik.http.routers.ghost.rule: "Host(`blog.davebauman.io`)"
      traefik.http.routers.ghost.entrypoints: "websecure"
      traefik.http.routers.ghost.tls.certresolver: "davebaumanio"
      traefik.http.routers.ghost.tls.domains[0].main: "davebauman.io"
      traefik.http.routers.ghost.tls.domains[0].sans: "*.davebauman.io"

Since I setup a wildcard cert, I could use the same certificate for this blog and other apps running on that domain.

If I wanted another app, it would look the same except the Host() rule would need to change.

Overall this worked well, and I never had any issues once I completed the initial setup, and it was easy to scale out to additional apps without any fuss.

Caddy

Traefik is a solid reverse proxy, but it is not a web server.  This was fine initially, since I was putting it in front of applications with their own servers (Ghost, Koken, etc).

But I recently wanted to host a static site, so I started looking into options.  The most obvious is to just run Nginx or another web server in a Docker container, and use Traefik to route to it.  

There are also some not-really maintained projects to integrate a web server with Traefik, but there is no built-in functionality for this, and I didn't want to add a dependency that might not get updated or not work with a future version of Traefik.

So I setup Nginx, which was easy enough to configure and worked well.  But by the time I setup a second site in Nginx, I started to think about why I was running Traefik to route to different apps, and Nginx to route to different virtual hosts.  Could I simplify my stack and do it all in one?

As it turns out, yes.  Traefik is a reverse proxy not a web server, but many web servers are also reverse proxies.  Nginx and Caddy are the ones I'm most familiar with, but probably most web servers can do this.

I hadn't used Caddy before, but I'd heard a lot of good things about it so I decided to abandon both Traefik and Nginx and see if Caddy could replace them both.

My Caddyfile has gotten a little longer with things like logging, compression, and cache control, but this is basically what I started with:

static.davebauman.io {
  root * /srv/static
  file_server
}


blog.davebauman.io {
  reverse_proxy ghost:2368
}

This creates two hostnames, one of which is served out of a folder, and the other is routed to my Ghost container.  Since I'm running Caddy in Docker, I can reference other containers in the same network by name.

Caddy also provides Let's Encrypt integration out of the box—I didn't even have to configure it, it just worked.  I don't have wildcard support; that would require additional setup, but I'm not even sure I need that.

There is a popular module caddy-docker-proxy that provides the same Docker label integration as Traefik, but I decided not to use it since I wasn't sure if I could use both Docker labels and a Caddyfile.  Since I'm new to Caddy I wanted to stick with a Caddyfile to make it easier to learn and debug.

Conclusion

It's not really a surprise that I could use Caddy to replace the combination of Traefik and Nginx.  I could have done the same with Nginx instead, but I really liked how easy it was to configure and use Caddy.  It's really just that good.

Traefik has a lot of features I wasn't using, and the features I was using have been easily replaced.  While I lost the Docker label support, I'm not running enough sites or changing the configuration frequently enough for that to matter (and I could add that to Caddy if I wanted).  The biggest advantage to me is that I have a simpler tech stack to maintain.

I didn't do any benchmarking on this; allegedly Caddy uses more memory that Nginx, but it probably doesn't use more memory than both Traefik and Nginx combined, so overall I should be ahead.

Now, all the traffic to my VM goes through Caddy, and is either routed to another Docker app, or served directly from the disk.  Easy!