Reference architecture
Most home networks trust their ISP router to do everything: routing, DNS, Wi-Fi, firewalling. This guide walks through the design of a dedicated network edge that does each of those jobs deliberately: a small firewall machine that filters DNS for every device, admits nobody but you, watches for intrusions, and keeps latency low even when the line is saturated.
This is a teaching build, not a description of any particular network. Every address, port and name below is an example value (documentation ranges from RFC 5737 and private ranges from RFC 1918). Adapt them to your own network, and treat the design decisions, not the values, as the content.
The edge is the 1 place every packet must pass, which makes it the highest-leverage spot in the whole network: a rule written there protects every device behind it, including the smart TV that will never receive another security update. Running the firewall on its own small machine, separate from whatever server you may also operate, means the security boundary keeps working when the big box is being rebuilt, upgraded, or misbehaving.
A capable setup is a low-power PC with 2 network ports running a firewall distribution (OPNsense and pfSense are the common open-source choices; everything below applies to either). It can run bare-metal or as a virtual machine on a small hypervisor; if virtualised, pass the network ports through to the VM so the host never bridges WAN traffic itself.
Inbound, the rule is simple: nothing. No port forwards, no management interfaces reachable from the internet, no exceptions for convenience. Remote access happens over a VPN (section 04), so there is nothing for an internet scanner to find.
Outbound deserves more thought than it usually gets. Most networks allow everything out, which means a compromised device can talk to whatever it likes. A stricter posture allows the protocols you actually use and logs the rest; at minimum, block outbound traffic to port 53 and 853 from everything except the firewall itself, which is what makes the next section enforceable.
Run a validating, recursive resolver (Unbound is the usual choice) on the firewall itself, resolving directly against the root servers rather than forwarding to your ISP or a public resolver. Every device on the network gets its DNS answered, filtered and cached in 1 place you control.
Filtering is 2 lists doing different jobs: a blocklist for ads, trackers and known-malicious domains (curated aggregates exist; start with a moderate one, not the most aggressive), and a threat-intelligence feed that updates faster and catches active phishing and malware infrastructure. Both are just files of domains the resolver answers with NXDOMAIN.
Filtering only works if devices cannot go around it, and modern devices try. 3 rules close the detours: redirect any outbound plain DNS (port 53) to the local resolver, so hard-coded resolvers like 8.8.8.8 silently get filtered answers; block DNS-over-TLS (port 853) outright; and put known DNS-over-HTTPS endpoints on the blocklist, since DoH hides inside normal HTTPS and cannot be blocked by port. Browsers that probe for DoH fall back cleanly to system DNS when the probe fails.
# NAT redirect: any LAN device asking any resolver on port 53
# gets answered by the firewall's own resolver instead
rdr on $lan_if proto { tcp udp } from any to !10.0.0.1 port 53 -> 10.0.0.1 port 53
# and DNS-over-TLS does not leave the network at all
block out quick proto { tcp udp } from any to any port 853
A VPN terminated on the firewall replaces every port forward. WireGuard is the simplest to reason about: a single UDP port, keys instead of passwords, and silence to anyone who probes it without a valid key, so scanners see nothing at all. Mesh overlays built on WireGuard (Tailscale and friends) trade a third-party coordination service for easier multi-device setup; running one on the firewall as a subnet router gives every enrolled device a route into the LAN without installing agents on each internal host.
1 sharp edge is worth knowing about, because it costs people days: mesh VPNs establish direct connections by UDP hole punching, which assumes your NAT keeps a stable source port. If the firewall's outbound NAT randomises source ports per destination (some do by default), hole punching fails silently and every connection falls back to the provider's relay servers: it still works, but latency jumps and throughput drops badly on a lossy line. The fix is a static-port outbound NAT rule for the VPN host's UDP traffic. The symptom to watch for: the mesh reports connections via a relay (“DERP” in Tailscale's case) instead of a direct address.
Anything that accepts authentication attempts deserves an automated response to abuse. 2 complementary tools: fail2ban reads a service's own logs and bans addresses that fail repeatedly, which is the right shape for protecting a specific daemon; CrowdSec does the same job with modern scenario detection and adds a crowd-sourced reputation feed, so addresses that attacked other people's networks are banned before they try yours. On a network with no inbound exposure these mostly protect services reachable over the VPN, which is exactly when you want the safety net: the moment something is exposed by mistake.
When an upload saturates the line, cheap router buffers fill up and every other flow queues behind it: video calls stutter and pings climb from 20ms to 500ms while a backup runs. The fix is modern queue management: enable fq_codel (or CAKE) shaping on the WAN interface, capped slightly below your measured line rate in each direction, so the queue lives in the firewall where it is managed instead of in the modem where it is not. Measure before and after with a bufferbloat test; the difference under load is usually 1 to 2 orders of magnitude.
Unpatched edge devices are how home networks actually get compromised, and manual patching stops happening the first busy month. Enable unattended security updates on every Linux host, and for the firewall itself take a configuration backup before each update; every mainstream firewall can export its full state as a single file, and restoring one turns a dead box into a 20-minute rebuild.
Designs are easy; running them teaches different things. 3 lessons that survive contact with reality:
Blocklists have false positives, and the failure mode is a family member whose shopping link silently fails. Keep an allowlist you can add to in seconds, and check the resolver's query log first whenever “the internet is broken”: it makes the diagnosis in 1 look.
Long-running daemons leak. A firewall service that holds deleted files open will slowly eat the disk while every file listing looks normal; the tell is disk usage reported by df climbing while du stays flat. When the leak is in software you do not maintain, a scheduled service restart is an honest mitigation; monitoring that would catch the disk filling is the real fix, and both together let you sleep.
Degradation hides. A VPN that falls back from direct connections to relayed ones keeps working, just badly, so nobody notices for weeks. For each path that has a fast mode and a fallback mode, learn the 1 command that tells you which mode you are in, and check it when things feel slow rather than assuming the problem is elsewhere.
If you build this, sequence it so the network keeps working at every step: firewall with default-deny inbound first, then the resolver with no filtering, then filtering once resolution is proven, then the DNS redirect rules, then the VPN (tested from outside before you rely on it), then queue management, then intrusion detection. Each layer is independently useful, and nothing later requires redoing anything earlier.