How to Setup Caddy + DuckDNS for Local HTTPS
After starting few containers on Docker, the one problem nobody mentions, and quite frankly, none of the installation guides talk about, is the web UI management. Not a management UI of a single project, but managing the UIs, the plural. More and more user friendly open source projects are adopting web UI as baseline, yet, the only method these projects openly recommend is connecting via IP address. If you were using a NAS like I am, it would end up looking like this: NAS_ip_address:port. Now don’t forget all the other ports NAS is already using, along with additional ports these Docker images may need.
I’ve already talked about why I am using Caddy and DuckDNS, individually. Combined, not only they provide local HTTPS functionality, it’s more sane way to manage ports. If I were to do something similar on Synology DSM, not only it was unreliable at times, it doesn’t give me one clean file to manage all the ports. I’ll explain it later.
This is how the compose.yaml would look like. It should be in the folder with .env, Caddyfile, and Dockerfile, all of them no extensions.
#compose.yaml
services:
caddy:
build: .
container_name: caddy
restart: unless-stopped
ports:
- ${HTTP_PORT}:80
- ${HTTPS_PORT}:443
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
environment:
- DUCKDNS_TOKEN=${DUCKDNS_TOKEN}
- NAS_IP=${NAS_IP}
networks:
- caddy_net
volumes:
caddy_data: null
caddy_config: null
networks:
caddy_net:
driver: bridge
name: caddy_net
On the DuckDNS side of things, once you have registered, get a DDNS domain and API token from the service. And set the IP address of the domain to the local (not public) address of the home server (or NAS). The token will go into the .env. Do not share the token. Don’t forget to add your own values for ports and IP in .env.
#.env
HTTP_PORT=
HTTPS_PORT=
DUCKDNS_TOKEN=
NAS_IP=
And then we will add DuckDNS plugin — as it is hinted in the yaml — so it may serve the HTTPS.
#Dockerfile
FROM caddy:builder AS builder
RUN xcaddy build \
--with github.com/caddy-dns/duckdns
FROM caddy:latest
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
The actual settings for Caddy lives in Caddyfile. And you can already see why this is more manageable. I added an example service, but you can keep adding on with just few lines. Restart the Caddy if you want to apply the settings. All the values starting with some means it takes its own value.
#Caddyfile
{
acme_dns duckdns {env.DUCKDNS_TOKEN}
}
someService.someDomainfromDuckDNS.duckdns.org {
reverse_proxy {env.NAS_IP}:somePort
}
It’s quite straightforward and rigid. To be honest, I wasn’t sure about yet another Docker container for other containers, but it turned out a lot better than I have anticipated. Having a one file where I could see which container’s web UI is assigned to what port is a nice bonus. Having a readable and functioning DDNS on a local network really just solves most of home lab management nightmares.

Comments will be automatically closed after 30 days.