Setting up Ghost using Docker and Caddy
After having heard about on it the interwebs for a bit, I finally decided to play around with Caddy myself last summer and see what the fuss was all about.
Reader, I dig it!
I mean, don't get me wrong – let's encrypt is AMAZING and it's no exaggeration that it single-handedly pulled forward HTTPS / TLS proliferation by many years (see their 2023 report). The internet today is more secure thanks to it.
Buuut. There are many different web servers and proxies, many different platforms & operating systems to support, and Python packaging / tooling / performance is still not great. So making it work, let alone work well, is no easy task. Which is to say, my experience using letsencrypt (via certbot) in a container environment (specifically Kubernetes / GKE) in concert with ingress-nginx was mediocre, at best. Things would break in weird ways, and when they did, debugging what was wrong took forever.
So the idea of a web server that also handles SSL certificates was very compelling. And it can act as a reverse proxy. And it has a robust plugin system. Very compelling indeed!
Naturally, the first thing I decided to host with Caddy was this blog!
Step 1: Spin up Ghost in a Docker container
This part is pretty straight-forward. Here's my Docker compose file:
services:
ghost:
image: ghost:latest
restart: always
ports:
# 2368 is the default port Ghost will use in the container
# We'll map this to port 8001 on the local host
- 8001:2368
volumes:
- diwaker-io-data:/var/lib/ghost/content
environment:
# see https://ghost.org/docs/config/#configuration-options
database__client: mysql
database__connection__host: IP_ADDRESS
database__connection__user: DB_USER
database__connection__password: DB_PASS
database__connection__database: DB_NAME
mail__transport: SMTP
mail__options__auth__user: MAILGUN_USER
mail__options__auth__pass: MAILGUN_PASS
mail__options__port: 2525
url: https://diwaker.io
volumes:
diwaker-io-data:
Step 2: Configure Caddy as a reverse proxy
Next, we'll tell Caddy to reverse proxy all traffic to diwaker.io
to port 8001 (which will then be served by the Ghost instance in the Docker container we just setup). Caddy is commonly configured via Caddyfiles:
The Caddyfile is a convenient Caddy configuration format for humans. It is most people's favorite way to use Caddy because it is easy to write, easy to understand, and expressive enough for most use cases.
Here's our Caddyfile:
diwaker.io {
reverse_proxy localhost:8001
}
That's it! Note that nowhere did we specify anything about HTTPS, TLS, configure certificates or certificate authorities – nothing.
Now you just run Caddy, with this Caddyfile and you're all set! Depending on your platform and how you installed Caddy, something like this should work: /path/to/caddy run --environ --config /etc/caddy/Caddyfile
ps: Caddy logs this when exiting. Cute.
{"level":"warn","ts":1705024830.2319794,"msg":"exiting; byeee!! 👋","signal":"SIGTERM"}