DHCP with docker-compose and bridge networking

I had been running Pi-hole with docker-compose on a RaspberryPi 3 for quite some time and it worked flawlessly. Since I use built-in DHCP functionality the Pi-hole container had been running with network_mode: "host":

version: "3"
services:
  pihole:
    image: pihole/pihole:latest
    restart: unless-stopped
    network_mode: "host"
    cap_add:
        - NET_ADMIN
    environment:
...

Some time ago I tried to use bridge networking but could not get it to work. Have a look here.

Two weeks ago I decided to move away from my two RaspberryPi to one Intel(r) NUC as home server. Since I wanted to run multiple services that would need HTTP on port 80 (like Pi-hole) I chose to use a docker reverse proxy.

So I gave bridge networking another try. As @diginc already pointed out on GitHub you need a DHCP relay to make this work. My router does not support this so I played around with different tools and software until I settled with dhcp-helper by Simon Kelley who is also the author of dnsmasq!

I created this Dockerfile:

FROM alpine:latest
RUN apk --no-cache add dhcp-helper
EXPOSE 67 67/udp
ENTRYPOINT ["dhcp-helper", "-n"]

And copied it to the subfolder dhcp-helper.

The new docker-compose file now looks like this:

version: "3"
services:
  pihole:
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
    restart: unless-stopped
    cap_add:
        - NET_ADMIN
    dns:
      - 127.0.0.1
      - 1.1.1.1
    environment:
      ServerIP: x.x.x.x
      DNS1: 1.1.1.1
      DNS2: 1.0.0.1
      VIRTUAL_HOST: pi.hole
      DNSMASQ_LISTENING: all
    volumes:
      - '/home/xxx/docker/pihole/pihole/:/etc/pihole/'
      - '/home/xxx/docker/pihole/dnsmasq.d/:/etc/dnsmasq.d/'
    depends_on:
      - dhcphelper
    networks:
      backend:
        ipv4_address: '172.31.0.100'
      frontproxy_proxy-tier: {}

  dhcphelper:
    build: ./dhcp-helper
    restart: unless-stopped
    network_mode: "host"
    command: -s 172.31.0.100
    cap_add:
      - NET_ADMIN

networks:
  backend:
    ipam:
      config:
        - subnet: 172.31.0.0/16
  frontproxy_proxy-tier:
    external: true

As you can see I configured dhcphelper with host networking but this is no problem because it only needs port 67.
DNSMASQ_LISTENING: all is needed because Pi-hole now needs to listen to docker bridge network for forwarded DHCP requests and to the host network adapter for incoming DNS requests.

After this DHCP requests were answered but I had the problem that DNS server in DHCP responses was set to 172.31.0.100 which is on the internal docker network and is not reachable from my home network.
In order to fix this I added another configuration file 07-dhcp-options to the dnsmasq.d folder:

dhcp-option=option:dns-server,x.x.x.x

Where x.x.x.x stands for the external IP address of my NUC (see Dockerfile above).

With this configuration now everything is working as expected even without a dhcp-relay on another device!

I hope this post will help someone who has the same problem as I had.

Yours,
DerFetzer

11 Likes

Thanks, I think I'll write up some docs on DHCP detailing the many ways to handle setting it up. Do you mind if I link your post to help others set this up the same way?

Done : Docker DHCP and Network Modes - Pi-hole documentation

Of course, you can link that post anywhere you like. I hope someone finds it useful.

1 Like

A post was split to a new topic: NET_ADMIN in Docker Swarm

A post was split to a new topic: Docker DHCP not receiving requests

Heya,

Thank you very much for this! Exactly what I need!

I'm having some issues trying to get this to work - I've hopefully followed all the instructions, but I've got the following message on my Windows box "An error occurred while renewing interface Ethernet 2 : unable to contact your DHCP server. Request has timed out."

Seem I'm having issues with the DHCP part.

