Skip to content
Infrastructure

Let's Encrypt Wildcard Certificates For A Homelab

By Victor Da Luz
Let's Encrypt Wildcard ACME DNS-01 Traefik Docker Swarm Cloudflare

Wildcard certificates cover every subdomain under a domain. One cert, many names. For a homelab with a growing service list, that is useful.

Here is a quick primer on what they are, how they work with Let’s Encrypt, and what I set up at home.

What A Wildcard Is

A wildcard cert secures *.example.com. It validates grafana.example.com, nextcloud.example.com, and so on. It does not cover the bare domain example.com unless you also include it as a Subject Alternative Name.

How Let’s Encrypt Issues Wildcards

  • ACME is the protocol the client and CA use to prove control of a domain
  • For wildcards you must use the DNS-01 challenge
  • DNS-01 works by creating a special TXT record under _acme-challenge.example.com
  • The CA looks up the TXT record and, if it matches the challenge, issues the cert

For homelabs this is ideal. You do not need to expose an HTTP endpoint to the internet. You automate DNS and the certs renew on their own.

Why It Helps At Home

  • One certificate for many services behind a reverse proxy
  • Clean URLs for internal and external access
  • Less per service certificate management
  • Easy to add new services without touching the CA

How I Set It Up

The reverse proxy is Traefik on Docker Swarm. The certificate resolver uses DNS-01 with my DNS provider. Here are the steps I used:

  • Deploy Traefik on Swarm with the provider integration
  • Enable the DNS-01 resolver in Traefik
  • Provide the DNS API token as a secret
  • Point services at Traefik with HTTPS labels
  • Verify the first wildcard issuance, then watch auto renewals

This moved me off per service certificates and into a single, automated flow.

Using Cloudflare DNS On The Free Tier

  • Create a scoped API Token in Cloudflare with Permissions set to “Zone DNS Edit” and Resource limited to your zone
  • In Traefik, configure the ACME resolver to use the Cloudflare provider
  • Store the API token as a Docker secret and reference it with the “_FILE” environment variable variant that Traefik expects (for example CLOUDFLARE_DNS_API_TOKEN_FILE)
  • Traefik creates and deletes the _acme-challenge TXT records during issuance and renewal
  • Propagation is usually fast on Cloudflare free tier. Traefik handles waiting and retries

What I Changed

I set this up while preparing an MDM stack. Before that, Traefik used the HTTP challenge for individual certs. I migrated to DNS-01 and wildcard support and cleaned up the config.

  • Deployed Traefik on Swarm and updated to the current provider flags
  • Switched from HTTP challenge to DNS-01 for wildcard issuance
  • Stored the DNS token as a secret and fixed an environment variable mismatch
  • Verified Traefik health and dashboard
  • Standardized URLs in the homepage config so services pointed at the proxy

This also made later work easier. Adding services was a matter of labels and DNS.

Common Pitfalls

  • Wildcards need DNS-01. The HTTP-01 challenge does not work for *.
  • For Cloudflare in Traefik, use the correct variable: CLOUDFLARE_DNS_API_TOKEN or the safer CLOUDFLARE_DNS_API_TOKEN_FILE with secrets
  • Remember to include the root domain as a SAN if you need it
  • Keep an eye on rate limits during testing

Secrets Instead Of Bind Mounts

Use Docker Swarm secrets for API tokens rather than bind mounting files or baking tokens into env vars. Secrets are mounted as in-memory files only to the container that needs them, are not stored in image layers, and do not show up in docker inspect environment output.

Example

# create the secret once
printf '%s' "your-cloudflare-token" | docker secret create cloudflare_api_token -
# in your Traefik service definition
services:
  traefik:
    secrets:
      - cloudflare_api_token
    environment:
      # Traefik reads the token from this path
      - CLOUDFLARE_DNS_API_TOKEN_FILE=/run/secrets/cloudflare_api_token
    # ... other labels and config ...
secrets:
  cloudflare_api_token:
    external: true

This keeps credentials out of compose files and logs, and works well with the “_FILE” env variants many tools support.

What I Would Do Again

  • Keep certificate automation in the proxy instead of per service
  • Use secrets for API tokens as shown above
  • Standardize service URLs early so you do not chase internal IPs later

That is the setup that has worked well for me. Short, simple, and ready to grow with the lab.

Ready to Transform Your Career?

Let's work together to unlock your potential and achieve your professional goals.