Skip to content

Instantly share code, notes, and snippets.

@sanyarnd
Last active March 12, 2026 21:03
Show Gist options
  • Select an option

  • Save sanyarnd/37a9271a0eb25b47e0eeac99052f6db3 to your computer and use it in GitHub Desktop.

Select an option

Save sanyarnd/37a9271a0eb25b47e0eeac99052f6db3 to your computer and use it in GitHub Desktop.
MikroTik dynamic domain-based routing to containers (Mihomo + ByeDPI)

MikroTik dynamic domain-based routing to containers (Mihomo + ByeDPI)

Route selected domains via containers using DNS-populated address lists and policy routing. LAN keeps normal WAN; chosen domains go either to ByeDPI (DPI-bypass) or Mihomo (VPN).

Based on the article by @wiktorbgu at Habr.

How it works

  • Router DNS resolves domains and fills address-lists (to-dpi, to-mihomo).
  • Mangle marks connections by lists, then marks routing.
  • Policy routes send marked traffic to container gateways.
  • LAN DNS is intercepted, so clients use router DNS.

Topology

  • containers bridge: 172.18.0.0/24
    • veth1-byedpi: 172.18.0.2 (ByeDPI)
    • veth2-mihomo: 172.18.0.3 (Mihomo)
  • LAN uses router DNS; policy routing directs per-domain flows.

Commands

Explanation: Create interface lists.

/interface list add name=WAN
/interface list add name=LAN
/interface list add name=CONTAINERS

# TODO: this is your router-specific configuration, these settings might be already set
# /interface list member add interface="ether2" list=WAN
# /interface list member add interface=bridge list=LAN
# CONTAINERS will be added later

Explanation: RFC1918/CGNAT address-lists and bypass marking for local destinations.

/ip firewall address-list add address=10.0.0.0/8 list=rfc6890
/ip firewall address-list add address=172.16.0.0/12 list=rfc6890
/ip firewall address-list add address=192.168.0.0/16 list=rfc6890
/ip firewall address-list add address=100.64.0.0/10 list=rfc6890 comment=CGNAT

/ip firewall mangle add chain=prerouting in-interface-list=!WAN dst-address-list=rfc6890 action=accept comment="Bypass route-marking for local RFC addresses"

Explanation: Bridge for containers, veth interfaces, IPs, add to CONTAINERS list.

/interface bridge add name=containers port-cost-mode=short
/ip address add address=172.18.0.1/24 interface=containers network=172.18.0.0

/interface veth add address=172.18.0.2/24 gateway=172.18.0.1 name=veth1-byedpi
/interface veth add address=172.18.0.3/24 gateway=172.18.0.1 name=veth2-mihomo

/interface bridge port add bridge=containers interface=veth1-byedpi
/interface bridge port add bridge=containers interface=veth2-mihomo

/interface list member add interface=containers list=CONTAINERS

Explanation: Configure container runtime, mounts, envs; add ByeDPI and Mihomo.

Note: enabling containers requires a direct physical contact:

Enable Container mode and follow the instructions the command gives you (read more about Device-mode). You will need to confirm the device-mode with a press of the reset button, or a cold reboot (if using Containers on x86).

# Common setup
# Note: it uses tmpfs, you might prefer to mount a directory instead
/disk add slot=tmpfs tmpfs-max-size=200M type=tmpfs
/container config set layer-dir=tmpfs tmpdir=tmpfs memory-high=512.0MiB registry-url=https://registry-1.docker.io

# Mihomo (VLESS)
/container mounts add dst=/etc/mihomo name=mihomo-config src=/mihomo
# Follow the instructions from https://hub.docker.com/r/wiktorbgu/mihomo-mikrotik and fill SRV1 variable
/container envs add name=mihomo-envs key=SRV1 value="vless://..."
/container add check-certificate=no dns=1.1.1.1,8.8.8.8,9.9.9.9 envlists=mihomo-envs interface=veth2-mihomo mounts=mihomo-config name=mihomo remote-image=wiktorbgu/mihomo-mikrotik:1.19.14 root-dir=mihomo start-on-boot=yes workdir=/

# ByeDPI
# Note: it uses tmpfs, you might prefer to mount a directory instead
/container envs add name=byedpi-envs key=QUIC value=ACCEPT
/container add check-certificate=no cmd="-n google.com -Qr -f-204 -s1:5+sm -a1 -As -d1 -s3+s -s5+s -q7 -a1 -As -o2 -f-43 -a1 -As -r5 -Mh -s1:5+s -s3:7+sm -a1" envlists=byedpi-envs interface=veth1-byedpi name=byedpi remote-image=wiktorbgu/byedpi-hev-socks5-tunnel:redirect root-dir=/byedpi start-on-boot=yes workdir=/

Explanation: Create FIB tables; default routes via container IPs.

/routing table add fib name=to-dpi
/routing table add fib name=to-mihomo

/ip route add dst-address=0.0.0.0/0 gateway=172.18.0.2 routing-table=to-dpi
/ip route add dst-address=0.0.0.0/0 gateway=172.18.0.3 routing-table=to-mihomo

Explanation: DNS servers and domain → address-list population. Note: empty name= line will error; replace/skip when applying.

