Caddy and TLS certs


Caddy provides a reverse proxy with TLS termination for all internal services at the shop.
You must be on the shop LAN or on the shop VPN to access it. Working with certbot, we can get wildcard certs for all services.

Caddy runs as a container on the c220 on container named caddy with IP

Finally Pi-Hole provides DNS to match FQDN <-> TLS CN. Pi-Hole has IP



Assuming Ubuntu 22, following their install docs:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf '' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf '' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

systemctl enable caddy
systemctl start caddy


From their install docs:

sudo snap install core; sudo snap refresh core

Verify that list-timers will run the renew:

systemctl list-timers|grep certbot

Get the python script and make it executable. This uses the DNS service with some CNAME trickery:

mkdir -p /etc/letsencrypt/
curl -o /etc/letsencrypt/
chmod +x /etc/letsencrypt/
apt install python3
ln -s /usr/bin/python3 /usr/bin/python

First time cert generation w/ DNS update

You only have to do this ONCE!

sudo certbot certonly --manual --manual-auth-hook /etc/letsencrypt/ --preferred-challenges dns --debug-challenges -d \* -d

You'll then be prompted to create a DNS entry, something like:    CNAME

Create this by: 1. SSH to 1. sudo su - 1. vim /etc/bind/master/ 1. edit serial number at top to be today's date 1. add new line for above DNS entry 1. restart DNS with rndc reload

Credentials are now stored in JSON in /etc/letsencrypt/acmedns.json. These are backed up in keepass just in case. Though you could go through above steps again if they're lost.

Back on the certbot box, hit return to continue the validation process. Certs should be created in /etc/letsencrypt/live/

Set default cert in Caddy

In /etc/caddy/Caddyfile declare the top most host as shown below. All subsequent hosts will inherit this cert:

# this host just declared to define default cert all other hosts inherit {
   tls /etc/letsencrypt/live/ /etc/letsencrypt/live/

Adding service

Configure Caddy

Assuming you had a new service at called, you would:

  1. ssh into caddy box
  2. vim /etc/caddy/Caddyfile
  3. add new host entry (and see "Variations on Caddyfile entries" below). Because we declared a default host above, we can just add 3 lines which include the host and IP. It implicitly uses port 80 for IPv4 hosts: {
  4. restart caddy: systemctl restart caddy

Configure Pi-Hole

Set up new DNS entry:

  1. log into pihole
  2. go to custom DNS
  3. add new DNS entry for to resolve to Note that .29 is the IP of caddy, not the IP of the service your proxying.

Variations on Caddyfile entries

Step 3 above in "Configure Caddy" can have other options to support self signed certs and IPv6 hosts (or both!).

Self signed cert

We go from 3 lines to 9. The main difference is that we're telling it which IP with https:// and to ignore self signed certs with tls_insecure_skip_verify: {
   reverse_proxy {
      transport http {

IPv6 entry

Note the use of brackets around the IP [] and port at the end :80. {
        reverse_proxy [fd42:7c97:9426:8f29:216:3eff:fe0a:71c9]:80