← Files

mikrotik-doh-failover#

Failover DNS-over-HTTPS (DoH) configuration for MikroTik RouterOS, as diffable .rsc scripts.

Docs · Releases · Changelog · Issues · Security · For agents

<img src="https://github.com/paulmann/mikrotik-doh-failover/raw/main/assets/social-preview.png" alt="mikrotik-doh-failover: one verified DoH upstream for RouterOS, with a plain-DNS fallback that flips use-doh-server automatically" width="100%">

RouterOS routes a LAN's DNS through the router, but by default that upstream leg
is plaintext. This project configures one verified DoH upstream with a
plain-DNS fallback, forces classic LAN DNS to the router, and adds a scheduled
health-check that switches between DoH and plain DNS on failure. It is the
"how to reproduce and maintain" companion to the article
«Архитектура отказоустойчивого DNS-over-HTTPS в RouterOS»
on sysadministrator.ru.

Documentation status. Counts in this README were measured from the
published tree on 2026-10-22 (v0.1.0). Long-form design rationale is in
docs/; the scripts were verified on RouterOS 7.23.1 and 7.24.5.


Contents#


What this is and why#

A common broken approach to "DoH on MikroTik" is to add several DoH servers via
/ip dns static-server type=doh and expect failover. That syntax does not exist
and does not produce redundancy; RouterOS supports exactly one use-doh-server.
Another common failure is verify-doh-cert=yes without a trusted CA, which
silently stops all resolution.

This project avoids both:

Every claim in this README and in docs/ was verified on real devices; the
version and device-mode are stated where behaviour differs.

Capabilities#

Capability Detail
Encrypted upstream one DoH upstream with verify-doh-cert=yes
Fallback plain family DNS (1.1.1.3,1.0.0.3) when DoH is unreachable
Recovery automatic return to DoH after the health check passes
Bootstrap static A records for the DoH hostname
Force classic DNS dstnat redirect of UDP/TCP 53 from the LAN bridge
Resolver protection drop DNS from WAN, accept from LAN (UDP and TCP)
Failover policy read,write,policy,test only (no ftp/reboot/password)

By the numbers#

Measured from the published tree on 2026-10-22 (v0.1.0):

Surface Count Measured from
RouterOS scripts 7 routeros/
Example configs 1 examples/
Docs 3 docs/
Diagrams 3 diagrams/
Devices tested 5 docs/verification.md
RouterOS versions tested 2 (7.23.1, 7.24.5) docs/verification.md

How it works#

flowchart LR
    Client["LAN client"] -->|"DNS 53"| ForceDNS["force-DNS redirect"]
    ForceDNS --> Resolver["Router resolver"]
    Resolver --> Mode{"DoH healthy?"}
    Mode -->|"yes"| DoH["DoH upstream<br/>verify-doh-cert=yes"]
    Mode -->|"no"| Plain["plain family DNS"]
    Monitor["health monitor 3m"] -->|"flips use-doh-server"| Resolver
    CA["Trusted CA"] -->|"TLS validation"| DoH
    NTP["NTP"] -->|"clock"| CA
Component Responsibility
00-install-ca.rsc trust anchor for verify-doh-cert=yes
01-configure-doh.rsc one DoH upstream + bootstrap + fallback
02-security-policies.rsc force classic DNS + resolver protection
03-capsman.rsc targeted CAPsMAN/DHCP integration
04-cert-update.rsc optional CA refresh
05-dns-health-monitor.rsc health check + failover scheduler

Installation#

Requirements:

Quick start (Cloudflare Family):

# 1. Upload certs/cloudflare-doh-ca.crt to the router, then:
/import file-name=00-install-ca.rsc
/import file-name=01-configure-doh.rsc
/import file-name=02-security-policies.rsc
/import file-name=05-dns-health-monitor.rsc

For other providers (Quad9, Google, Cloudflare standard/security) edit the
values in routeros/config.example.rsc.

First steps#

/ip dns cache flush
:put [:resolve example.com]     # real IP
:put [:resolve pornhub.com]     # 0.0.0.0 with the Family filter
/log print where message~"connection error"

Reliability and limits#

Situation Behaviour
DoH unreachable failover script switches to plain family DNS (logged error)
DoH recovers script restores DoH (logged warning)
Both DoH and plain down script keeps the last config and logs critical
Client uses its own DoH/DoT not blocked (documented limit)
device-mode=home scheduler is rejected; run the monitor by hand

Scheduler rejected: not allowed by device-mode#

On devices with device-mode=home (for example the RB4011iGS+ tested here),
RouterOS rejects /system scheduler add and /tool fetch with
not allowed by device-mode. This is an expected RouterOS limit, not a script
defect: the DoH config and force-DNS still work, but the failover monitor can
only be run manually. Options:

  1. leave home and run /system script run DNS_HealthMonitor_Failover by hand;

  2. switch to advanced to enable the scheduler:

    /system/device-mode/update device-mode=advanced

    then confirm the change and reboot.

Security#

Requirements and compatibility#

Device RouterOS device-mode Wireless stack Result
Chateau PRO ax 7.24.5 advanced legacy /caps-man (controller) verified (hub)
cAP ax 7.24.5 advanced legacy /caps-man (client) verified
RB4011iGS+ 7.24.5 home — verified; scheduler blocked
hAP ax² 7.24.5 advanced — verified
RB951Ui-2HnD 7.24.5 advanced — verified
RB2011UiAS 7.23.1 advanced — verified

Documentation#

Document Contents
docs/architecture.md design rationale and threat model
docs/verification.md per-subsystem checks + failover test checklist
docs/troubleshooting.md known failures and workarounds
routeros/config.example.rsc all tunable values

Contributing#

Read CONTRIBUTING.md. Security issues: SECURITY.md,
never a public issue.

License#

MIT © 2026 Mikhail Deynekin.