version: "3"
services:
pihole:
image: pihole/pihole:latest
ports:
- "53:53/tcp"
- "53:53/udp"
restart: unless-stopped
cap_add:
- NET_ADMIN
dns:
- 127.0.0.1
- 192.168.0.3
hostname: PiHole
environment:
ServerIP: 192.168.0.2
DNS1: 192.168.0.3
DNS2: 192.168.0.3
VIRTUAL_HOST: pi.hole
DNSMASQ_LISTENING: all
volumes:
- 'pihole:/etc/pihole/'
- 'dnsmasq:/etc/dnsmasq.d/'
depends_on:
- dhcprelay
networks:
pihole:
ipv4_address: '172.22.0.100'
docker-macvlan: {}

dhcprelay:
build: ./dhcprelay
restart: always
network_mode: host
command: ["-id", "eno2", "-iu", "br-23c79ae556ef", "172.22.0.100"]
cap_add:
- NET_ADMIN

networks:
pihole:
ipam:
config:
- subnet: 172.22.0.0/16
docker-macvlan:
external: true

volumes:
pihole:
dnsmasq:

[docker-macvlan] - local macvlan false false default 192.168.0.0/27 192.168.0.1
[pihole_backend] - pihole local bridge true false default 172.22.0.0/16

Host docker is running on: 192.168.0.254

I'm unsure where I'm screwing up (possibly with the backend part or different values from frontproxy_proxy-tier?)

Thanks for any help!

@modem7 Your dhcp-helper command looks a bit weird to me. What are you trying to accomplish with those arguments?

Hi, Thank you very much for this amazing post. I have a small question, I followed all the instructions, I don't see any errors per se' my setup should be working but I can't seem to access my pi-hole from the "ServerIP" which is in my case is 192.168.0.14 (where my mini-server is living). The logs from the pi-hole seems all to be fine following are last few lines from the logs:

[cont-init.d] 20-start.sh: exited 0.
[cont-init.d] done.
[services.d] starting services
[services.d] done.
Starting crond
Starting pihole-FTL (no-daemon) as root
Starting lighttpd

I really can't seem to understand what is happening. I think it is either stuck somewhere or did something wrong. The one thing I can think of is the "frontproxy_proxy-tier" network, docker complain the network did not exists, and I simply created it.

Also I ssh'd into the pi-hole container:

root@809820f86916:/# pihole status
[βœ“] DNS service is running
[βœ“] Pi-hole blocking is Enabled

Any clue to where I should even start looking?

1 Like

Hi,

Thanks a lot for this small tuto and explanation.
I'm having the network bridge problem. PiHole shows only one IP address in the Client column, which in my case is 172.18.0.1. Of course I'd love to have the real IP address of each client.

Then I followed the method to try to fix it.
I'm not getting any errors and everything seams to work but it didn't solve my problem, I still have only this IP visible.

My router doesn't support dhcp relay.
I don't need to use dhcp in pihole as I'm using dhcp from my router.

My only goal is to solve the client email showing wrong.
Can someone help me on this? did I do something wrong in following the tuto ?

here is my docker-compose file:

version: "3"
services:
  pihole:
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80"
    restart: unless-stopped
    cap_add:
        - NET_ADMIN
    dns:
      - 127.0.0.1
      - 1.1.1.1
    environment:
      ServerIP: 192.168.1.153
      DNS1: 1.1.1.1
      DNS2: 1.0.0.1
      VIRTUAL_HOST: pi.hole
      DNSMASQ_LISTENING: all
    volumes:
      - '/Users/dan/etc-pihole/:/etc/pihole/'
      - '/Users/dan/etc-dnsmasq.d/:/etc/dnsmasq.d/'
    depends_on:
      - dhcphelper
    networks:
      backend:
        ipv4_address: '172.33.0.100'
      frontproxy_proxy-tier: {}

  dhcphelper:
    build: ./dhcp-helper
    restart: unless-stopped
    network_mode: "host"
    command: -s 172.33.0.100
    cap_add:
      - NET_ADMIN

networks:
  backend:
    ipam:
      config:
        - subnet: 172.33.0.0/16
  frontproxy_proxy-tier:
    external: true

Thanks for your help.

Hello, I was wondering if someone could help me. I am using this slightly modified docker-compose file:

