🎉 If you want a self‑contained, production‑ready reverse proxy that automatically provisions TLS certificates from Let’s Encrypt and uses LuaDNS as the DNS provider, you’re in the right place.
Below you’ll find a step‑by‑step guide that walks through:
- Installing the required containers
- Configuring Traefik with LuaDNS DNS‑Challenge
- Running the stack and verifying everything works
TL;DR – Copy the files, set your environment variables, run
docker compose up -d, and point a browser tohttps://<your‑hostname>.
📁 Project Layout
traefik/
├── certs/ # ACME certificates will be stored here
├── docker-compose.yml # Docker‑Compose definition
├── .env # Environment variables for the stack
└── etc_traefik/
└── traefik.yml # Traefik configuration
└── dynamic/ # Dynamic Traefik configuration will be stored here
└── whoami.yml # WhoAmI configuration
Why this structure?
certs/– keeps the ACME JSON file outside the container so it survives restarts.etc_traefik/– keeps the Traefik config in a dedicated folder for clarity..env– central place to store secrets and other runtime values.
🔧 Step 1 – Prepare Your Environment
1. Install Docker & Docker‑Compose
If you don’t already have them:
# Debian/Ubuntu
sudo apt update && sudo apt install docker.io docker-compose-plugin
# Verify
docker --version
docker compose version
2. Clone or Create the Project Folder
mkdir -p traefik/certs traefik/etc_traefik/dynamic
cd traefik
⚙️ Step 2 – Create the Configuration Files
1. docker-compose.yml
services:
traefik:
image: traefik:v3.5
container_name: traefik
hostname: traefik
env_file:
- ./.env
environment:
- TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_EMAIL=${LUADNS_API_USERNAME}
restart: unless-stopped
# Expose HTTP, HTTPS and the dashboard
ports:
- "8080:8080" # Dashboard (insecure)
- "80:80"
- "443:443"
volumes:
- ./certs:/certs
- ./etc_traefik:/etc/traefik
- /var/run/docker.sock:/var/run/docker.sock:ro
healthcheck:
test: ["CMD", "traefik", "healthcheck"]
interval: 30s
retries: 3
timeout: 10s
start_period: 10s
whoami:
image: traefik/whoami
container_name: whoami
hostname: whoami
depends_on:
traefik:
condition: service_healthy
labels:
- "traefik.enable=true"
Why
whoami?
It’s a simple container that prints the request metadata. Perfect for testing TLS, routing and DNS‑Challenge.
2. .env
UMASK="002"
TZ="Europe/Athens"
# LuaDNS credentials (replace with your own)
LUADNS_API_TOKEN="<Your LuaDNS API key>"
LUADNS_API_USERNAME="<Your Email Address>"
# Hostname you want to expose
MYHOSTNAME=whoami.example.org
# (Optional) LibreDNS server used for challenge verification
DNS="88.198.92.222"
Important – Do not commit your
.envto version control.
Use a.gitignoreentry or environment‑variable injection on your host.
3. etc_traefik/traefik.yml
# Ping endpoint for health checks
ping: {}
# Dashboard & API
api:
dashboard: true
insecure: true # `true` only for dev; enable auth in prod
# Logging
log:
filePath: /etc/traefik/traefik.log
level: DEBUG
# Entry points (HTTP & HTTPS)
entryPoints:
web:
address: ":80"
reusePort: true
websecure:
address: ":443"
reusePort: true
# Docker provider – disable auto‑exposure
providers:
docker:
exposedByDefault: false
# Enable file provider
file:
directory: /etc/traefik/dynamic/
watch: true
# ACME resolver using LuaDNS
certificatesResolvers:
letsencrypt:
acme:
# Will read from TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_EMAIL
# Or your add your email address directly !
email: ""
storage: "/certs/acme.json"
# Uncomment the following line for production
## caServer: https://acme-v02.api.letsencrypt.org/directory
# Staging environment (for testing only)
caServer: https://acme-staging-v02.api.letsencrypt.org/directory
dnsChallenge:
provider: luadns
delayBeforeCheck: 0
resolvers:
- "8.8.8.8:53"
- "1.1.1.1:53"
Key points
storagepoints to the sharedcerts/folder.- We’re using the staging Let’s Encrypt server – change it to production when you’re ready.
dnsChallenge.provideris set toluadns; Traefik will automatically look for a LuaDNS plugin.
4. etc_traefik/dynamic/whoami.yml
http:
routers:
whoami:
rule: 'Host(`{{ env "MYHOSTNAME" }}`)'
entryPoints: ["websecure"]
service: "whoami"
tls:
certResolver: letsencrypt
services:
whoami:
loadBalancer:
servers:
- url: "http://whoami:80"
🔐 Step 3 – Run the Stack
docker compose up -d
Docker will:
- Pull
traefik:v3.5andtraefik/whoami. - Create the containers, mount volumes, and start Traefik.
- Trigger a DNS‑Challenge for
whoami.example.org(via LuaDNS). - Request an ACME certificate from Let’s Encrypt.
Tip – Use
docker compose logs -f traefikto watch the ACME process in real time.
🚀 Step 4 – Verify Everything Works
-
Open a browser and go to https://whoami.example.org
(replace with whatever you set inMYHOSTNAME). -
You should see a JSON response similar to:
Hostname: whoami
IP: 127.0.0.1
IP: ::1
IP: 172.19.0.3
RemoteAddr: 172.19.0.2:54856
GET / HTTP/1.1
Host: whoami.example.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-GB,en;q=0.6
Cache-Control: max-age=0
Priority: u=0, i
Sec-Ch-Ua: "Brave";v="141", "Not?A_Brand";v="8", "Chromium";v="141"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Sec-Gpc: 1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 602.13.13.18
X-Forwarded-Host: whoami.example.org
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: traefik
X-Real-Ip: 602.13.13.18
-
In the browser’s developer tools → Security tab, confirm the certificate is issued by Let’s Encrypt and that it is valid.
-
Inspect the Traefik dashboard at http://localhost:8080 (you’ll see the
whoamirouter and its TLS configuration).
🎯 What’s Next?
| Feature | How to enable |
|---|---|
| HTTPS‑only | Add - "traefik.http.middlewares.redirectscheme.scheme=https" to the router and use it as a middlewares label. |
| Auth on dashboard | Use Traefik’s built‑in auth middlewares or an external provider. |
| Automatic renewal | Traefik handles it automatically; just keep the stack running. |
| Production CA | Switch caServer to the production URL in traefik.yml. |
by making the change here:
# Uncomment the following line for production
caServer: https://acme-v02.api.letsencrypt.org/directory
## caServer: https://acme-staging-v02.api.letsencrypt.org/directory
Final Thoughts
Using Traefik with LuaDNS gives you:
- Zero‑configuration TLS that renews automatically.
- Fast DNS challenges thanks to LuaDNS’s low‑latency API.
- Docker integration – just add labels to any container and it’s instantly exposed.
Happy routing! 🚀
That’s it !
PS. These are my personal notes from my home lab; AI was used to structure and format the final version of this blog post.
Original Post is here:
https://balaskas.gr/blog/2025/10/10/setting-up-traefik-and-lets-encrypt-acme-with-luadns-in-docker/