Note: This OpenWrt policy routing article describes an anonymised reference architecture for learning network design, troubleshooting and safe change management. It contains no names of real online services, domains, endpoints, credentials or provider-specific settings. It is not a guide for bypassing regional availability, licensing, payment rules or security controls of online services.
A small home network can become surprisingly complex. Add local DNS, several possible egress paths, WireGuard, policy routing, monitoring and backups, and a “small router” quickly becomes a system where one poorly scoped exception can affect far more traffic than intended.
The most important lesson from this kind of lab is therefore not how to send packets through a different path. It is how to keep every exception narrow, observable, predictable and reversible.

What are we trying to achieve?
The OpenWrt policy routing design goal is simple: the vast majority of traffic should continue to use the ordinary Internet uplink. Only a deliberately defined group of connections may be classified through DNS and handled by a separate routing policy.
Ordinary traffic
→ ordinary upstream
DNS query matches an approved domain group
→ dedicated resolver path
Address returned by that query
→ dynamic destination set
→ policy routing
→ Primary
→ Secondary
→ ordinary upstream
This is very different from a blanket rule such as “send this entire device through a VPN”. The exception belongs to a classification decision, not to a phone, television or laptop as a whole.
For related edge-network topics, see more networking projects.

Hardware: a small lab does not need a large server
This architecture does not require a powerful x86 machine. A small single-board computer is enough if it has reliable operating-system support, sufficient memory and networking that fits the topology.
A practical baseline can include:
- a Raspberry Pi, Orange Pi or comparable SBC supported by the chosen operating system;
- a reliable power supply and adequate cooling;
- a high-endurance microSD card or more durable storage when supported;
- at least one Ethernet interface;
- an optional USB-to-Ethernet adapter if the topology needs physically separate interfaces;
- an existing router or gateway for the ordinary Internet path;
- an optional managed switch when VLANs are part of the lab;
- secure off-device backup storage;
- optional monitoring infrastructure.
Before buying hardware, check current platform support, available system images, interface requirements, storage reliability and power quality. The price of the board matters less than whether the device can be restored predictably after a failure.
Example lab shopping list
Affiliate disclosure: the Amazon links below are affiliate links. If you buy through one of them, I may receive a commission at no additional cost to you. The products are examples that fit the lab described here; this does not mean that every item has been personally tested by me.
- Orange Pi Zero3 1 GB — example SBC used as the edge node;
- passive aluminium case for Orange Pi Zero3 — passive cooling and mechanical protection;
- 5 V / 3 A (15 W) USB-C power supply — 5 V / 3 A USB-C power supply;
- SanDisk High Endurance 64 GB microSD — endurance-oriented storage suitable for a continuously running edge device;
- TP-Link TL-SG105E — optional managed switch for a VLAN-based lab;
- UGREEN SD/microSD card reader — useful for system-image installation and microSD recovery.
Not every item is mandatory. The simplest build needs only an SBC, compatible case, correct power supply, microSD card and an existing Ethernet network. The switch and card reader are optional additions.
OpenWrt policy routing: clear software roles
One practical implementation can use OpenWrt as the routing and firewall platform, but the important part is not the product names. It is the separation of responsibilities:
- OpenWrt — interfaces, VLANs, routing, firewall and service management;
- dnsdist — one controlled DNS entry point and rule dispatcher;
- SmartDNS — helper resolution for selected domain groups and updates to dynamic address sets;
- mwan3 — policy routing and ordered failover;
- WireGuard — optional tunnels used as Primary and Secondary paths;
- Zabbix or a similar system — monitoring of system state, services and backups.
OpenWrt 25.12 and newer use apk as the package manager instead of the older opkg. That detail matters when recovery procedures or package-install automation are written for current releases.
One function, one owner
On a compact router it is easy to run several components with overlapping responsibilities. dnsmasq, dnsdist and SmartDNS can all provide DNS-related functions. If two processes both try to become “the main DNS server”, troubleshooting quickly becomes ambiguous.
A cleaner model is:
- one process owns the DNS port exposed to clients;
- helper resolvers listen only on loopback or deliberately restricted interfaces;
- one system owns active policy-routing decisions;
- other tools do not modify the same routing decisions in parallel.
This separation can be more important than the exact software choice. Once the owner of each decision is clear, failures can be isolated to DNS, health checking, the dynamic set, the firewall or routing.
Why not route an entire device?
The easiest policy-routing rule is often something like: “if the source is this phone, send everything through the alternate path”. It works, but it has a large blast radius.
The browser, operating-system updates, messaging apps, cloud synchronisation and unrelated software all inherit the same latency and failure behaviour. The exception is no longer narrow.
A more selective design leaves ordinary traffic untouched and applies the alternate policy only to destinations derived from controlled DNS classification.
Stage 1: one controlled DNS entry point
If DNS is part of the classification mechanism, clients must actually use that DNS path. The edge node should therefore be the visible DNS entry point, and rules for the selected domain group should be evaluated before more general resolver policies.
- Does the name match a small, approved suffix list?
- If yes, send the query to a dedicated resolver pool.
- If no, use the normal resolver or ordinary DNS policy.
- Return the answer through the same controlled entry point.
dnsdist is well suited to this role because it can match DNS names by suffix and direct matching queries into a specific pool. The pool can then use its own backend-selection policy, such as firstAvailable.

