Evaggelos Balaskas - System Engineer

The sky above the port was the color of television, tuned to a dead channel

Blog
Posts
Wiki
About
Contact
rss.png twitter linkedin github gitlab profile for ebal on Stack Exchange

Your MacBook can run AI too »
  -  
Oct
10
2025
Setting Up Traefik and Let’s Encrypt (ACME) with LuaDNS in Docker
Posted by ebal at 18:46:59 in blog

🎉 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:

  1. Installing the required containers
  2. Configuring Traefik with LuaDNS DNS‑Challenge
  3. 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 to https://<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 .env to version control.
Use a .gitignore entry 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

  • storage points to the shared certs/ folder.
  • We’re using the staging Let’s Encrypt server – change it to production when you’re ready.
  • dnsChallenge.provider is set to luadns; 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:

  1. Pull traefik:v3.5 and traefik/whoami.
  2. Create the containers, mount volumes, and start Traefik.
  3. Trigger a DNS‑Challenge for whoami.example.org (via LuaDNS).
  4. Request an ACME certificate from Let’s Encrypt.

Tip – Use docker compose logs -f traefik to watch the ACME process in real time.


🚀 Step 4 – Verify Everything Works

  1. Open a browser and go to https://whoami.example.org
    (replace with whatever you set in MYHOSTNAME).

  2. 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
  1. In the browser’s developer tools → Security tab, confirm the certificate is issued by Let’s Encrypt and that it is valid.

  2. Inspect the Traefik dashboard at http://localhost:8080 (you’ll see the whoami router 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/

Tag(s): Traefik, letsencrypt, acme, luadns
    Tag: Traefik, letsencrypt, acme, luadns
Your MacBook can run AI too »
  -  

Search

Admin area

  • Login

Categories

  • blog
  • wiki
  • pirsynd
  • midori
  • books
  • archlinux
  • movies
  • xfce
  • code
  • beer
  • planet_ellak
  • planet_Sysadmin
  • microblogging
  • UH572
  • KoboGlo
  • planet_fsfe

Archives

  • 2025
    • October
    • September
    • April
    • March
    • February
  • 2024
    • November
    • October
    • August
    • April
    • March
  • 2023
    • May
    • April
  • 2022
    • November
    • October
    • August
    • February
  • 2021
    • November
    • July
    • June
    • May
    • April
    • March
    • February
  • 2020
    • December
    • November
    • September
    • August
    • June
    • May
    • April
    • March
    • January
  • 2019
    • December
    • October
    • September
    • August
    • July
    • June
    • May
    • April
    • March
    • February
    • January
  • 2018
    • December
    • November
    • October
    • September
    • August
    • June
    • May
    • April
    • March
    • February
    • January
  • 2017
    • December
    • October
    • September
    • August
    • July
    • June
    • May
    • April
    • March
    • February
    • January
  • 2016
    • December
    • November
    • October
    • August
    • July
    • June
    • May
    • April
    • March
    • February
    • January
  • 2015
    • December
    • November
    • October
    • September
    • August
    • July
    • June
    • May
    • April
    • March
    • January
  • 2014
    • December
    • November
    • October
    • September
    • August
    • July
    • June
    • May
    • April
    • March
    • February
    • January
  • 2013
    • December
    • November
    • October
    • September
    • August
    • July
    • June
    • May
    • April
    • March
    • February
    • January
  • 2012
    • December
    • November
    • October
    • September
    • August
    • July
    • June
    • May
    • April
    • March
    • February
    • January
  • 2011
    • December
    • November
    • October
    • September
    • August
    • July
    • June
    • May
    • April
    • March
    • February
    • January
  • 2010
    • December
    • November
    • October
    • September
    • August
    • July
    • June
    • May
    • April
    • March
    • February
    • January
  • 2009
    • December
    • November
    • October
    • September
    • August
    • July
    • June
    • May
    • April
    • March
    • February
    • January
Ευάγγελος.Μπαλάσκας.gr

License GNU FDL 1.3 - CC BY-SA 3.0