# don't forget to import certificates for DoH or use verify-doh-cert=no
# 
# /tool fetch https://cacerts.digicert.com/DigiCertAssuredIDRootG2.crt.pem
# /certificate import file-name=DigiCertAssuredIDRootG2.crt.pem passphrase=””
/ip dns set allow-remote-requests=yes cache-max-ttl=1d cache-size=10000KiB use-doh-server=https://doh.opendns.com/dns-query

# ByeDPI (to-dpi)
/ip dns static
    add address-list=to-dpi match-subdomain=yes name=youtube.com type=FWD
    add address-list=to-dpi match-subdomain=yes name=youtu.be type=FWD
# ... add more if needed

# VPN (to-mihomo)
/ip dns static
    add address-list=to-mihomo match-subdomain=yes name=rutracker.org type=FWD
    add address-list=to-mihomo match-subdomain=yes name=rutracker.cc type=FWD
# ... add more if needed

Explanation: Masquerade LAN and containers to WAN for outbound connectivity.

/ip firewall nat add chain=srcnat out-interface-list=WAN action=masquerade comment="Masquerade LAN -> WAN"
/ip firewall nat add chain=srcnat src-address=172.18.0.0/24 out-interface-list=WAN action=masquerade comment="Masquerade
containers -> WAN"

Explanation: Configure firewall rules.

# Default filters (they should be present in defconf)
# /ip firewall filter add chain=input action=accept connection-state=established,related comment="Router input: established/related"
# /ip firewall filter add chain=input action=drop connection-state=invalid comment="Router input: drop invalid"
# /ip firewall filter add chain=input action=drop in-interface-list=!LAN comment="Drop input not from LAN"
# /ip firewall filter add chain=forward action=accept connection-state=established,related comment="Forward: established/related"
# /ip firewall filter add chain=forward action=drop connection-state=invalid comment="Forward: drop invalid"
# /ip firewall filter add chain=forward action=drop connection-state=new connection-nat-state=!dstnat in-interface-list=WAN comment="Drop new from WAN not dstnated"

# Mark fasttrack connection (disable default, create new)
# or you can edit the default rule by enabling `connection-mark=no-mark` and `packet-mark=no-mark`
/ip firewall filter set [find chain=forward action=fasttrack-connection] disabled=yes
/ip firewall filter add chain=forward action=fasttrack-connection connection-state=established,related
in-interface-list=LAN out-interface-list=WAN packet-mark=no-mark connection-mark=no-mark hw-offload=yes comment="FastTrack safe (exclude marked)"

# Allow LAN access to containers
/ip firewall filter add chain=forward in-interface-list=LAN dst-address=172.18.0.0/24 action=accept comment="Clients -> containers (UI, etc)"

# Local DNS interception
/ip firewall nat add action=redirect chain=dstnat dst-address-list=!rfc6890 dst-port=53 in-interface-list=LAN protocol=udp
/ip firewall nat add action=redirect chain=dstnat dst-address-list=!rfc6890 dst-port=53 in-interface-list=LAN protocol=tcp

Explanation: Mark connections and route per-address-list; optionally mark router-originated traffic.

/ip firewall mangle add chain=prerouting in-interface-list=LAN dst-address-list=to-dpi action=mark-connection new-connection-mark=to-dpi-con passthrough=yes comment="Conn-Mark: LAN -> to-dpi"
/ip firewall mangle add chain=prerouting in-interface-list=LAN dst-address-list=to-mihomo action=mark-connection new-connection-mark=to-mihomo-con passthrough=yes comment="Conn-Mark: LAN -> to-mihomo"

/ip firewall mangle add chain=prerouting in-interface-list=LAN connection-mark=to-dpi-con action=mark-routing new-routing-mark=to-dpi passthrough=no comment="Route-Mark: to-dpi"
/ip firewall mangle add chain=prerouting in-interface-list=LAN connection-mark=to-mihomo-con action=mark-routing new-routing-mark=to-mihomo passthrough=no comment="Route-Mark: to-mihomo"

# Optional: mark router-originated traffic as well
/ip firewall mangle add chain=output dst-address-list=rfc6890 action=accept comment="Bypass local RFC from router"
/ip firewall mangle add chain=output dst-address=172.18.0.0/24 action=accept comment="Bypass router -> containers UI"
/ip firewall mangle add chain=output dst-address-list=to-dpi action=mark-routing new-routing-mark=to-dpi passthrough=no comment="Router-originated -> to-dpi"
/ip firewall mangle add chain=output dst-address-list=to-mihomo action=mark-routing new-routing-mark=to-mihomo passthrough=no comment="Router-originated -> to-mihomo"

Explanation: An optional address list for container subnet.

/ip firewall address-list add address=172.18.0.0/24 list=containers comment="Containers subnet"

Validate

  • Debug containers:
    • /container/print
    • /container/logs print
  • Try access 172.18.0.2 and 172.18.0.3 from router/LAN, e.g. http://172.18.0.3:9090/ui/
  • Address-lists should not be empty: /ip firewall address-list print where list=to-dpi or list=to-mihomo
  • Mangle counters must be non-zero: /ip firewall mangle print stats
  • Browse target domains to confirm routing

In the end it also would be the best to reboot router, devices and also flush DNS cache.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment