Skip to content

Setting Up a Pihole Docker

pihole-chacho

For some easy-listening learning, I often turn to NetworkChuck's YouTube channel. Recently, I decided to set up Pi-hole, a network-wide ad and tracker blocking application that acts as a DNS sinkhole and optionally as a DHCP server. However, Chuck's video used a Pi-hole image that wasn't ARM architecture compatible. I modified it, pulled the regular pihole/pihole:latest image, and then watched Brandon Lee's VirtualizationHowTo channel for additional insights. Here are my notes from this process:

Option 1: Create the Pihole Container

docker run -dit -p 53:53/tcp -p 53:53/udp -p 80:80 -p 443:443     -v "$(pwd)/etc-pihole:/etc/pihole:z" -v "$(pwd)/etc-dnsmasq.d:/etc/dnsmasq.d:z" --name chacho_pihole pihole/pihole:latest
Explanation:
1. We are running the container with the latest version of Pi-hole, naming it chacho_pihole.
2. Port forwarding is set up so the host forwards traffic to the Pi-hole container. Note: On macOS, mDNSResponder may use port 53 (and possibly 5353). In that case, use an alternative port like 5399 for the host while keeping port 53 for the container.
3. We are mounting two volumes from the current working directory to the container's directories.

Once this container is spun up, and in a healthy status (docker ps to check this), we're ready to visit the Pi-hole interface. Open up a browser and use your docker host IP address like so:

http://10.2.3.4/admin

Option 2: Configure docker-compose.yaml

The benefit of this method is being able to configure SSL using a Traefik container in conjunction with the Pi-hole container.

mkdir pihole && cd pihole && nano docker-compose.yaml

version: '3.3'

services:
  traefik2:
    image: traefik:latest
    container_name: traefik
    restart: always
    ports:
      - "80:80"    # The HTTP port
      - "443:443"  # The HTTPS port
    networks:
      - traefik
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"  # So Traefik can listen to Docker events
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=true"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"

  pihole:
    image: pihole/pihole:latest
    container_name: chacho-pihole
    restart: always
    ports:
      - "5380:53/tcp"
      - "5380:53/udp"
    dns:
      - 208.67.220.220
      - 208.67.222.222
    environment:
      TZ: 'America/New York'
      WEBPASSWORD: 'AdSinker'
      PIHOLE_DNS_: 208.67.222.222;208.67.220.220
      DNSSEC: 'false'
      VIRTUAL_HOST: pihole.cloud.local
      PIHOLE_DOMAIN: pi.hole
    volumes:
      - "~/pihole/etc-pihole:/etc/pihole"
      - "~/pihole/etc-dnsmasq.d:/etc/dnsmasq.d"
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.pihole.rule=Host(`pihole.cloud.local`)"
      - "traefik.http.services.pihole.loadbalancer.server.port=80"
      - "traefik.http.routers.pihole.tls=true"
      - "traefik.http.routers.pihole.entrypoints=websecure"

networks:
    traefik:
      driver: bridge
      name: traefik
      ipam:
        driver: default
        config:
          - subnet: 172.19.0.0/16
Run the following command to start the containers:
docker-compose up -d
This command creates the two Docker containers we configured. Since we are running this on a local network, add the VIRTUAL_HOST to the /etc/hosts file on the Docker host to ensure it resolves:

sudo echo "10.2.3.4 pihole.cloud.local" >> /etc/hosts
And now we're ready to pop open our browser and dump our URL in to bring up the Pi-hole login screen.
http://pihole.cloud.local/admin

Option 3: Set Up Pihole and Traefik with SSL (TLS)

If you are not using Pi-hole over the internet and it's just for your local network, this option is unnecessary. Use this process if you need to host Pi-hole on a domain and want it secured with TLS.
** Note: Although many people still refer to it as SSL, the modern term is TLS. **

mkdir piholeSSL && cd piholeSSL && nano docker-compose.yaml
version: '3.8'

services:
  traefik2:
    image: traefik:latest
    container_name: traefik
    restart: always
    command:
      - "--log.level=DEBUG"
      # Enable the Traefik Dashboard
      - "--api.dashboard=true"
      # Tell Traefik to discover containers using the docker API
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"  # Use false to only expose services explicitly

      # Set up insecure listener to redirect all traffic to TLS
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"

      # Set up LetsEncrypt with ACME DNS-01 challenge via Cloudflare
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"
      - "--certificatesresolvers.letsencrypt.acme.email=${CLOUDFLARE_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"

      # Set up the TLS Configuration for websecure listener
      - "--entrypoints.websecure.http.tls.certResolver=letsencrypt"
      - "--entrypoints.websecure.http.tls=true"
      - "--entrypoints.websecure.http.tls.domains[0].main=yourdomain.com"
      - "--entrypoints.websecure.http.tls.domains[0].sans=*.yourdomain.com"
    environment: 
    CLOUDFLARE_EMAIL:username@email.com
    CLOUDFLARE_DNS_API_TOKEN:<API-Token-String>

    ports:
      - "80:80"    # The HTTP port
      - "443:443"  # The HTTPS port
    networks:
      - traefik
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"  # So Traefik can listen to Docker events

  pihole:
    image: pihole/pihole:latest
    container_name: chacho-pihole
    restart: always
    ports:
      - "5380:53/tcp"
      - "5380:53/udp"
    dns:
      - 208.67.220.220
      - 208.67.222.222
    environment:
      TZ: 'America/New York'
      WEBPASSWORD: 'AdSinker'
      PIHOLE_DNS_: 208.67.222.222;208.67.220.220
      DNSSEC: 'false'
      VIRTUAL_HOST: pihole.cloud.local
      PIHOLE_DOMAIN: lan
    volumes:
      - "~/pihole/etc-pihole:/etc/pihole"
      - "~/pihole/etc-dnsmasq.d:/etc/dnsmasq.d"
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.pihole.rule=Host(`pihole.cloud.local`)"
      - "traefik.http.services.pihole.loadbalancer.server.port=80"
      - "traefik.http.routers.pihole.tls=true"
      - "traefik.http.routers.pihole.entrypoints=websecure"

networks:
    traefik:
      driver: bridge
      name: traefik
      ipam:
        driver: default
        config:
          - subnet: 172.19.0.0/16