Updated May 2026: This guide has been revised to support RouterOS 7.22.

Starting with RouterOS v7.5, MikroTik introduced the ability to run Linux containers directly on supported devices. This opens up a world of possibilities from running ad-blockers like Pi-hole to local DNS resolvers such as DNSCrypt-proxy.

In this guide, I’ll show how I migrated my Pi-hole setup from a Raspberry Pi Zero to a MikroTik router. So far it’s been running smoothly.


Prerequisites

Before we start, make sure you have:

  1. MikroTik RouterOS v7.5+
  2. ARM64-based device
  3. External storage (USB or M.2, formatted as ext3 or ext4)
  4. Physical access to the device (you’ll need to reboot it manually)

We’ll install two containers:

  • Pi-hole: ad-blocking DNS server
  • DNSCrypt-proxy: encrypted DNS resolver

Step-by-Step Instructions

Connect via SSH or WebFig terminal to your router.

Step 1. Add CA Root Certificates

Fetch and import CA root certificates:

/tool/fetch https://curl.se/ca/cacert.pem
/certificate/import file-name=cacert.pem

Step 2. Enable Container Mode

/system/device-mode/update container=yes

You’ll be prompted to reboot your router. Do it manually.


Step 3. Configure Networking

Create a veth interface for PI-hole and assign it an IP address:

/interface/veth/add name=veth1 address=172.17.0.2/24 gateway=172.17.0.1

Create a bridge for containers:

/interface/bridge/add name=containers
/ip/address/add address=172.17.0.1/24 interface=containers

Add the veth interface to the bridge:

/interface/bridge/port add bridge=containers interface=veth1

Add NAT masquerade for outbound traffic:

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

Step 4. Configure Environment Variables for Pi-hole

/container/envs/add list=ENV_PIHOLE key=TZ value="UTC"
/container/envs/add list=ENV_PIHOLE key=FTLCONF_webserver_api_password value="mysecurepassword"
/container/envs/add list=ENV_PIHOLE key=DNSMASQ_USER value="root"
/container/envs/add list=ENV_PIHOLE key=FTLCONF_dns_upstreams value="172.17.0.3#5053"
/container/envs/add list=ENV_PIHOLE key=FTLCONF_dns_listeningMode value="ALL"
/container/envs/add list=ENV_PIHOLE key=FTLCONF_dns_queryLogging value="false"
/container/envs/add list=ENV_PIHOLE key=FTLCONF_ntp_sync_active value="false"
/container/envs/add list=ENV_PIHOLE key=FTLCONF_ntp_sync_interval value=0

Step 5. Prepare Storage

Format your USB drive:

/disk format usb1 file-system=ext4 mbr-partition-table=no

Create mounted volumes for Pi-hole:

/container/mounts/add list=MOUNT_PIHOLE_PIHOLE src=usb1/volumes/pihole/pihole dst=/etc/pihole
/container/mounts/add list=MOUNT_PIHOLE_DNSMASQD src=usb1/volumes/pihole/dnsmasq.d dst=/etc/dnsmasq.d

Set the container registry and image directory:

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

Step 6. Deploy Pi-hole Container

Add and configure the Pi-hole container:

/container/add remote-image=pihole/pihole interface=veth1 root-dir=usb1/images/pihole mountlists=MOUNT_PIHOLE_PIHOLE,MOUNT_PIHOLE_DNSMASQD envlist=ENV_PIHOLE name=pihole

Forward Pi-hole’s web port:

/ip firewall nat add action=dst-nat chain=dstnat dst-address=192.168.88.1 dst-port=8080 protocol=tcp to-addresses=172.17.0.2 to-ports=80

Access Pi-hole at: http://192.168.88.1:8080/admin/

Forward DNS port:

/ip firewall nat add action=dst-nat chain=dstnat dst-address=192.168.88.1 dst-port=53 protocol=udp to-addresses=172.17.0.2 to-ports=53

Step 7. Configure DNSCrypt

Create another veth interface for DNSCrypt:

/interface/veth/add name=veth2 address=172.17.0.3/24 gateway=172.17.0.1
/interface/bridge/port add bridge=containers interface=veth2

Add a mounted volume for DNSCrypt:

/container/mounts/add list=MOUNT_DNSCRYPT src=usb1/volumes/dnscrypt/config dst=/config

Add the DNSCrypt container:

