How do I configure DNSCrypt with a Docker Pi-hole?

Hello RonV42, I'm just following your thread here, would you be able to check my version of your docker-compose.yml please?

Version: '2'

services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    hostname: pihole
    domainname: local            # <-- Update
#    mac_address: d0:ca:ab:cd:ef:01
    cap_add:
      - NET_ADMIN
    networks:
      pihole_network:
        ipv4_address: 192.168.1.251  # <-- Update
    dns:
      - 1.1.1.1
      - 1.0.0.1
    ports:
      - 443/tcp
      - 53/tcp
      - 53/udp
      - 67/udp
      - 80/tcp
    environment:
      ServerIP: '192.168.1.251'         # <-- Update (match ipv4_address)
      VIRTUAL_HOST: 'pihole.local'  # <-- Update (match hostname + domainname)
      WEBPASSWORD: 'letmein'                 # <-- Add password (if required)
      TZ: 'Europe/London'
      IPv6: 'false'
      DHCP_ACTIVE: 'true'
      DHCP_START: '192.168.1.201'
      DHCP_END: '192.168.1.249'
      DHCP_ROUTER: '192.168.1.254'
      DHCP_LEASETIME: '2'
      DHCP_IPv6: 'false'
      DHCP_rapid_commit: 'true'
      DNS1: 192.168.1.250#53       # <-- DNS1 of pihole configured to DNSCRYPT address
      DNS2: 192.168.1.250x#53       # <-- DNS2 of pihole configured to DNSCRYPT address
    volumes:
       - '/etc/pihole/:/etc/pihole/'
       - '/etc/dnsmasq.d/:/etc/dnsmasq.d/'
    restart: unless-stopped

  dnscrypt:
    container_name: dnscrypt-proxy
    image: gists/dnscrypt-proxy:latest
    hostname: dnscrypt
    domainname: local             # <-- Update
    ports:
    - 53/udp
    - 53/tcp
    networks:
      pihole_network:
        ipv4_address: 192.168.1.250  # <--Update
    volumes:
    - '/etc/dnscrypt-proxy/dnscrypt-proxy.toml:/etc/dnscrypt-proxy/dnscrypt-proxy.toml'  # <--custom toml file for easy editing
    restart: always

networks:
  pihole_network:
    driver: macvlan
    driver_opts:
      parent: eth0     # <-- validate the native network inferface on host
    ipam:
      config:
        - subnet: 192.168.1.0/24            # <-- Update
          gateway: 192.168.1.254             # <-- Update
          ip_range: 192.168.1.0/32        # <-- Update

