# AdGuard Home answers DNS for the LAN, sending queries upstream over # DNS-over-HTTPS. Caddy serves AdGuard's admin UI over HTTPS, with a # certificate from Let's Encrypt via a Cloudflare DNS-01 challenge. x-logging: &default-logging driver: json-file options: max-size: "10m" max-file: "3" x-common: &common restart: unless-stopped logging: *default-logging security_opt: - no-new-privileges:true services: adguardhome: <<: *common container_name: adguardhome image: adguard/adguardhome:latest mem_limit: 768m # Published on IPv4 only. Docker forwards IPv4 with NAT, which keeps the # client's real address, so allowed_clients in AdGuardHome.yaml can check # it. IPv6 goes through Docker's userland proxy, and every client would # appear to come from the bridge network's private address. ports: # DNS_PORT lets CI run alongside systemd-resolved; leave it unset on the Pi. - "0.0.0.0:${DNS_PORT:-53}:53/tcp" - "0.0.0.0:${DNS_PORT:-53}:53/udp" volumes: # adguard-conf/AdGuardHome.yaml is copied from AdGuardHome.example.yaml # by the playbook on first run. AdGuard rewrites it when you change # settings in the UI. - ./adguard-conf:/opt/adguardhome/conf - ./adguard-work:/opt/adguardhome/work tmpfs: - /tmp healthcheck: # Answered from AdGuard's hosts file, so it doesn't depend on the upstream. test: ["CMD-SHELL", "nslookup localhost 127.0.0.1 >/dev/null || exit 1"] interval: 30s timeout: 5s retries: 3 start_period: 10s networks: - net caddy: <<: *common container_name: caddy # Stock Caddy can't do the Cloudflare DNS-01 challenge, so build it with # the caddy-dns/cloudflare module. build: context: . dockerfile: caddy.Dockerfile # Rebuild on a fresh caddy:2 base, not a stale cached one. pull: true mem_limit: 128m # IPv4 only, for the same reason as AdGuard: the Caddyfile's LAN-only # check needs the client's real address. ports: - "0.0.0.0:80:80" - "0.0.0.0:443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config tmpfs: - /tmp # Holds CLOUDFLARE_API_TOKEN; the playbook writes it with mode 0600. env_file: - .caddy.env depends_on: adguardhome: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:2019/config/ || exit 1"] interval: 30s timeout: 5s retries: 3 start_period: 15s networks: - net volumes: # Caddy's certificates and ACME account. A named volume keeps them across # `docker compose down`, so a redeploy doesn't hit Let's Encrypt rate limits. caddy_data: caddy_config: networks: net: driver: bridge