/container/add remote-image=klutchell/dnscrypt-proxy:2.1.14 interface=veth2 root-dir=usb1/images/dnscrypt mountlists=MOUNT_DNSCRYPT name=dnscrypt

Step 8. Configure and Start Containers

Start and stop containers to generate volume structure:

/container/start pihole
/container/start dnscrypt
# Wait for a few seconds
/container/stop pihole
/container/stop dnscrypt

Use SFTP to upload configuration files(if needed):

  • create on local machine file dnscrypt-proxy.toml with following content
    server_names = ['cloudflare', 'google']
    listen_addresses = ['0.0.0.0:5053']
    max_clients = 250
    ipv4_servers = true
    ipv6_servers = false
    dnscrypt_servers = true
    doh_servers = true
    require_dnssec = true
    require_nolog = true
    require_nofilter = true
    force_tcp = false
    timeout = 2500
    keepalive = 30
    use_syslog = true
    cert_refresh_delay = 240
    bootstrap_resolvers = ['9.9.9.11:53', '8.8.8.8:53']
    ignore_system_dns = true
    netprobe_timeout = 30
    netprobe_address = '9.9.9.9:53'
    log_files_max_size = 10
    log_files_max_age = 7
    log_files_max_backups = 1
    block_ipv6 = false
    cache = true
    cache_size = 4096
    cache_min_ttl = 2400
    cache_max_ttl = 86400
    cache_neg_min_ttl = 60
    cache_neg_max_ttl = 600
    [query_log]
      format = 'tsv'
    [nx_log]
      format = 'tsv'
    [blacklist]
    [ip_blacklist]
    [whitelist]
    [schedules]
    [sources]
      [sources.public-resolvers]
        urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md']
        cache_file = 'public-resolvers.md'
        minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
        refresh_delay = 72
        prefix = ''
      [sources.relays]
        urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/relays.md', 'https://download.dnscrypt.info/resolvers-list/v3/relays.md']
        cache_file = 'relays.md'
        minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
        refresh_delay = 72
        prefix = ''
    [broken_implementations]
    fragments_blocked = ['cisco', 'cisco-ipv6', 'cisco-familyshield', 'cisco-familyshield-ipv6', 'cleanbrowsing-adult', 'cleanbrowsing-adult-ipv6', 'cleanbrowsing-family', 'cleanbrowsing-family-ipv6', 'cleanbrowsing-security', 'cleanbrowsing-security-ipv6']
    [static]
    
  • create another file adlists.list with following content:
    http://sysctl.org/cameleon/hosts
    https://v.firebog.net/hosts/AdguardDNS.txt
    https://v.firebog.net/hosts/Easyprivacy.txt
    https://adaway.org/hosts.txt
    https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
    https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt
    https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt
    https://s3.amazonaws.com/lists.disconnect.me/simple_malvertising.txt
    
  • navigate into usb1/volumes/pihole/pihole and remove everything
  • copy dnscrypt-proxy.toml into usb1/volumes/dnscrypt/config
  • rnavigate into usb1/volumes/dnscrypt/config and remove everything
  • copy adlists.list into usb1/volumes/pihole/pihole

Then restart both containers:

/container/start pihole
/container/start dnscrypt

Step 9. Verify DNS Functionality

From your PC:

nslookup google.com 192.168.88.1
dig google.com @192.168.88.1

Step 10. Configure Auto-Start and Memory Limits

Make containers start automatically after reboot:

/container/set 0 start-on-boot=yes
/container/set 1 start-on-boot=yes

Limit container memory usage:

/container/config/set memory-high=200M

Troubleshooting Tips

  • Ensure your external storage is mounted and formatted as ext4.
  • Check logs with /container/logs print follow=yes.
  • If DNS doesn’t resolve, verify that your bridge and veth interfaces are configured properly and default DNS is disabled on the router.
  • Confirm that Pi-hole is using DNSCrypt.

Conclusion

Running Pi-hole and DNSCrypt on MikroTik RouterOS v7 is a powerful way to consolidate your home network setup.

No extra Raspberry Pi or external machine required just your router.

You now have:

  • Encrypted DNS (via DNSCrypt)
  • Ad-blocking and analytics (via Pi-hole)
  • Centralized configuration and startup automation

MikroTik’s container support may be limited, but with a bit of setup, it’s incredibly flexible. Give it a try!


Documentation