I think my main questions are as follows:

  1. line 7, your version had hostname: pihole2, is there a reason that you added the number 2? So that it doesn't conflict with anything? Am I ok with just pihole?

  2. line 14 (ipv4 address) and line 25 (ServerIP) ==> should these match? I'm guessing so.

  3. why do you have dns entries (lines 16 and 17) and DNS1 and DNS2 (lines 37 and 38)? Are the entries in lines 16 and 17 fallback addresses incase DNS1 and DNS2 aren't available?

  4. am I ok still to use DNS1 and DNS2 entries? Should I be using PIHOLE_DNS_ instead?

  5. does dnscrypt-proxy.toml (line 56 get created when DNScrypt is pulled/run for the first time?

  6. I am a little confused as to ip_range (line 68) with the /32 subnet mask ==> what range is this referring to? the DHCP range that I have already defined for Pi-hole in lines 31 and 32?
    Or is it a range that dnscrypt will use? Please advise?

Basically I want to design my network like this:
Network: 192.168.1.0/24
Gateway: 192.168.1.254
Docker Host Raspberry Pi 2 eth0: 192.168.1.251
Pi-hole container: 192.168.1.251
DNScrypt container: 192.68.1.251
DHCP range given out by Pi-hole: 192.168.1.1 - 192.168.1.249
(I'm presuming the Docker Host can have the same IP of the Pi-hole container...correct?)

Thank you for your time and assistance, it is sincerely appreciated.

Just a heads up this is an advanced Docker configuration and you should have a very good foundation in Docker networking, configuration, volumes, etc. There are lots of variable items when using Docker that are environment/host specific.

You can name the host anything you want. I have multiples for redundancy and testing thus the number.

Yes they need to match, the entry on line 14 is for the docker networking and the line 25 is for the configuration of pi-hole.

I don't use DNS1 or DNS2 as the lookup for the container where pi-hole is running. I want the container to always have the ability to lookup things from a known good DNS server.

Since you will only have one upstream DNS from DNScrypt you only need to define one of the two. In my environment I have two hosts running DNScrypt for redundancy.

It's been a while since I did a fresh install but it should create that file upon the container being created for the first time. If not you can go to the github page of DNScrypt and download a configuration file from there.

This is needed for macvlan networking

Since I am using static addresses for each container using this network I usually set to a unused IP address in the subnet you are creating the network.

Hi RonV42, thank you, I sincerely appreciate you taking the time to answer my previous questions. I'll try and respond.

  1. Makes sense with regards to the hostname being whatever you want it to be.

  2. Thanks for confirming ipv4 address and ServIP need to match, and explaining the need to repeat the same info for different reasons.

  3. Makes sense that the Pi-hole container's DNS entries point to the IP of the container running DNScrypt, thanks.

  4. So here my question is more about terminology. In the Pi-hole docker page (GitHub - pi-hole/docker-pi-hole: Pi-hole in a docker container) I noticed that "DNS1" and "DNS2" are potentially 'depreciated environment variables' and therefore I was wondering if I should use the newer environment variable "PIHOLE_DNS_" instead (because the old variables could break the setup in the future...?

  5. Could you please give a link for a DNScrpyt configuration file that you could recommend? With the configurations you would insert in the file?

  6. Ok sorry to have to continue asking about the ip_range of the macvlan, I just want to take the opportunity to clear this up. I definitely appreciate your first point in your reply, about this being "an advanced Docker configuration and you should have a very good foundation in Docker networking, configuration, volumes, etc". So I apologise if some of my questions come from a place of ignorance. That said, this is an excellent opportunity to put some docker learning into practice in a very useful real-world situation. I have done basic Docker tutorials, but at some point one has to take the leap into the larger world of using the tool.
    So, my question here becomes:
    if i set the ip_range to 192.168.1.0/32, this seems wrong to me, because it is specifying a single IP address, no?
    Therefore, am I to carve-out a subset IP range from my larger 192.168.1.0/24 network, especially for the macvlan, to host the Pi-hole and DNScrpyt containers? For example, 192.168.1.248/30, would give me 4 IP addresses 192.168.1.248 - .251 exclusively for my two containers (.250 for DNScrypt and .251 for P-hole). Would this work as an entry in the docker-compose.yml for ip_range?
    My question related to that is, having declared this pihole-network (macvlan) in the yml, does having the smaller subnet as a subsection of the larger subnet (192.168.1.0/24) stop the normal/real computer clients (e.g. my Mac laptop, my TV, my PS4 etc) using or reaching the Pi-hole container at 192.168.1.251?

Once again, thank you for taking the time to help!

I have updated my yaml file for the docker compose to use the newer variables be advised I don't use the pihole for DHCP I have a more advanced DHCP configuration that was easier to manage outside of pihole since I use a lot of VLANs.

 environment:
      ADMIN_EMAIL: thebigguy@something.com
      TZ: America/Chicago
      ServerIP: 192.168.x.x              # <-- Update (match ipv4_address)
      WEBPASSWORD: letmein                   # <-- Add password (if required)
      VIRTUAL_HOST: pihole3.something.com  # <-- Update (match hostname + domainname)
      PIHOLE_DNS_: 192.168.x.x#53;192.168.x.x#53
      REV_SERVER: "true"  #used for router that is providing DHCP
      REV_SERVER_DOMAIN: something.com     # domain of local network router/dhcp server
      REV_SERVER_TARGET: 192.168.x.x      # ip address of local network router
      REV_SERVER_CIDR: 192.168.0.0/16        # Reverse DNS Zone
      DNSMASQ_LISTENING: all                 # Listen on all interfaces
      TEMPERATUREUNIT: 'f'                     # temperature units
      WEBUIBOXEDLAYOUT: boxed                # for working on large screens

As for the macvlan, ipam is the management component of a docker network. In simplest terms it's DHCP server for containers that use that network. Since the containers themselves declare the IP address there is no need for ipam to hand out IP addresses and a cidr of /32 allows me just to declare a single address since an cidr is mandatory for a docker network for example 192.168.1.100/32. As stated macvlan opens the door to a lot of configuration specifics and these were the minimum to get the networking to work with fewest downsides.

As for the toml file I made just some minor tweaks to the example. You will have to change the listen address to match the address in your docker compose yaml file for DNScrypt. Also it would be best to read up on the entries in the comments in the file. This config shows two DoH providers, but there are many more if you read the documentation or you can allow the proxy to find the best. The documentation is your friend.

##################################
#         Global settings        #
##################################

## List of servers to use
##
## Servers from the "public-resolvers" source (see down below) can
## be viewed here: https://dnscrypt.info/public-servers
##
## The proxy will automatically pick working servers from this list.
## Note that the require_* filters do NOT apply when using this setting.
##
## By default, this list is empty and all registered servers matching the
## require_* filters will be used instead.
##
## Remove the leading # first to enable this; lines starting with # are ignored.

# server_names = ['scaleway-fr', 'google', 'yandex', 'cloudflare']
server_names = ['cloudflare','quad9-doh-ip4-nofilter-pri']   


## List of local addresses and ports to listen to. Can be IPv4 and/or IPv6.
## Example with both IPv4 and IPv6:
## listen_addresses = ['127.0.0.1:53', '[::1]:53']

listen_addresses = ['127.0.0.1:53','192.168.x.x:53']

Hi RonV42, thank you for your last response and the information it contained.

To be honest I'm still struggling with the networking. I understand the basic point of the macvlan, but what happens is that whenever I try to implement it, it is created but I then cannot ssh into the host RPi (my 192.168.1.251, hosting both the Pi-hole and DNScrpyt-proxy containers). I'm presuming the reason is because the macvlan is creating it's own vlan within my larger/real 192.168.1.0/24 network?
This inability to ssh into the host is really stopping any progress.
I have tried putting various lengthed /CIDR masks in the ip_range section of the ipam, in the docker-compose.yml, but this doesn't help.

I think in the yml I need to be able to publish the ip addresses of the DNScrypt-proxy container as 192.168.1.250 and of the Pi-hole container as 192.168.1.251 (same as the host) and both containers be visible on my 192.168.1.0/24 network, without them being part of their smaller vlan?


As for the dnscrypt-proxy.toml file, I've downloaded that, and got it working I think, just keeping the listen addresses as [0.0.0.0:5053], so on all interfaces. The other parameters where rather obvious in meaning. But yes, I will definitely continue to read up on their full significance in the documentation.

Yes it's becomes frustrating when you think you have it figured out and then something else is effected. Macvlan doesn't really create a traditional VLAN. Macvlan networks allow you to assign a MAC address to a container, making it appear as a physical device on your network. The Docker daemon does the rest. I needed this approach to get around other port 80 application I had and also to make sure that features like DHCP still worked.

My road to Docker has taken years and that is why I suggest folks learn in steps. In Docker it isn't easy because your have the host networking then docker networking, then how the application needs to interact with the network. These are all contributing factors if something is going to work properly. What throws the wrench into the machine is that each type of Docker network has limitations and if you don't have a good understanding how the app you want to containerize works it becomes a system of trial and error sometimes.

Since my trials with docker I have setup many "home lab" devices to try out these combinations but this is just personal education and to help out my peers with their home networks in this age of work from home, security, etc.

Best wishes in your journey into the world of Docker and containers.