Run Pi-hole Docker in Ubuntu for Family Internet Safety and Ads Blocking - NETSEC


Learning, Sharing, Creating

Cybersecurity Memo

Sunday, July 4, 2021

Run Pi-hole Docker in Ubuntu for Family Internet Safety and Ads Blocking

Pi-hole or Pihole is a Linux network-level advertisement and Internet tracker blocking application[2][3][4][5] which acts as a DNS sinkhole[6] and optionally a DHCP server, intended for use on a private network.[1] It is designed for low-power embedded devices with network capability, such as the Raspberry Pi,[3][7] but supports any Linux machines.[6][8][9][10]

Pi-hole has the ability to block traditional website advertisements as well as advertisements in unconventional places, such as smart TVs and mobile operating system advertisements.[

- from Wikipedia

In this post, I summarized all necessary steps to set up my home Pi-hole server. 

Install Ubuntu, Config Static IP and Update System

Set up Static IP :

root@hpthin:~# cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
      addresses: []
         addresses: [,]
  version: 2

When editing Yaml files, make sure you follow the YAML code indent standards. If the syntax is not correct, the changes will not be applied.

Once done, save the file and apply the changes by running the following command:

  • sudo netplan apply

Update Ubuntu 20.04 8system to latest:

 [root@OCP1-Ubuntu ~]# apt update -y && apt upgrdate -y

Install Docker, Docker-Compose and Portainer

Install Docker on Ubuntu 20.04:

#Ubuntu 20.04
sudo apt install
Install Docker Compose on Ubuntu 20.04:
#Ubuntu 20.04
sudo apt install docker-compose
Please make sure your VPS's firewall port 80, 443 and 9000 has been opened. We can close 9000 later.
[root@ubuntu20 ~]# docker volume create portainer_data
[root@ubuntu20 ~]# docker run -d -p 9000:9000 --name portainer --restart always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
Unable to find image 'portainer/portainer-ce:latest' locally latest: Pulling from portainer/portainer-ce 94cfa856b2b1: Pull complete 49d59ee0881a: Pull complete f220caeff793: Pull complete Digest: sha256:67e3edef02ba465d18663cd273cc24ec2764b27686ea5afbe8f392317a70ed72 Status: Downloaded newer image for portainer/portainer-ce:latest d0ff883b063156b5929a8999593d38837501e6c16ffcefcbefb221ebe0301a32 [root@ubuntu20 ~]#
Verify Portainer from Internet by visiting http://<VPS's Public IP>:9000

Free Up Port 53, Used By systemd-resolved

Ubuntu has systemd-resolved listening on port 53 by default. In case you want to run your own DNS server, you can't because port 53 is already in use, so you'll get an error similar to this: "listen tcp bind: address already in use".

$ sudo lsof -i :53

systemd-r 610 systemd-resolve   12u  IPv4  19377      0t0  UDP localhost:domain 
systemd-r 610 systemd-resolve   13u  IPv4  19378      0t0  TCP localhost:domain (LISTEN)

1. Edit /etc/systemd/resolved.conf with a text editor (as root), e.g. open it with Nano console text editor:

sudo nano /etc/systemd/resolved.conf

And uncomment (remove # from the front of the line) the DNS= line and the DNSStubListener= line. Next, change the DNS= value in this file to the DNS server you want to use (e.g. to use a local proxy, to use the Cloudflare DNS, etc.), and also change the DNSStubListener= value from yes to no.

This is how the file should look after you've made these changes (I'm using as the DNS server here, which is the Cloudflare DNS):


2. Create a symbolic link for /run/systemd/resolve/resolv.conf with /etc/resolv.conf as the destination:

sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
Here, -s is for creating a symbolic and not hard link, and -f is for removing any existing destination files (so it removes /etc/resolv.conf if it exists).

3. Reboot your system.

Port 53 should now be free on your Ubuntu system, and you shouldn't be getting errors like "listen tcp bind: address already in use" anymore.

You can check to see if port 53 is in use or not by running sudo lsof -i :53 - if port 53 is not in use, this command shouldn't show any output.


Install Docker, Docker-Compose and Portainer

Method 1: Quick Start using Docker-compose.yml

From Dockerhub :

  1. Copy docker-compose.yml.example to docker-compose.yml and update as needed. See example below: Docker-compose example:
version: "3"

# More info at and
    container_name: pihole
    image: pihole/pihole:latest
      - "53:53/tcp"
      - "53:53/udp"
      - "67:67/udp"
      - "80:80/tcp"
      TZ: 'America/Chicago'
      # WEBPASSWORD: 'set a secure password here or it will be random'
    # Volumes store your data between container upgrades
      - './etc-pihole/:/etc/pihole/'
      - './etc-dnsmasq.d/:/etc/dnsmasq.d/'
    # Recommended but not required (DHCP needs NET_ADMIN)
      - NET_ADMIN
    restart: unless-stopped
  1. Run docker-compose up --detach to build and start pi-hole
  2. Reset Password:

root@754f4e9fbbc5:/# sudo pihole -a -p

Method 2: using from Github

Github project: /docker-pi-hole

Create file based on

No need to change anything here. 



[[ -d "$PIHOLE_BASE" ]] || mkdir -p "$PIHOLE_BASE" || { echo "Couldn't create storage directory: $PIHOLE_BASE"; exit 1; }

# Note: ServerIP should be replaced with your external ip.
docker run -d \
    --name pihole \
    -p 53:53/tcp -p 53:53/udp \
    -p 80:80 \
    -e TZ="America/Chicago" \
    -v "${PIHOLE_BASE}/etc-pihole/:/etc/pihole/" \
    -v "${PIHOLE_BASE}/etc-dnsmasq.d/:/etc/dnsmasq.d/" \
    --dns= --dns= \
    --restart=unless-stopped \
    --hostname pi.hole \
    -e VIRTUAL_HOST="pi.hole" \
    -e PROXY_LOCATION="pi.hole" \
    -e ServerIP="" \

printf 'Starting up pihole container '
for i in $(seq 1 20); do
    if [ "$(docker inspect -f "{{.State.Health.Status}}" pihole)" == "healthy" ] ; then
        printf ' OK'
        echo -e "\n$(docker logs pihole 2> /dev/null | grep 'password:') for your pi-hole: https://${IP}/admin/"
        exit 0
        sleep 3
        printf '.'

    if [ $i -eq 20 ] ; then
        echo -e "\nTimed out waiting for Pi-hole start, consult your container logs for more info (\`docker logs pihole\`)"
        exit 1

Make it executable: chmod u+x

root@hpthin:~# chmod u+x
root@hpthin:~# ls
docker-compose.yml  etc-dnsmasq.d  etc-pihole  snap  var-log
root@hpthin:~# ./
WARNING: Localhost DNS setting (--dns= may fail in containers.
Starting up pihole container .......... OK
Assigning random password: uGPzx5JW for your pi-hole: https:///admin/

Upgrade Pi-hole Docker

Normal updating command "pihole -up" will not work inside of docker. We will have to update whole docker image then create a new docker based on the new download images.

Here are all commands

  • Check if a newer image is available

docker pull

  • Stop and remove the container

$ docker ps

CONTAINER ID  IMAGE                           COMMAND  CREATED       STATUS           PORTS   NAMES

73528d5ca4e8           14 hours ago  Up 14 hours ago  53/tcp  pihole

docker stop pihole

docker rm pihole

  • Create a new container

docker-compose up -d

  • Remove old image(s)

docker image prune

Log in Pi-hole Dashboard

Default URL: https://<ip>/admin

Pi-hole DNS Configuration for Family Safe

208.67. 222.123 and 208.67. 220.123 are OpenDNS's FamilyShield DNS servers. They are configured at the server level to block 4 categories (Pornography, Tasteless, Proxy/Anonymizer, and Sexuality).

1 comment: