Pi-hole is a network-level ad and tracker blocking application that acts as a DNS sinkhole, returning a null address for known ad and tracking domains. This guide deploys Pi-hole using Docker Compose with Traefik handling automatic HTTPS for the admin dashboard, after freeing the system's port 53. By the end, you'll have Pi-hole resolving and sinkholing DNS queries with an HTTPS-secured admin console.
Free Port 53
Ubuntu's systemd-resolved binds port 53 by default. Release it before deploying.
1. Stop and disable systemd-resolved:
$ sudo systemctl stop systemd-resolved
$ sudo systemctl disable systemd-resolved
2. Replace the resolver configuration:
$ sudo rm /etc/resolv.conf
$ echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
Set Up the Directory Structure
1. Create the project directory structure:
$ mkdir -p ~/pihole/{etc-pihole,etc-dnsmasq.d,letsencrypt}
$ cd ~/pihole
2. Create the environment file:
$ nano .env
DOMAIN=pihole.example.com
LETSENCRYPT_EMAIL=admin@example.com
TZ=UTC
Deploy with Docker Compose
1. Add your user to the Docker group:
$ sudo usermod -aG docker $USER
$ newgrp docker
2. Create the Docker Compose manifest:
$ nano docker-compose.yml
services:
traefik:
image: traefik:latest
container_name: traefik
restart: unless-stopped
environment:
DOCKER_API_VERSION: "1.44"
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=traefik-public"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--certificatesresolvers.le.acme.httpchallenge=true"
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.le.acme.email=${LETSENCRYPT_EMAIL}"
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
pihole:
image: pihole/pihole:latest
container_name: pihole
restart: unless-stopped
environment:
TZ: ${TZ}
volumes:
- ./etc-pihole:/etc/pihole
- ./etc-dnsmasq.d:/etc/dnsmasq.d
ports:
- "53:53/tcp"
- "53:53/udp"
labels:
- "traefik.enable=true"
- "traefik.http.routers.pihole.rule=Host(`${DOMAIN}`)"
- "traefik.http.routers.pihole.entrypoints=websecure"
- "traefik.http.routers.pihole.tls=true"
- "traefik.http.routers.pihole.tls.certresolver=le"
- "traefik.http.services.pihole.loadbalancer.server.port=80"
3. Start the services:
$ docker compose up -d
4. Verify the services are running:
$ docker compose ps
Initial Configuration
1. Set the admin password:
$ docker compose exec pihole pihole setpassword
2. Open the dashboard:
Open https://pihole.example.com/admin in a browser and sign in.
3. Permit dashboard access from all origins:
Go to Settings → DNS, toggle Basic to access advanced settings, and select Permit all origins under Interface Settings. Save & Apply.
Warning: Restrict access to port 53 with your firewall — open DNS resolvers can be abused for amplification attacks.
Test Resolution
From any client, confirm blocking and normal resolution:
$ dig @SERVER_IP flurry.com
$ dig @SERVER_IP vultr.com
The first should resolve to 0.0.0.0; the second should return real records.
Next Steps
Pi-hole is running with HTTPS for the dashboard. From here you can:
- Add custom blocklists via Adlists in the dashboard
- Enable conditional forwarding to your LAN router for local hostname resolution
- Point your LAN DHCP server at Pi-hole for network-wide filtering
For the full guide with additional tips, visit the original article on Vultr Docs.
Top comments (0)