Adventures in Networking

Main menu:

Run BIND9 DNS in a Container on MikroTik RouterOS

This guide shows how to run a BIND9 DNS server in a container on MikroTik RouterOS, using external storage and RouterOS container support. The setup was tested on the MikroTik RB5009UG+S+IN, but should work on most MikroTik routers that support containers.

The container runs BIND independently from RouterOS DNS and uses mounted storage for configuration, zones, and cache data.

Why Run BIND Instead of RouterOS DNS?

For many years I ran BIND on dedicated hardware—first repurposed thin clients and later Raspberry Pis.

A couple of years ago, I simplified my home network by removing those servers and relying on RouterOS DNS forwarding with static host entries. That worked well, but eventually I began expanding my home lab again and wanted the full flexibility of a real DNS server:

  • authoritative zones
  • advanced caching configuration
  • DNSSEC control
  • full configuration flexibility

Running BIND in a container on the router turned out to be a simple and efficient solution.

Requirements

To run a BIND container on RouterOS you need:

  • A MikroTik device that supports RouterOS containers
  • External USB storage
  • A compatible container image
  • A dedicated container subnet

The RB5009 hardware is more than capable of handling a small DNS container.

Storage for RouterOS Containers

RouterOS containers require external storage.

I initially tested using a USB thumb drive, but those are not designed for continuous disk activity. Instead I connected an NVMe SSD through a USB enclosure and formatted it on the router.

After connecting the disk:

  1. Navigate to System → Disks
  2. Select the device (for example usb1)
  3. Format it using ext4

RouterOS will then expose the disk through the Files interface.

Installing Container Support on RouterOS

Download the container package from:

https://mikrotik.com/download

Make sure the package matches:

  • RouterOS version
  • CPU architecture

Upload the package through WinBox → Files, then reboot the router.

Next enable container support:

/system/device-mode/update container=yes

Perform a cold reboot (power cycle) after enabling containers.

Preparing the BIND Directory Structure

Create the following directories on the USB disk:

usb1/bind9
usb1/bind9/cache
usb1/bind9/etc
usb1/bind9/hints
usb1/bind9/lib
usb1/bind9/log

These correspond to the directories used inside the container:

Container DirectoryPurpose
/etc/bindBIND configuration
/var/cache/bindDNS cache
/var/lib/bindzone storage
/var/loglogging
/usr/share/dnsDNS hints

If you already run BIND elsewhere, you can copy your existing configuration into the /etc/bind directory.

Selecting a BIND Container Image

RouterOS can download container images directly from Docker Hub.

After some experimentation I found a container built specifically for MikroTik:

https://hub.docker.com/r/tecspace/bind9-mikrotik

This avoids architecture compatibility issues that sometimes occur with official images.

Creating Container Mount Points

Container mounts connect RouterOS directories to the container filesystem.

Create mounts mapping the RouterOS directories to the container paths:

ListSrcDst
MOUNT_BIND9/usb1/bind9/cache/var/cache/bind
MOUNT_BIND9/usb1/bind9/etc/etc/bind
MOUNT_BIND9/usb1/bind9/hints/usr/share/dns
MOUNT_BIND9/usb1/bind9/lib/var/lib/bind
MOUNT_BIND9/usb1/bind9/log/var/log

Environment Variables

Environment variables can override container defaults.

Example configuration:

ListKeyValue
ENV_BIND9BIND9_STAY_ON1
ENV_BIND9BIND9_CMD_OPTS-g -4 -u named
ENV_BIND9BIND9_CFG_OPTSversion “hidden”;directory “/var/cache/bind”;listen-on {any;};listen-on-v6 {none;};recursion yes;allow-query {0.0.0.0/0;};d/system/device-mode/update container=yesnssec-validation yes;max-cache-size 64M;

Most configuration should still live in standard BIND configuration files.

Container Networking Configuration

Create a dedicated bridge for containers:

/interface/bridge/add name=bridge_containers
/ip/address/add address=172.16.0.1/24 interface=bridge_containers

Create a virtual Ethernet interface:

/interface/veth/add name=veth1 address=172.16.0.2/24 gateway=172.16.0.1
/interface/bridge/port/add bridge=bridge_containers interface=veth1

Enable NAT so the container can reach the internet:

/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.16.0.0/24

Creating the BIND Container

Configure the container registry:

/container/config/set registry-url=https://registry-1.docker.io tmpdir=usb1/tmp

Create the container:

/container/add \
remote-image=tecspace/bind9-mikrotik:1.0 \
interface=veth1 \
root-dir=usb1/images/bind9 \
mountlists=MOUNT_BIND9 \
envlist=ENV_BIND9 \
start-on-boot=yes \
name=bind9

Monitor the container download:

/container/print

Once the status shows stopped, start the container:

/container/start bind9

Using RouterOS to Forward DNS to the BIND Container

Once the server is operational, you may want the router itself to use the container.

Example RouterOS configuration:

/ip dns
set servers=172.16.0.2 allow-remote-requests=yes

Performance Observations

The BIND container uses minimal resources and has no noticeable impact on router performance.

In testing, DNS responses from BIND were actually slightly faster than RouterOS DNS forwarding—only by a few milliseconds, but consistently measurable.

Running BIND directly on the router turned out to be an elegant solution.

Testing DNS Performance

For DNS benchmarking I recommend:

https://www.grc.com/dns/benchmark.htm

GRC DNS Benchmark remains one of the best tools for comparing resolver performance.