Stage 2: classify domains narrowly
Large applications frequently rely on shared CDN and cloud infrastructure. That creates a tempting shortcut: if one answer points into a large CDN, include the entire CDN in the special routing rule.
That is usually a bad idea.
The same infrastructure may deliver operating-system updates, websites, video services and unrelated applications. A provider-wide match can therefore expand the scope of policy routing far beyond the original intent.
A resilient domain list should be:
- small;
- evidence-based;
- attributable to the intended application family;
- periodically reviewed;
- free of broad generic infrastructure domains unless there is a strong reason to include them.
Stage 3: let DNS answers create temporary routing state
Routers do not make forwarding decisions from DNS names. At forwarding time they mainly see destination addresses. A bridge is therefore needed between name classification and routing.
In OpenWrt policy routing, a dynamic destination set can provide that bridge — an ipset or nftset, depending on the stack.
Approved DNS answer
→ extract the returned address
→ add it temporarily to the destination set
→ policy routing matches the destination against that set
SmartDNS supports domain rules whose results can be placed into ipset or nftset. This avoids maintaining a static list of destination addresses and lets the routing policy follow current DNS answers.
That set should not be treated as disposable cache data, however. It is live classification state. Flushing it without a reason removes the router’s current mapping between observed DNS answers and the routing policy.

OpenWrt policy routing with ordered failover
Redundancy naturally suggests multiple independent paths, but that does not mean the traffic must be load-balanced.
A more predictable model is:
Primary
→ Secondary
→ ordinary upstream
mwan3 policies can contain multiple members. Members with lower metrics take precedence, while members with the same metric can be load-balanced. Distinct metrics therefore fit a deterministic failover design well.
DNS should reflect the same state. While Primary is healthy, dedicated queries use Primary. If it fails, Secondary takes over. If both protected paths are unavailable, the design must explicitly choose between the normal Internet uplink as a final fallback and an intentional visible failure.
A hidden fallback that keeps querying a public resolver in parallel while Primary is healthy makes testing ambiguous and weakens observability.

Failure modes that only become obvious in real testing
1. A client can bypass the whole classifier by using another DNS resolver
If a client receives a different resolver through DHCP, the edge node never sees its DNS queries. The application may still obtain a valid IP address, but the dynamic destination set is not updated. The symptom looks like a policy-routing failure even though the actual problem is DNS distribution to the client.
Whenever the client and router resolve the same hostname differently, verify the resolver used by the client before widening any routing rule.
2. Do not fix one missing domain by classifying an entire CDN
A provider-wide match might fix one test while silently pulling a large amount of unrelated traffic into the special policy.
3. Primary and Secondary should be genuinely active/passive
A backup resolver should remain quiet in the healthy Primary state. A negative health result should be what triggers the transition.
4. Do not run two active policy-routing systems at once
Two independent components marking packets and maintaining their own routing tables turn troubleshooting into guesswork. One component should own PBR decisions.
5. Do not restart everything after every small change
A domain-list change should not automatically require a firewall restart or full device reboot. Restart only the service whose configuration changed so that cause and effect remain visible.
Monitoring: verify state, not just logs
Logs are useful, but on embedded systems they may be short-lived. Monitoring should therefore verify the state that the design actually depends on:
- load, memory, free storage and uptime;
- DNS, routing and firewall process state;
- reachability of the intended DNS listeners;
- health of Primary, Secondary and the ordinary uplink;
- existence and entry count of the dynamic destination set;
- presence of the expected policy-routing rule;
- age of the newest local backup;
- age of a marker updated only after a successful off-device backup.
With Zabbix, active checks are a practical model for an edge node: the agent initiates the connection to the server or proxy, so a passive listener does not have to be exposed solely for routine metrics.

Backup and change discipline
Backup is part of the architecture, not an optional extra. A useful backup should support both partial recovery of individual components and a complete rebuild after storage failure.
Inspect
→ timestamped backup
→ one change
→ validate syntax
→ restart only the affected service
→ verify
→ keep a rollback path ready
At least one backup copy belongs outside the edge node. If the archive contains VPN keys, SSH keys or other secrets, it should be stored only in trusted, appropriately protected storage.
How do you verify that the design actually works?
An interface showing “UP” is not enough. Validation should cover the complete dependency chain:
- the client really uses the intended DNS;
- the query is visible at the controlled DNS entry point;
- a matching domain reaches the intended resolver pool;
- the answer updates the dynamic destination set;
- traffic to the returned address matches the expected PBR rule;
- Primary is used in the healthy state;
- a controlled Primary failure moves the traffic to Secondary;
- failure of both protected paths produces the explicitly defined fallback behaviour;
- ordinary unclassified traffic remains on the normal uplink throughout the tests.
Only an end-to-end test like this proves that the architecture works as a system rather than as a collection of individually healthy components.
Final thoughts
The most valuable part of this Home-Edge Lab is not a particular tunnel, DNS resolver or SBC model. It is the discipline of the boundaries:
- ordinary traffic keeps its ordinary path;
- exceptions remain narrow and explainable;
- DNS and routing represent the same state;
- the dynamic destination set is treated as live system state;
- failover has one unambiguous order;
- each function has a clear owner;
- monitoring verifies state rather than merely collecting logs;
- backup and rollback are part of the normal workflow.
That is what keeps a relatively complex home lab understandable months later, safe to modify and just as easy to remove when it is no longer needed.