version: "3"
services:
  pihole:
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
    restart: unless-stopped
    cap_add:
        - NET_ADMIN
    dns:
      - 127.0.0.1
      - 172.20.0.3#53
    environment:
      ServerIP: 172.20.0.2
      DNS1: 172.20.0.3#53
      DNSMASQ_LISTENING: all
    depends_on:
      - dhcphelper
    networks:
      backend:
        ipv4_address: '172.20.0.2'
      frontproxy_proxy-tier: {}

  dhcphelper:
    build: ./dhcp-helper
    restart: unless-stopped
    network_mode: "host"
    command: -s 172.20.0.2
    cap_add:
      - NET_ADMIN

networks:
  backend:
    ipam:
      config:
        - subnet: 172.20.0.0/16
  frontproxy_proxy-tier:
    external: true

When I first ran docker-compose up -d, it prompted that the network frontproxy_proxy-tier was not present. The prompt suggested using docker network create frontproxy_proxy-tier. Is that what I'm supposed to do?

Also, what should I put in the "Ranges of IP addresses to be given out" and "Router (gateway) IP address" in the Pihole GUI? Should this be the network range 192.168.0.2-192.168.0.255 with my router being 192.168.0.1?

Additional information:
my router: 192.168.0.1
Pihole: 192.168.0.2 (docker network 172.20.0.2)

Thanks!

From my understanding (I could be completely wrong), having the dhcphelper container in host network mode and bridging it to the pihole container would be enough to make this setup work, right? (At least I've tried it like this and it works fine)

I say this because I don't understand the purpose of the frontproxy_proxy-tier network. It is external, which the docs state it means the following (that's why @ggl-wolfy receives that error):

This network has been created outside of Compose. docker-compose up does not attempt to create it, and raises an error if it doesn’t exist.

@DerFetzer: Is the frontproxy_proxy-tier network something specific to your setup but unnecessary in general?

Also, @ggl-wolfy: I think your configuration is OK, at least I've done something similar and it works.

Cheers :slight_smile:

This is the network of the reverse proxy.

@ggl-wolfy If you are not using a reverse proxy you have to forward port 80 of the pihole container to the host and can remove the external network. ServerIP has to be set to your external IP address (192.168.0.2).

Yes, the range is what you want the DHCP server to hand out IP addresses from. You could leave out some IP addresses when you want to use a range independently from Pi-hole.

1 Like

Thank you both for all your help.

@DerFetzer Sorry to bother you, but I still have some problems with the setup.

Thanks for the explanation regarding the network. I am indeed using a reverse proxy by linking Pihole from another container within the same network using http://pihole:80. So I have removed frontproxy_proxy-tier. That's ok?

I have now set Server IP to 192.168.0.2, and the network range and gateway as mentioned above.

A dev suggested (don't remember where) using 99-dhcp-debugging.conf which contained the line log-dhcp for troubleshooting, and I got the following when going through pihole.log:
no address range available for DHCP request via eth0

ifconfig on my raspberry pi:

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.2  netmask 255.255.255.0  broadcast 192.168.0.255

If you could provide me with some advice? Thank you.

Though 192.168.0.255 is a valid address, maybe dnsmasq has a problem with it. You could try 192.168.0.254 instead.
How does your 02-pihole-dhcp.conf look like?

.255 is a network's broadcast address.
Just as .0, it should not be assigned to a device.

Thanks. I made the change and I still receive the same error. Here's 02-pihole-dhcp.conf:

###############################################################################
#  DHCP SERVER CONFIG FILE AUTOMATICALLY POPULATED BY PI-HOLE WEB INTERFACE.  #
#            ANY CHANGES MADE TO THIS FILE WILL BE LOST ON CHANGE             #
###############################################################################
dhcp-authoritative
dhcp-range=192.168.0.2,192.168.0.254,24h
dhcp-option=option:router,192.168.0.1
dhcp-leasefile=/etc/pihole/dhcp.leases

I also have dhcp-option=option:dns-server,192.168.0.2 in 07-dhcp-options.conf as described in the above guide.

Oh, perfect, thanks!

Maybe it's time to dockerize my reverse proxy, I have it running in the host OS as of now :stuck_out_tongue: