d4rk notes
Infrastructure

Traefik and AdGuard Homelab Setup

Running Traefik v3 as the reverse proxy for all homelab Docker services alongside AdGuard Home for DNS. How the two connect and why this combo works well.

dami2026-06-02traefikadguarddnshomelabdocker

Status

live

Difficulty

intermediate

Time Required

40m

Last Tested

2026-06-01

Why This Combo

AdGuard Home handles DNS for my local network — ad and tracker blocking, custom local DNS records, and upstream resolver config. Traefik handles reverse proxying for all Docker services — subdomain routing, SSL termination, cert renewal.

They work together like this: AdGuard has a wildcard DNS rewrite pointing *.internal.yourdomain.com to the Traefik host IP. Traefik picks up the request and routes it to the right container based on Docker labels.

Every homelab service ends up at a clean https://service.internal.yourdomain.com with valid SSL. No messing with /etc/hosts, no self-signed cert warnings.

AdGuard Home Setup

Running AdGuard in Docker:

services:
  adguard:
    image: adguard/adguardhome
    container_name: adguardhome
    volumes:
      - ./adguard/work:/opt/adguardhome/work
      - ./adguard/conf:/opt/adguardhome/conf
    ports:
      - "53:53/udp"
      - "53:53/tcp"
      - "3000:3000"
    restart: unless-stopped

After setup, go to Filters → DNS rewrites and add:

*.internal.yourdomain.com → 192.168.1.x

Replace 192.168.1.x with the IP of the server running Traefik. All subdomains on that pattern now resolve to Traefik.

Point your router's DNS at the AdGuard IP so all devices on the network use it.

Traefik Static Config

traefik.yml for the homelab internal setup:

api:
  dashboard: true

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
  websecure:
    address: ":443"

certificatesResolvers:
  cloudflare:
    acme:
      email: your@email.com
      storage: /letsencrypt/acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"

providers:
  docker:
    exposedByDefault: false
    network: proxy

Using Cloudflare DNS challenge here means Traefik can issue wildcard certs for *.internal.yourdomain.com even though the domain isn't publicly accessible — the challenge is validated via DNS, not HTTP.

Traefik Compose

networks:
  proxy:
    external: true

services:
  traefik:
    image: traefik:v3
    container_name: traefik
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./letsencrypt:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik.rule=Host(`traefik.internal.yourdomain.com`)
      - traefik.http.routers.traefik.tls.certresolver=cloudflare
      - traefik.http.routers.traefik.service=api@internal

Labelling Other Services

Any service on the proxy network gets a route with labels:

networks:
  - proxy
labels:
  - traefik.enable=true
  - traefik.http.routers.myapp.rule=Host(`myapp.internal.yourdomain.com`)
  - traefik.http.routers.myapp.tls.certresolver=cloudflare
  - traefik.http.services.myapp.loadbalancer.server.port=8080

Deploy the container and it shows up in the Traefik dashboard immediately.

The Full Flow

  1. Device queries DNS → AdGuard resolves service.internal.yourdomain.com → Traefik IP
  2. Request hits Traefik on 443
  3. Traefik matches hostname to container via Docker labels
  4. SSL terminates with a valid wildcard cert (issued via Cloudflare DNS-01 challenge)
  5. Container receives the proxied request on its internal port

Clean local HTTPS for every service, no cert warnings, no manual DNS entries per service. Add a container with labels and it just works.