This is a how-to guide inspired by “Quick and Easy SSL Certificates for Your Homelab!”" focus on Traefik reverse proxy server. Although this guide focuses on DuckDNS, a similar configuration can apply to any DNS provider.

blog-14-1

The trick is by setting private IP for the domain name you own, in my case DuckDNS.

Get DuckDNS Sub-Domain

Sign-up an account in duckdns.org, choose a sub-domain(As I choose a dummy sub-domain lser.duckdns.org) and add your home server IP address(In my case 192.168.0.120)

blog-14-1

Copy token which will be used later for DNS challenge

blog-14-1

Configure Traefik

Reference docs https://doc.traefik.io/traefik/https/acme/

Define certificatesResolvers

Below is an example toml config snippet. You can find the complete configuration in my Github repo veerendra2/raspberrypi-homeserver/services/traefik/config/traefik.toml

...
[certificatesResolvers.whatever-resolver-name-here.acme]
  email = "veerendra2@github.com"
  storage = "/etc/traefik/acme.json"
  [certificatesResolvers.whatever-resolver-name-here.acme.dnsChallenge]
    provider = "duckdns"
    resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
...

I defined certificatesResolvers with name whatever-resolver-name-here with dnsChallenge(DNS challenge)

  • email - A manditory option to identify user
  • storage - Storage file location to store private key and other certificate details
  • provider - duckdns (List of other dns provider supported by Traefik here)

SSL Certificate for the Domain

As Traefik docs says

Defining a certificate resolver does not result in all routers automatically using it. Each router that is supposed to use the resolver must reference it.

Use defined certificatesResolvers(In my case, it is whatever-resolver-name-here) by adding it in labels like below docker-stack.yml

πŸ‘‰ You can also find below docker swarm docker-stack.yml in my Github repo. https://github.com/veerendra2/raspberrypi-homeserver/tree/main/services/traefik

...
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=network_public"
        - "traefik.http.routers.api.rule=Host(`lser.duckdns.org`) && PathPrefix(`/dashboard`) || Host(`lser.duckdns.org`) && PathPrefix(`/api`)"
        - "traefik.http.routers.api.tls.certresolver=whatever-resolver-name-here"
        - "traefik.http.routers.api.entrypoints=https"
        - "traefik.http.routers.api.tls=true"
        - "traefik.http.routers.api.service=api@internal"
        - "traefik.http.services.dummy.loadbalancer.server.port=9999"
    env_file:
      - .env_traefik
    secrets:
      - duckdns
...

As you see in the above docker-stack.yml, I mentioned duckdns secret. To complete the DNS challenge, you must pass the DuckDNS token you copied earlier as environmental variables to Traefik

πŸ‘‰ Reference docs https://go-acme.github.io/lego/dns/duckdns/

Here is a directory tree for a better understanding

veerendra at atom in ~/raspberrypi-homeserver/services/traefik on main
$ tree .
.
β”œβ”€β”€ config
β”‚Β Β  β”œβ”€β”€ acme.json
β”‚Β Β  └── traefik.toml
β”œβ”€β”€ docker-stack.yml
β”œβ”€β”€ network.yml
└── secrets
    └── duckdns.txt

3 directories, 7 files

$ cat .env_traefik
DUCKDNS_TOKEN_FILE=/run/secrets/duckdns

$ cat secrets/duckdns.txt
[REDACTED]

Once everything is placed, deploy the stack by running below command

$ docker stack deploy -c docker-stack.yml traefik
...

That’s it!, Traefik completes the DNS challenge and gets a SSL certificate for your DuckDNS domain. If you want to create a sub-domain for any other service, you can simply add a domain name and resolver in docker labels like below

...
        - "traefik.http.routers.api.rule=Host(`my-service.lser.duckdns.org`)"
        - "traefik.http.routers.api.tls.certresolver=whatever-resolver-name-here"
        - "traefik.http.routers.api.entrypoints=https"
...