xcaddy
xcaddy is awesome for setting up your LetsEncrypt certs with a DNS challenge (handy if you don't often - or ever - want to expose port 80/443 to the www)
Install xcaddy
# Get regular caddy first so xcaddy compiles right
wget https://github.com/caddyserver/caddy/releases/download/v2.11.0-beta.1/caddy_2.11.0-beta.1_linux_amd64.tar.gz
tar -xzvf caddy_2.11.0-beta.1_linux_amd64.tar.gz
# Then download/extract xcaddy
wget https://github.com/caddyserver/xcaddy/releases/download/v0.4.5/xcaddy_0.4.5_linux_amd64.tar.gz
tar -xzvf xcaddy_0.4.5_linux_amd64.tar.gz
# Download/install go
sudo apt install golang -y
# Build the custom xcaddy
./xcaddy build \
--with github.com/mholt/caddy-dynamicdns \
--with github.com/caddy-dns/cloudflare
Get a CloudFlare API token
Do that here:
- Click Create Token.
- Under API token templates, look for Edit Zone DNS and click Use Template
- At the next screen, leave Permissions settings as is, and under Zone Resources, leave the default of Include and Specific Zone and then in the blank drop-down box, choose the domain you want to assign the token to.
- (Optional) Under Client IP address filtering add the IP(s) you want to limit token use to.
Create the Caddy file
Here's an example:
some.cool.site.com {
tls {
dns cloudflare {env.CF_API_TOKEN}
}
reverse_proxy reverse_proxy http://127.0.0.1:8000
log {
output file /var/log/caddy/yoursiteslog.log
}
}
Run xcaddy
CF_API_TOKEN=xxx sudo --preserve-env=CF_API_TOKEN ./caddy run --config ./Caddyfile
Troubleshooting
Token changes in summer 2026 (?)
In summer of 2026, a bunch of my LetsEncrypt-enabled Caddy configs had certs that started to expire. After reading this post I discovered I needed to update the xcaddy plugins:
./xcaddy build \
--with github.com/mholt/caddy-dynamicdns \
--with github.com/caddy-dns/cloudflare
Then the cert renewals worked!
Upgrade golang
When I was trying to compile xcaddy, I got complaints about not being able to compile the github.com/mholt/caddy-dynamicdns component. The fix was:
# 1) grab latest 1.25.x (adjust version if you want)
cd /tmp
wget https://go.dev/dl/go1.25.5.linux-amd64.tar.gz
# 2) install to /usr/local/go
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.25.5.linux-amd64.tar.gz
# 3) ensure your shell uses it
echo 'export PATH=/usr/local/go/bin:$PATH' >> ~/.profile
source ~/.profile
go version