Reverse Proxying Podman Quadlets with Caddy
This is a momentous occasion for me. This evening I was able to finally finish setting up a reverse proxy for my podman quadlets on my home server. It only took about an hour of futzing around... using Caddy. For context, this is the fourth time I've attempted this dream setup of mine over the years.
The first three times, I tried using Traefik because it's supposed to be so container-friendly. The allure of your service mesh configuration living in tags on your containers is hard to ignore. Sadly, I could never get it to fit with my particular requirements, which I outlined on a previous post about setting up multiple mDNS A records on a single machine with a dynamic IP address. The snag I hit this time is that rootless podman doesn't play nice with Traefik's docker provider, eliminating any real advantage over other reverse proxy solutions.

I've probably spent days trying to get Traefik to work. It took me an hour to get Caddy running exactly how I wanted it. I'm impressed.
The first thing I had to do was create a place for my Caddyfile and Caddy's data to live.
mkdir ~/caddy
mkdir ~/caddy/conf
mkdir ~/caddy/data
~/caddy/conf/Caddyfile
{
skip_install_trust
}
http://wiki.local {
reverse_proxy tiddlywiki:8080
}
http://plex.local {
reverse_proxy host.containers.internal:32400
}
irc.local {
reverse_proxy thelounge:9000
}
I have the skip_install_trust option set since this is running inside a container.
Plex is routed to host.containers.internal because my Plex quadlet runs on the host network.
I'm not using TLS for the wiki or plex for simplicity sake, but I am using it for my installation of The Lounge in order to allow for push notifications. I can only issue self-signed certificates for .local DNS names. Therefore, I need to share Caddy's root.crt to devices where I plan to use The Lounge. That's a minority of devices, so it's easier to only use TLS where necessary.
In order for Caddy to be able to resolve the Tiddlywiki container, both quadlets must share a podman network.
~/.config/containers/systemd/proxy.network
[Unit]
Description=Podman network for Caddy services
After=network.target
[Network]
NetworkName=proxy
[Install]
WantedBy=default.target
This is actually a pattern from Traefik that I nicked. If I want a service to be accessible by Caddy, both Caddy and the service need to share this network to talk to each other. My Tiddlywiki gets Network=proxy added to its quadlet file and its PublishPort setting removed so it doesn't leak any ports onto the host network. My Plex server doesn't need anything special since it's already using Network=host.
Remember to load it.
systemctl --user daemon-reload
systemctl --user start proxy-network.service
~/.config/containers/systemd/caddy.container
[Unit]
Description=Caddy
After=network.target
[Container]
ContainerName=caddy
Image=docker.io/library/caddy:2-alpine
AutoUpdate=registry
UserNS=keep-id
Network=proxy
Volume=/home/server-user/caddy/conf:/etc/caddy:Z
Volume=/home/server-user/caddy/data:/data/caddy:Z
PublishPort=80:80/tcp
[Service]
Restart=always
[Install]
WantedBy=default.target
Nothing particularly special about the actual Caddy quadlet. Remember to load it.
systemctl --user daemon-reload
systemctl --user start caddy.service
I chose to add http://plex.local:80 to Plex's settings menu at "Settings" -> "Network" -> "Custom server access URLs". I don't know if that's strictly necessary, but it seems to be recommended.
If you can believe it, that's all it takes. It works like I've always dreamed.