Docker – External IP for Your Docker App

· #docker #external-ip #linux #netplan · 3 min read

Back story

I was playing around with Pi-hole on my Ubuntu host and got stuck when I was starting up the container with 53, 67 (udp / tcp both) was getting errors (“listen udp4 0.0.0.0:53: bind: address already in use”) because the Ubuntu host system itself were using those ports.

I have previously used reverse-proxies to get around such problems but this time I wanted a simpler solution so I don’t have to manage another service in my local network to run Pi-hole.

First attempt was to try out macvlan (https://docs.docker.com/network/macvlan/) using docker networks, but that turned out not so straightforward as I wasn’t able to lease an IP using the 802.1q trunk bridge network interface in the local LAN DHCP server.

Solution

Add a secondary IP address on your host interface. Which can be achieved by finding out the physical interface which you want to use for the secondary IP address first.

ip addr

Find the host interface you want to utilise for the secondary IP, in my case it was “enp59s0”.

In order to add a virtual or secondary IP to the same interface, run the following

sudo ip address add 10.10.0.254/24 dev enp59s0

You need to be matching the subnet used in your local network or your IP won’t be reachable within the local LAN. In my case, I assigned an IP address “10.10.0.254” which I have manually reserved through my router configuration so DHCP doesn’t give away that IP to a new device that registers in the network.

After adding the secondary IP, my “ip addr” command was outputting the following segment under “enp59s0” interface, which shows our virtual IP (10.10.0.254).

2: enp59s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether xx:xx:59:8b:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 10.10.0.10/24 brd 10.10.0.255 scope global dynamic noprefixroute enp59s0
       valid_lft 86245sec preferred_lft 86245sec
    inet 10.10.0.254/24 scope global secondary enp59s0
       valid_lft forever preferred_lft forever
    inet6 efef::dada:2372:afaf:f89d/64 scope link noprefixroute
       valid_lft forever preferred_lft forever

Docker Compose Magic

Following is my Pi-hole docker-compose.yml for reference.

Pay attention to the “Ports” section, where I have specified the “virtual” or secondary IP address which we added above for the docker application to utilise when binding ports.

version: "3"

# More info at https://github.com/pi-hole/docker-pi-hole/ and https://docs.pi-hole.net/
services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      - "10.10.0.254:53:53/tcp"
      - "10.10.0.254:53:53/udp"
      - "10.10.0.254:67:67/udp"
      - "10.10.0.254:81:80/tcp"
    environment:
      TZ: 'Australia/Sydney'
    # Volumes store your data between container upgrades
    volumes:
      - './etc-pihole/:/etc/pihole/'
      - './etc-dnsmasq.d/:/etc/dnsmasq.d/'
    # Recommended but not required (DHCP needs NET_ADMIN)
    #   https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
    cap_add:
      - NET_ADMIN
    restart: unless-stopped

Once the docker-compose ports are setup to use the virtual IP, start the docker app.

I tested my Pi-hole setup by trying to resolve an hostname using the virtual IP address 10.10.0.254.

~ dig google.com @10.10.0.254

; <<>> DiG 9.16.1-Ubuntu <<>> google.com @10.10.0.254
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17903
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;google.com.			IN	A

;; ANSWER SECTION:
google.com.		217	IN	A	142.250.66.238

Making the changes permanent

Not the scope of this article, you can google “Configure Secondary IP Address on Ubuntu using Netplan” and find various articles which would explain how to add a config file to automatically enable this virtual IP when the host computer starts up.