Caddy for Astro + Podman (HTTPS and Canonical Host)
How to put Caddy in front of a Podman-hosted Astro SSR app with automatic TLS and www redirect.
Category: On-Premises & Private Cloud
Once the Astro container was answering on 127.0.0.1:4321, I still needed HTTPS and a single canonical hostname. I picked Caddy over nginx for exactly one reason — its built-in ACME client means I never touch certbot timers or renewal cron jobs.
This post is the thin layer that turns the private app port into a public HTTPS site.
Install Caddy
Caddy isn’t in the default Debian repos, so I added Cloudsmith’s stable channel:
sudo apt update
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list >/dev/null
sudo apt update
sudo apt install -y caddy
Reverse proxy and canonical host
Problem: Two DNS names pointed at the VPS — adrian-altner.de and www.adrian-altner.de. I wanted exactly one to live publicly, with the other 301-ing to it.
Implementation: Caddy’s site blocks do both jobs at once — TLS issuance and the redirect — with no extra config.
# /etc/caddy/Caddyfile
www.adrian-altner.de {
redir https://adrian-altner.de{uri} permanent
}
adrian-altner.de {
encode zstd gzip
reverse_proxy 127.0.0.1:4321
}
Solution: Caddy fetches Let’s Encrypt certificates for both hostnames on first boot, serves adrian-altner.de via the reverse proxy, and answers www.adrian-altner.de with a permanent redirect.
Validate and reload
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl restart caddy
sudo systemctl enable caddy
sudo systemctl status caddy --no-pager
A formatting warning on validate is harmless — caddy fmt cleans it up in place:
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
Verifying the result
curl -I http://adrian-altner.de
curl -I https://adrian-altner.de
curl -I https://www.adrian-altner.de
What I was looking for:
- HTTP redirects to HTTPS (Caddy does this automatically once TLS is in place).
wwwreturns a 301 to the apex domain.- The TLS certificate is trusted and issued by Let’s Encrypt.
Network hygiene
With Caddy as the only public entrypoint, there’s no reason for port 4321 to be reachable from the outside world. Binding it to localhost in compose closes that door:
ports:
- "127.0.0.1:4321:4321"
Now the Node process only listens on the loopback interface — Caddy alone handles 80/443.
What to take away
- Caddy collapses “reverse proxy”, “TLS”, and “canonical host” into a handful of lines of config.
- Automatic HTTPS means one less cron job — no certbot, no renewal hooks.
- Bind the app container to
127.0.0.1as soon as a proxy sits in front of it; there’s no upside to exposing it publicly. - One canonical hostname with the other redirecting avoids duplicate-content SEO surprises later.