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.
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!