Safeguard
Supply Chain Security•

Build a Home Firewall on a Mini PC: VLANs, IDS, DNS Filtering, Updates

Turn a two or four port mini PC into a default deny home firewall with VLANs, IPv6, an open source IDS, filtered encrypted DNS, updates and backups.

Safeguard Security Team
49 min read

A home firewall on a mini PC is a small computer with at least two network ports that runs an open source firewall operating system, sits between your internet connection and your devices, and enforces a default deny policy you wrote. Put your ISP router in bridge mode (or behind the mini PC), split your network into VLANs for trusted devices, IoT, guests and management, then add filtered DNS, an optional IDS, automatic updates and off box configuration backups.

This guide goes one level deeper than our tiny data center at home build and our look at whether open source router firmware is more secure than stock. It covers why a dedicated firewall helps, what hardware to look for, the platform families, VLAN design, a default deny rule set for IPv4 and IPv6, intrusion detection, DNS filtering with encrypted upstream DNS, a full step by step build on Linux with nftables (with the equivalent BSD pf rules alongside), testing, and an update and backup routine. Hardware and software are described by category, never ranked, and every configuration option comes from the project's own documentation.

Key takeaways

  • A firewall is a policy enforcement point between networks with different security postures. NIST SP 800-41 Rev. 1, "Guidelines on Firewalls and Firewall Policy" (September 2009, Karen Scarfone and Paul Hoffman), is still the clearest primary reference on policy design, and its core advice is a written policy that blocks by default.
  • The OpenBSD PF guide states the recommended practice plainly: take a "default deny" approach, so the first filter rule is block all and you then pass only what you need. nftables reaches the same place with policy drop on the input and forward chains.
  • A mini PC firewall gives you things most consumer routers do not: VLAN aware zones you define, per zone rules, readable logs, an IDS, a local validating resolver with filtering, a patch cadence you control, and a configuration you can back up as text.
  • For IPv6, your firewall acts as a customer edge router. RFC 7084 requires it to request a delegated prefix with DHCPv6 (RFC 8415), assign a separate /64 to each LAN interface, and advertise itself with Router Advertisements. RFC 4890 lists the ICMPv6 messages a firewall must not drop, including Packet Too Big, Router Advertisement and Neighbor Discovery.
  • RFC 6092 (January 2011) describes "simple security" for residential IPv6 gateways: inbound traffic is filtered unless it belongs to a flow started from inside, and the gateway must not answer DNS queries or DHCPv6 discovery arriving on the outside interface.
  • Suricata, the Open Information Security Foundation's open source IDS and IPS engine, allows all traffic by default in IPS mode and drops only what its rules match; it can sit in the forwarding path through an nftables queue rule. Start in detect only mode, because a busy rule set on a small CPU caps throughput and an inline engine that stops can stop your internet.
  • DNS filtering works best as a local resolver that forwards over DNS over TLS (RFC 7858, port 853), applies a Response Policy Zone blocklist, and refuses to return private addresses for public names. DNS over HTTPS (RFC 8484) shares port 443 with ordinary web traffic, so a firewall cannot reliably block it by port alone.
  • Updates and backups are the parts people skip. Use the platform's own update tool (syspatch on OpenBSD, freebsd-update on FreeBSD, unattended security updates on Debian) and keep a dated copy of every configuration file off the box.

Why replace or sit behind an ISP router?

Most homes have a single box from the ISP that is modem, router, Wi-Fi access point, DHCP server, DNS forwarder and firewall at once. It is designed to be cheap, remotely managed by the ISP, and easy for anyone to set up. That is a reasonable design goal, but it leaves you with very little control. You usually cannot create more than a guest network, cannot write rules between your own segments, cannot see what was blocked and why, and cannot decide when firmware updates arrive. Our post on how a home router is made walks through why: a vendor SDK, a pinned kernel and a release schedule set by the maker.

A mini PC firewall changes four things.

  1. Policy you can read. The whole rule set is a text file. You can diff it, review it, comment it and restore it.
  2. Segmentation. With 802.1Q VLANs you can run five or ten separate networks over one cable to a managed switch, and write rules between them. Your smart plugs and TV no longer share a broadcast domain with your laptop and NAS.
  3. Visibility. Packet filter logs, flow counters, DNS query logs and IDS alerts tell you what each device is doing.
  4. Patch control. General purpose operating systems publish security advisories and binary updates on a regular cadence, and you decide when to apply them.

There are honest costs. You now own an extra device that can fail, a configuration that can be wrong, and an update routine you must follow. If your household depends on the connection for work, keep the ISP router's original configuration written down so you can fall back in minutes. And a home firewall does not protect a device from what it installs; it limits what a compromised device can reach and lets you see it.

Bridge mode, double NAT, or replace the ISP router?

There are three common placements. Pick the first one your ISP supports.

PlacementHow it worksProsCons
ISP device in bridge or modem only modeThe ISP box passes the public IPv4 address and the IPv6 delegation straight to the mini PC's WAN portSingle NAT, full IPv6 prefix delegation, inbound filtering in one placeNot every ISP device offers it; some need a support call
ISP router in front, mini PC behind it (double NAT)Mini PC's WAN gets a private address from the ISP routerWorks with any ISP; ISP features such as voice ports keep workingTwo layers of NAT, harder port forwarding, IPv6 delegation may be missing or a smaller prefix
Replace the ISP router entirelyMini PC connects to an ONT, cable modem or DSL modem directly, sometimes with a VLAN tag or PPPoE on the WANCleanest designSome ISPs require their own device or specific credentials; TV and voice services may break

If you must double NAT, ask the ISP router to place the mini PC in its "DMZ host" or exposed host setting so unsolicited inbound traffic reaches your firewall's rules instead of being half handled by two devices.

What hardware do you need?

A home firewall is not demanding by server standards, but a few properties matter more than raw CPU speed.

Network ports and controller chipsets

You need at least two ports: one WAN and one LAN. With VLANs on a managed switch, two ports are enough for any number of internal networks, because every internal VLAN rides one tagged "trunk" link. A third or fourth port is still useful for a dedicated management port, a separate link for a server, or a spare WAN for a backup connection.

The network controller chipset matters more than the port count. Check that your chosen operating system has a mature driver for it before you buy. For example, the FreeBSD igc(4) manual page lists the Intel I225 and I226 2.5 Gb Ethernet controllers it supports (including I225-V, I225-LM, I226-V and I226-LM) and notes hardware VLAN tag insertion and extraction and VLAN checksum offload. Linux and OpenBSD publish equivalent driver lists. Many small boxes now ship with 2.5 GbE ports; the speed matters less than whether the driver is stable under sustained load on your OS. Avoid USB network adapters for the WAN or trunk port where you can; a PCI Express or on board controller is easier to keep stable.

CPU, RAM and storage

ComponentWhat mattersPractical guidance
CPUSingle core speed for packet filtering; more cores for an IDS; hardware AES instructions for VPN throughputA recent low power x86 or ARM chip handles routing and filtering at typical home broadband speeds. An inline IDS is where CPU becomes the limit.
RAMState table size, DNS cache, IDS rule memoryFiltering alone needs little. Budget several gigabytes if you plan to run an IDS with a large rule set.
StorageReliability and endurance, not speedA small SSD is fine. Avoid cheap memory cards. Logs and IDS output are the main writes, so rotate them.
PowerThe box runs every hour of the yearMeasure at the wall with a plug in meter under your real load, and put it on a UPS with your modem and switch. Our homelab UPS guide covers sizing and graceful shutdown.
CoolingFanless boxes run warm in closetsGive it airflow; heat shortens component life and can throttle the CPU.

Switch and access point

VLANs only help if the rest of the network understands them. You need:

  • A managed switch that supports IEEE 802.1Q VLANs, lets you mark one port as a tagged trunk to the firewall, and lets you assign other ports as untagged access ports in a single VLAN. 802.1Q is the IEEE standard for bridges and bridged networks; the current edition, IEEE 802.1Q-2022, was approved on 21 September 2022 and published on 22 December 2022. Our explainer on how Ethernet switching works covers MAC learning and tagging in detail.
  • A Wi-Fi access point that can map each SSID to a VLAN. Your trusted SSID goes to the LAN VLAN, the IoT SSID to the IoT VLAN, the guest SSID to the guest VLAN. The access point's own management address should live in the management VLAN.

What are the open source firewall platform options?

There are three broad families. All of them are capable; the choice is about which documentation, tooling and update model you want to live with. No ranking is implied.

FamilyPacket filterConfiguration styleUpdate modelFits people who
BSD pf based firewall distributionspf (from OpenBSD, also in FreeBSD)Web interface that writes pf rules, plus add on packagesDistribution's own release and patch cadenceWant a web UI, packages for IDS and DNS, and minimal shell work
Linux based firewall distributionsnftables or the iptables compatibility layerWeb interface or command line with the distribution's own toolsDistribution's own cadencePrefer Linux drivers and tooling but still want a firewall focused build
A plain general purpose OS (OpenBSD, FreeBSD or a Linux distribution such as Debian)pf on the BSDs, nftables on LinuxText files you write and version yourselfThe OS project's security updates (syspatch, freebsd-update, Debian security updates)Want to understand every line, keep config in git, and are comfortable at a shell

A few facts about the underlying filters help you choose.

  • pf is OpenBSD's packet filter. The OpenBSD PF User's Guide describes it as "OpenBSD's system for filtering TCP/IP traffic and doing Network Address Translation". Rules are evaluated first to last, and unless a rule carries the quick keyword, the last matching rule wins. The guide's "Building a Router" example builds a complete home gateway with NAT, DHCP, a caching resolver and Wi-Fi.
  • FreeBSD ships three firewalls in the base system, according to its Handbook: PF, IPFW and IPFILTER. The Handbook's PF section starts from a minimal two rule policy, block in all and pass out all keep state, and then grows it.
  • nftables is the Linux packet classification framework that replaces iptables. The nftables wiki publishes a "Simple ruleset for a home router" that uses policy drop on the input and forward chains, a connection tracking verdict map that accepts established and related traffic and drops invalid packets, and per interface jump chains.

A firewall distribution saves time and gives you a GUI; a plain OS gives you a smaller, fully understood system. The walkthrough below uses plain Debian with systemd-networkd and nftables because every piece is documented in a man page, and shows the pf equivalent so BSD users can follow along.

How should you design the network?

Start from the question NIST SP 800-41 asks: which networks have different security postures, and what should be allowed between them? For most homes that gives five zones.

ZoneVLAN IDIPv4 subnet (example)IPv6 /64 from delegationWho lives hereMay reach
WANuntagged on port 1from ISPfrom ISPthe internetnothing inside, except replies
LAN (trusted)10192.168.10.0/24subnet ID 0x10laptops, phones, desktops, NASinternet, IoT (to manage it), firewall DNS and DHCP
IoT20192.168.20.0/24subnet ID 0x20TVs, smart plugs, speakers, camerasinternet (optionally only named services), firewall DNS and DHCP
Guest30192.168.30.0/24subnet ID 0x30visitorsinternet, firewall DNS and DHCP
Management99192.168.99.0/24none neededfirewall admin, switch and access point managementnothing; only the LAN admin host may reach it

All IPv4 subnets come from the private ranges in RFC 1918 (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16). Pick ranges that will not collide with networks you join over VPN; 192.168.0.0/24 and 192.168.1.0/24 are the ones most likely to clash.

Worked example: how big an IPv6 prefix should you ask for?

RFC 7084 requirement WPD-2 says a customer edge router that sends a size hint "MUST ask for a prefix large enough to assign one /64 for each of its interfaces, rounded up to the nearest nibble". A nibble is four bits.

  • Five internal interfaces (LAN, IoT, guest, management, a lab VLAN) need five /64s. Five subnets need 3 bits (2 to the 3rd power is 8). Rounded up to the nearest nibble, that is 4 bits, so you hint for a /60 (64 minus 4), which holds 16 /64 networks.
  • Twenty internal interfaces need 5 bits (32 subnets). Rounded up to a nibble that is 8 bits, so you hint for a /56 (64 minus 8), which holds 256 /64 networks.

Your ISP decides what it actually delegates. Many delegate a /56 or a /60 to homes; some delegate a /64, which leaves you only one IPv6 network. If you get a /64, put IPv6 on the trusted LAN only and leave IoT and guest on IPv4 until you can get more.

Where the switch and access point fit

The cabling is simple. Firewall port 1 goes to the modem. Firewall port 2 goes to switch port 1, configured as a trunk carrying VLANs 10, 20, 30 and 99 tagged. Switch ports for wired devices are untagged access ports in one VLAN each. The access point uplink is a trunk carrying the VLANs its SSIDs use plus VLAN 99 for its own management. Change the switch and access point management addresses into VLAN 99 last, after you have confirmed you can still reach them from the LAN admin host.

How do you write a default deny rule set?

NIST SP 800-41 Rev. 1 says a firewall policy defines how firewalls handle both inbound and outbound traffic, and that generally all inbound and outbound traffic not expressly permitted by the policy should be blocked. It also recommends permitting outbound traffic only from the source addresses actually in use inside the network, which is the anti spoofing rule you will see again under IPv6. In practice that means:

  1. Drop everything on the input chain (traffic to the firewall itself) and on the forward chain (traffic through it) unless a rule allows it.
  2. Accept packets that belong to connections you already allowed (connection tracking state established and related), and drop packets the tracker marks invalid.
  3. Allow each zone to reach only the firewall services it needs (DHCP, DNS, and from the admin host, SSH).
  4. Allow each zone the forwarding it needs (LAN to WAN, LAN to IoT, IoT to WAN, guest to WAN).
  5. NAT IPv4 from the inside to the WAN address. Do not NAT IPv6.
  6. Log what you drop at a rate limit, so the logs are useful without filling the disk.

The decision table

From \ ToFirewallWANLANIoTGuestMgmt
WANdrop (ICMPv6 essentials and DHCPv6 replies allowed)n/adropdropdropdrop
LANDNS, DHCP, SSH from admin host onlyallown/aallowdropadmin host only
IoTDNS, DHCPallow (or allowlisted)dropn/a (consider client isolation)dropdrop
GuestDNS, DHCPallowdropdropn/adrop
MgmtNTP, DNSupdates onlydropdropdropn/a

Replies to allowed connections flow back automatically because of connection tracking, so "LAN to IoT allow" means your phone can open the TV's control port while the TV still cannot open a connection to your laptop.

How do you handle IPv6?

IPv6 changes three things.

There is no NAT, so filtering does the work. Every device gets a globally routable address. RFC 6092, "Recommended Simple Security Capabilities in Customer Premises Equipment (CPE) for Providing Residential IPv6 Internet Service", describes the expected default: allow traffic that belongs to flows started from inside, filter the rest. It also sets specific rules, such as REC-8 (an integrated DNS resolver must not process queries arriving on the exterior interface) and REC-9 (inbound DHCPv6 discovery packets on the exterior interface must not be processed). REC-5 and REC-6 are anti spoofing rules: do not forward outbound packets whose source is not one of your inside prefixes, and do not accept inbound packets claiming to come from one.

ICMPv6 is not optional. RFC 4890 lists traffic a firewall must not drop. For transit traffic that includes Destination Unreachable (type 1, all codes), Packet Too Big (type 2), Time Exceeded (type 3, code 0) and Parameter Problem (type 4, codes 1 and 2). It also says Echo Request (128) and Echo Response (129) need not be filtered in IPv6. For traffic to the firewall itself, it adds the Neighbor Discovery and router messages: Router Solicitation (133), Router Advertisement (134), Neighbor Solicitation (135) and Neighbor Advertisement (136). Drop Packet Too Big and path MTU discovery breaks; drop Neighbor Discovery and IPv6 stops working on the link.

Prefix delegation and Router Advertisements. RFC 8415 defines DHCPv6, including the Identity Association for Prefix Delegation (IA_PD) a router uses to request a prefix. RFC 7084 requires the CE router to support DHCPv6 prefix delegation (WPD-1), to assign a separate /64 from the delegated prefix to each LAN interface (L-2), to advertise itself as a router for those prefixes (L-3), and, if the delegated prefix changes, to immediately advertise the old prefix with a preferred lifetime of zero (L-13). WPD-5 says packets addressed to the delegated prefix but not to any assigned subnet must be dropped, which in practice means an unreachable route for the unused parts of the delegation. Our post on IPv6 address autoconfiguration explains SLAAC, DHCPv6 and privacy addresses on the client side.

On Linux, systemd-networkd implements both halves. On the WAN interface, the DHCPv6 client requests a prefix, and PrefixDelegationHint= in the [DHCPv6] section sends the size hint. On each internal interface, DHCPPrefixDelegation=yes assigns a subnet from the delegated prefix, SubnetId= in the [DHCPPrefixDelegation] section picks which one, and IPv6SendRA=yes announces it with Router Advertisements. The [DHCPPrefixDelegation] section's Announce= setting (default yes) controls whether the delegated prefix goes into the RA.

How do IDS and IPS work at home?

An intrusion detection system (IDS) inspects copies of traffic and raises alerts when it matches signatures or protocol anomalies. An intrusion prevention system (IPS) sits in the forwarding path and can drop matching packets. Suricata describes itself as "a high performance Network IDS, IPS and Network Security Monitoring engine", open source and owned by the community run Open Information Security Foundation (OISF), licensed under GPL version 2.

Modes and placement

ModeHow packets reach the engineEffect if the engine stopsGood first choice for
IDS (passive) on a LAN interfaceCaptured with AF_PACKET or similar; the original packet is forwarded by the kernel regardlessAlerts stop; traffic keeps flowingEveryone starting out
IPS at layer 3 with NFQUEUEAn nftables rule queues forwarded packets to Suricata, which returns a verdictWith the bypass option, packets pass; without it, all queued traffic is droppedUsers who have tuned rules for weeks in IDS mode
IPS at layer 2 with AF_PACKET or netmapSuricata copies packets between two dedicated interfacesLink stops unless you add a hardware or software bypassBridged designs; needs two spare ports

Suricata's own IPS documentation explains the important details. In IPS mode, it "will allow all traffic, and will use drop or reject rules to block unwanted traffic". Exception policies default to drop in IPS mode, so packets are dropped rather than passed uninspected when Suricata hits an internal error or resource limit. TCP stream inspection also changes: in IDS mode data is inspected after it is acknowledged, while in IPS mode new data is inspected immediately in a sliding window. For NFQUEUE, the docs show an nftables chain on the forward hook at priority 10, evaluated after your normal filter rules, with a queue rule; the bypass option makes the queue behave like accept when no program is listening, which "can be used to avoid downtime of link when Suricata is not running but this also means that the blocking feature will not be present". The fanout option maps packets to queues by CPU so you can run one queue per core.

Recent Suricata documentation also describes a separate firewall mode. Its key difference from IPS mode is a default drop policy, so traffic is accepted only if a rule allows it. Treat it as an advanced option; at home, nftables or pf remains the place for your allow rules.

Performance trade offs

The IDS is where a small firewall runs out of headroom. Suricata's runmode documentation lists single, workers and autofp, and says "generally, the workers runmode performs the best". In workers mode each thread handles a packet from capture to output, which scales with cores. Practical rules of thumb:

  • Enable only the rule categories that match what you run. A home network with no mail server, no industrial controllers and no web servers does not need those signatures.
  • Inspect the internal side, not the WAN. Rules on the WAN see every scan on the internet and generate noise; rules on the LAN side see what your own devices do, which is the more useful signal.
  • Watch CPU and packet drop counters under a full speed download before you go inline. If the engine cannot keep up in IDS mode, it will throttle you in IPS mode.

Privacy trade offs

An IDS records protocol metadata about every device in the house: domain names looked up, TLS server names, HTTP hosts, file transfers and alerts. That is the point of it, and it is also sensitive. Decide who can read the logs, keep retention short (days, not months), store logs on the firewall or a host you control rather than an online service, and tell the people who live with you that the network is monitored. Encrypted traffic limits what any IDS can see; it will see that a connection happened and some handshake metadata, not page contents.

Rule updates

suricata-update is the official tool for fetching and managing rule sources. By default it downloads the rule set into /var/lib/suricata/rules/ and merges all enabled rules into a single file, /var/lib/suricata/rules/suricata.rules. Rules disabled by default can be turned on with /etc/suricata/enable.conf, and noisy rules turned off with disable.conf. Run it daily from a timer, then reload Suricata.

How do you add DNS filtering and encrypted upstream DNS without breaking devices?

DNS is the cheapest, most effective filter in a home network. Every device asks a resolver before connecting, and you control the resolver. A good design has three layers.

Layer 1: a local validating resolver for every zone

Run a recursive or forwarding resolver on the firewall, listening on each internal interface. Unbound, from NLnet Labs, is a common choice on all three platform families; the OpenBSD PF guide's own "Building a Router" example enables it with rcctl enable unbound as the LAN's caching resolver. Useful options documented in unbound.conf(5):

  • access-control: limits which subnets may query.
  • auto-trust-anchor-file: enables DNSSEC validation with a root trust anchor kept current by RFC 5011 probes.
  • qname-minimisation: yes sends the minimum number of labels upstream "to enhance privacy".
  • private-address: strips private addresses from answers for public names, which blocks DNS rebinding attacks against devices on your LAN.

Layer 2: encrypted transport to an upstream resolver

If you forward rather than resolve recursively, encrypt the hop to the upstream resolver so your ISP and anyone on the path cannot read or alter your queries.

ProtocolStandardPortWhat the firewall can seeTypical home use
Plain DNSRFC 1035 family53 UDP and TCPEverythingInside the house, from devices to your resolver
DNS over TLS (DoT)RFC 7858, May 2016853 TCPThat a connection to port 853 happenedResolver to upstream
DNS over HTTPS (DoH)RFC 8484, October 2018443 TCP (HTTPS)Looks like any HTTPS connectionBrowsers and some apps, often bypassing your resolver
DNS over QUIC (DoQ)RFC 9250, May 2022853 UDPThat a QUIC flow to port 853 happenedNewer resolvers and clients

Unbound supports DoT upstream with forward-tls-upstream: yes in a forward-zone and tls-cert-bundle: pointing at your system CA bundle. In forward-addr:, you append @853 for the port and # plus the server's TLS name so Unbound can authenticate it.

Layer 3: filtering with a Response Policy Zone

unbound.conf(5) documents an rpz: clause, "Configuration for Response Policy Zones that allows for DNS filtering", which requires the respip module. An RPZ is a DNS zone file where each record says what to do with a name, such as answer NXDOMAIN. You can load a blocklist from a URL with a refresh interval or from a local file. Local overrides go in local-zone: statements with types such as always_nxdomain.

Keeping devices from going around you

Devices bypass a local resolver in three ways, and the fixes have limits.

  • Hard coded plain DNS on port 53. Redirect it. An nftables redirect in the NAT prerouting chain sends any port 53 query from IoT and guest zones to the firewall's resolver.
  • DoT on port 853. Block port 853 outbound from IoT and guest. Devices that only try DoT will usually fall back to the DNS server DHCP gave them.
  • DoH on port 443. It is ordinary HTTPS. Blocking by port breaks the web. For browsers you control, configure them to use your resolver. MozillaWiki's Trusted Recursive Resolver page documents Firefox's network.trr.mode setting (0 off by default, 2 TRR first, 3 TRR only, 5 off by choice) and links Mozilla's instructions for network operators who want DoH disabled on their network. For other devices, accept that some DoH traffic will go around you, and rely on the forward chain rules to limit where those devices can connect.

What breaks: captive portals in guest devices, streaming boxes that hard code a resolver and refuse a redirected answer, and anything relying on a blocked tracking domain to function. Keep a short allowlist and a way to bypass filtering per device (a separate RPZ policy or a local-zone with transparent).

Step by step: build the firewall on Debian with nftables

This walkthrough uses Debian with systemd-networkd, nftables, Unbound and Suricata. Interface names are examples: enp1s0 is WAN, enp2s0 is the trunk to the switch. Run ip link to find yours.

Step 1: write the installer to a USB drive

Warning: the next command erases the target drive. Writing an installer image overwrites every byte on the device you name. Pointing it at the wrong device destroys that disk's data, including your own system drive.

First identify the USB drive and confirm the path. Unplug it, run the listing, plug it in, run it again, and compare.

# Linux: list block devices with size, model and transport
lsblk -o NAME,SIZE,MODEL,TRAN,MOUNTPOINTS
# macOS equivalent
diskutil list external

Only when you are certain which device is the USB stick (for example /dev/sdX on Linux), verify the image checksum against the published checksum file and write it:

sha512sum -c SHA512SUMS --ignore-missing
sudo umount /dev/sdX* 2>/dev/null
sudo dd if=debian-netinst.iso of=/dev/sdX bs=4M status=progress conv=fsync

Install a minimal system on the mini PC with only the SSH server. During installation the target disk will also be erased; the installer names the disk it will use, so check the model and size against the mini PC's internal drive before you confirm.

Step 2: enable forwarding and install packages

sudo apt update
sudo apt install nftables unbound suricata suricata-update unattended-upgrades
sudo tee /etc/sysctl.d/90-router.conf >/dev/null <<'EOF'
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF
sudo sysctl --system

The nftables wiki's home router example reminds you of the same first step: forwarding must be on or nothing is routed.

Step 3: configure the WAN with DHCPv4 and DHCPv6 prefix delegation

# /etc/systemd/network/10-wan.network
[Match]
Name=enp1s0

[Network]
DHCP=yes
IPv6AcceptRA=yes

[DHCPv6]
PrefixDelegationHint=::/56

If the ISP hands out a /60, change the hint; RFC 7084 says the hint is only a hint, and the router must accept whatever size it is given (WPD-3).

Step 4: create the VLANs on the trunk

Each VLAN is a netdev with an ID. The [VLAN] section's Id= takes an integer from 0 to 4094 and is compulsory, according to systemd.netdev(5).

# /etc/systemd/network/20-vlan10.netdev
[NetDev]
Name=vlan10
Kind=vlan

[VLAN]
Id=10

Repeat for vlan20, vlan30 and vlan99. Then attach all of them to the trunk port; the VLAN= setting in the [Network] section of systemd.network(5) lists stacked netdevs to create on the interface:

# /etc/systemd/network/20-trunk.network
[Match]
Name=enp2s0

[Network]
VLAN=vlan10
VLAN=vlan20
VLAN=vlan30
VLAN=vlan99
LinkLocalAddressing=no

Step 5: address each VLAN, serve DHCP and announce IPv6

# /etc/systemd/network/30-vlan10.network
[Match]
Name=vlan10

[Network]
Address=192.168.10.1/24
DHCPServer=yes
DHCPPrefixDelegation=yes
IPv6SendRA=yes

[DHCPServer]
EmitDNS=yes
DNS=192.168.10.1

[DHCPPrefixDelegation]
UplinkInterface=enp1s0
SubnetId=0x10
Announce=yes

Copy it for vlan20 (192.168.20.1/24, SubnetId=0x20) and vlan30 (192.168.30.1/24, SubnetId=0x30). For vlan99, set only the static address, the DHCP server and no prefix delegation. Then apply:

sudo systemctl enable --now systemd-networkd
networkctl status vlan10
ip -6 addr show dev vlan10

networkctl should show the delegated /64 on each VLAN once the ISP has answered.

Step 6: write the nftables rule set

This rule set follows the nftables wiki's home router structure: policy drop, a connection tracking verdict map, and per zone jump chains. It uses the inet family so one table covers IPv4 and IPv6, and only NATs IPv4.

# /etc/nftables.conf
flush ruleset

define WAN  = "enp1s0"
define LAN  = "vlan10"
define IOT  = "vlan20"
define GST  = "vlan30"
define MGT  = "vlan99"
define ADMIN = 192.168.10.5

table inet fw {
  set rfc1918 {
    type ipv4_addr; flags interval;
    elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 }
  }

  chain from_wan {
    # DHCPv6 replies from the ISP to our client
    udp sport 547 udp dport 546 accept
    # ICMPv6 that must not be dropped (RFC 4890)
    icmpv6 type { destination-unreachable, packet-too-big, time-exceeded,
                  parameter-problem, echo-request, echo-reply,
                  nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
    icmp type { destination-unreachable, time-exceeded } accept
  }

  chain from_inside_common {
    udp dport { 53, 67 } accept
    tcp dport 53 accept
    icmpv6 type { nd-router-solicit, nd-neighbor-solicit, nd-neighbor-advert,
                  echo-request, packet-too-big } accept
    icmp type echo-request limit rate 5/second accept
  }

  chain input {
    type filter hook input priority 0; policy drop;
    ct state vmap { established : accept, related : accept, invalid : drop }
    iifname "lo" accept
    iifname $WAN jump from_wan
    iifname { $LAN, $IOT, $GST, $MGT } jump from_inside_common
    iifname $LAN ip saddr $ADMIN tcp dport 22 accept
    limit rate 10/minute log prefix "nft input drop: "
  }

  chain forward {
    type filter hook forward priority 0; policy drop;
    ct state vmap { established : accept, related : accept, invalid : drop }
    # ICMPv6 errors for transit traffic (RFC 4890)
    icmpv6 type { destination-unreachable, packet-too-big, time-exceeded,
                  parameter-problem, echo-request, echo-reply } accept
    # stop IoT and guest from using DNS over TLS or QUIC around our resolver
    iifname { $IOT, $GST } tcp dport 853 reject
    iifname { $IOT, $GST } udp dport 853 reject
    meta iifname . meta oifname {
      $LAN . $WAN,
      $IOT . $WAN,
      $GST . $WAN,
      $LAN . $IOT
    } accept
    iifname $LAN ip saddr $ADMIN oifname $MGT accept
    limit rate 10/minute log prefix "nft fwd drop: "
  }

  chain dstnat {
    type nat hook prerouting priority -100; policy accept;
    # force plain DNS from IoT and guest to the local resolver
    iifname { $IOT, $GST } meta l4proto { tcp, udp } th dport 53 redirect to :53
  }

  chain srcnat {
    type nat hook postrouting priority 100; policy accept;
    oifname $WAN ip saddr @rfc1918 masquerade
  }
}

Load it and make it persistent:

sudo nft -c -f /etc/nftables.conf   # check syntax without applying
sudo nft -f /etc/nftables.conf
sudo systemctl enable nftables
sudo nft list ruleset

Before you load rules on a box you reach over SSH, schedule a rollback: sudo systemd-run --on-active=5min nft flush ruleset and cancel it once you confirm you still have access. A locked out firewall is the most common first day mistake.

For BSD users, the same policy in pf looks like this, following the OpenBSD "Building a Router" example and pf.conf(5):

# /etc/pf.conf (OpenBSD shown; FreeBSD differs in interface names)
wan = "em0"
lan = "vlan10"
iot = "vlan20"
gst = "vlan30"

set block-policy drop
set skip on lo0
match out on egress inet from !(egress:network) to any nat-to (egress:0)
antispoof quick for { egress $lan $iot $gst }
block all
pass out quick on egress
pass in on { $lan $iot $gst } proto { udp tcp } to self port domain
pass in on { $lan $iot $gst } proto udp to self port bootps
pass in on $lan from $lan:network
pass in on $iot from $iot:network to ! { $lan:network $gst:network }
pass in on $gst from $gst:network to ! { $lan:network $iot:network }
pass inet6 proto icmp6 all icmp6-type { unreach toobig timex paramprob echoreq echorep }
pass inet6 proto icmp6 all icmp6-type { routersol routeradv neighbrsol neighbradv }

On FreeBSD, the Handbook enables pf with sysrc pf_enable=yes and logging with sysrc pflog_enable=yes. On OpenBSD, pf is enabled by default and pfctl -nf /etc/pf.conf checks the rule set before you load it with pfctl -f /etc/pf.conf.

Step 7: configure Unbound with DoT upstream and an RPZ blocklist

# /etc/unbound/unbound.conf.d/home.conf
server:
  interface: 192.168.10.1
  interface: 192.168.20.1
  interface: 192.168.30.1
  interface: 192.168.99.1
  access-control: 192.168.0.0/16 allow
  qname-minimisation: yes
  private-address: 10.0.0.0/8
  private-address: 172.16.0.0/12
  private-address: 192.168.0.0/16
  private-address: fd00::/8
  private-address: fe80::/10
  tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
  module-config: "respip validator iterator"

rpz:
  name: blocklist.local
  zonefile: /var/lib/unbound/blocklist.rpz
  rpz-log: yes

forward-zone:
  name: "."
  forward-tls-upstream: yes
  # replace with the resolver you choose; the name after # must match its certificate
  forward-addr: 192.0.2.10@853#dns.example.net

Add the IPv6 addresses of each interface and their access-control lines once you know the delegated prefix, or listen on ::0 with access-control limiting to your prefix. Then check and restart:

sudo unbound-checkconf
sudo systemctl restart unbound
dig @192.168.20.1 example.com +dnssec

If you choose not to forward at all, delete the forward-zone and Unbound resolves recursively from the root, with DNSSEC validation from its trust anchor. That avoids sending all your queries to one upstream provider, at the cost of plain text queries to authoritative servers.

Step 8: add Suricata, first as an IDS

Start passive. Set the capture interface to vlan10 (and later vlan20) in /etc/suricata/suricata.yaml under af-packet, set HOME_NET to your internal ranges, then fetch rules:

sudo suricata-update
sudo suricata -T -c /etc/suricata/suricata.yaml   # test configuration
sudo systemctl enable --now suricata
sudo tail -f /var/log/suricata/fast.log

After a few weeks of tuning, if you want inline blocking, add a chain from Suricata's NFQUEUE documentation after your filter rules and run Suricata with -q 0:

# evaluated after the forward filter (priority 10 vs 0)
sudo nft add chain inet fw ips '{ type filter hook forward priority 10; }'
sudo nft add rule inet fw ips iifname "vlan20" queue num 0 bypass

Queue only the zones you care about, keep bypass so a stopped engine does not cut the household off, and convert specific alert rules to drop rules one at a time.

Step 9: turn on updates

Debian's security page notes that unattended-upgrades "can be installed to keep the computer current with the latest security (and other) updates automatically".

sudo dpkg-reconfigure -plow unattended-upgrades
sudo unattended-upgrade --dry-run --debug | tail -20

On OpenBSD, syspatch fetches, verifies and installs binary patches; syspatch -c lists available patches and is "suitable for cron". On FreeBSD, the Handbook's update procedure is freebsd-update fetch followed by freebsd-update install, and if a kernel patch was applied, a reboot; compare freebsd-version -k with uname -r to see whether one is pending. Firewall distributions have their own update menus; read their release notes before each upgrade.

A kernel update on a firewall needs a reboot to take effect. Schedule it for a quiet hour, and check that the box comes back without you, since there is no other route to the internet while it is down.

Step 10: back up the configuration off the box

Everything that defines this firewall is text. Back it up with a date and keep at least one copy off the device.

sudo tar czf /root/fw-config-$(date +%F).tgz \
  /etc/nftables.conf /etc/systemd/network /etc/unbound \
  /etc/suricata /etc/sysctl.d/90-router.conf
scp /root/fw-config-$(date +%F).tgz admin@nas.home.arpa:/backups/firewall/

Better still, keep /etc/nftables.conf and the network files in a git repository and commit each change with a message saying why. When you change something and the house loses internet, git diff tells you exactly what changed. Test a restore at least once onto a spare disk or a virtual machine so you know the backup is complete. Our home NAS guide covers where those backups should live.

Step 11: test from each segment

Test like an outsider, from each zone. Plug a laptop into an access port in each VLAN (or join each SSID) and run:

TestFrom LANFrom IoTFrom guest
ip addr shows the right subnet and an IPv6 address from that VLAN's /64yesyesyes
dig example.com answers from the firewallyesyesyes
dig @192.0.2.53 example.com (an outside resolver)answersredirected to local resolverredirected to local resolver
ping 192.168.10.1 (LAN gateway)repliesno replyno reply
nc -vz 192.168.10.20 445 (a NAS on the LAN)connectstimes outtimes out
curl -sI https://example.comworksworksworks
ssh 192.168.99.2 (switch management)admin host onlyfailsfails
A blocked domain from your RPZNXDOMAINNXDOMAINNXDOMAIN

On the firewall, watch the drops while you test:

sudo journalctl -kf | grep "nft "
sudo nft list ruleset -a | less

Then test from outside. From a phone on mobile data, try to reach your WAN IPv4 address and one of your IPv6 addresses on a common port; both should time out.

A dated timeline of the standards behind a home firewall

DateDocumentWhat it gave home firewalls
February 1996RFC 1918, Address Allocation for Private InternetsThe 10/8, 172.16/12 and 192.168/16 ranges every home LAN uses
May 2007RFC 4890, ICMPv6 filtering recommendationsWhich ICMPv6 messages a firewall must pass
September 2009NIST SP 800-41 Rev. 1Firewall policy guidance: deny by default, document, test, manage
January 2011RFC 6092, simple security for IPv6 CPEDefault inbound filtering and anti spoofing rules for residential IPv6
November 2013RFC 7084, IPv6 CE router requirementsPrefix delegation, one /64 per LAN, Router Advertisement behaviour
May 2016RFC 7858, DNS over TLSEncrypted resolver to upstream on port 853
October 2018RFC 8484, DNS over HTTPSDNS inside HTTPS; the reason port based DNS control has limits
November 2018RFC 8415, DHCPv6 (consolidated)The IA_PD prefix delegation mechanism your WAN uses
May 2022RFC 9250, DNS over QUICEncrypted DNS over QUIC on UDP 853
December 2022IEEE 802.1Q-2022 publishedCurrent edition of the VLAN tagging standard

Common mistakes

  • Locking yourself out. Loading a new rule set over SSH without a scheduled rollback. Always use nft -c or pfctl -n first and a timed flush.
  • Dropping all ICMPv6. Copying IPv4 habits into IPv6 breaks path MTU discovery and Neighbor Discovery. Follow RFC 4890's must not drop list.
  • Forgetting that IPv6 bypasses your IPv4 rules. A rule set that only covers ip and not inet (nftables) or inet6 (pf) leaves IPv6 wide open or wide shut. Test both.
  • Trusting the IoT VLAN to be isolated when the access point bridges it. If the access point does not tag SSIDs, the "IoT network" is on your LAN. Check with ip addr from a device on each SSID.
  • Leaving the switch and access point management on the user VLANs. Anyone on the LAN, or a compromised IoT device on a flat network, can reach their admin pages.
  • Going inline with the IDS on day one. A rule that misfires, or an engine that falls behind, takes the household offline. Run IDS mode first, keep bypass on the queue.
  • Logging everything forever. DNS and IDS logs are a record of your household's behaviour. Rotate them and keep retention short.
  • Blocking port 443 to stop DoH. It stops the web. Configure browsers you control and accept limits for others.
  • Never updating. A firewall that never gets patched becomes the weakest device on the network. Our post on end of life firewall risks explains what happens when updates stop entirely.
  • No off box backup. If the SSD dies, rebuilding from memory takes a weekend. Rebuilding from a tarball or git repository takes an hour.
  • Exposing the admin interface on the WAN. Remote administration belongs behind a VPN, not on an open port.

An update and backup routine

WhenTaskCommand or action
Daily (automatic)Security updatesunattended-upgrades, or syspatch -c from cron on OpenBSD
Daily (automatic)IDS rule refreshsuricata-update then reload Suricata
WeeklyRead the drop and IDS logs for anything newjournalctl -k, fast.log
WeeklyCheck DNS blocklist refresh and resolver healthunbound-control stats_noreset
MonthlyReboot if a kernel update is pendingCompare running and installed kernel; reboot at a quiet hour
MonthlyBack up configuration off the boxDated tarball or git commit and push to the NAS
Every config changeCommit with a reasongit commit -am "allow LAN to printer on IoT"
Twice a yearTest restore and rerun the segment testsRestore onto a spare disk or VM; run the Step 11 table
Twice a yearUpdate switch and access point firmware from their makersCheck the maker's support page; back up their configs first
YearlyReview the zone and rule table against the devices you own nowRemove rules for devices that are gone

FAQ

Do I need a mini PC firewall if my ISP router has a firewall?

Not strictly. The ISP router's NAT and default inbound blocking already stop unsolicited IPv4 connections. A mini PC firewall adds segmentation between your own devices, rules you can read, logging, DNS filtering, an optional IDS, and a patch cadence you control. It is worth it if you have many IoT devices, a homelab, or work devices at home.

How many network ports does a home firewall need?

Two is enough: one WAN and one trunk to a managed switch carrying all internal VLANs. A third port is handy for a dedicated management link or a second WAN, and a fourth if you want an inline bridge for an IDS.

Is pf or nftables better for a home firewall?

Neither is better in general. pf on OpenBSD and FreeBSD has a compact, readable syntax and documentation built around a home router example. nftables is the Linux framework with very broad hardware driver support. Choose the operating system you are most comfortable updating and debugging.

How do I put IoT devices on a separate VLAN?

Create the VLAN on the firewall and the switch, tag it on the trunk, then either plug wired IoT devices into untagged access ports in that VLAN or create a separate SSID on your access point mapped to that VLAN. Add forward rules allowing IoT to WAN, and LAN to IoT if you need to control devices from your phone.

Does an IDS slow down my internet?

In passive IDS mode, it does not slow forwarding, but it uses CPU. In inline IPS mode, every packet waits for a verdict, so a small CPU with a large rule set can cap your throughput. Measure in IDS mode first, enable only relevant rules, and use the workers runmode.

How do I stop devices from bypassing my DNS filter?

Redirect plain DNS on port 53 from untrusted zones to your resolver, block outbound port 853 to stop DNS over TLS and QUIC, and configure the browsers you control to use your resolver. DNS over HTTPS on port 443 cannot be reliably blocked by port, so pair DNS filtering with forward rules that limit what untrusted devices can reach.

What IPv6 prefix size should I ask my ISP for?

Ask for enough /64s for every internal network, rounded up to the nearest nibble, as RFC 7084 describes. Five networks round up to a /60; up to 256 networks fit in a /56. Your ISP decides what it actually delegates.

How often should I update my home firewall?

Apply security updates automatically every day where your platform supports it, refresh IDS rules daily, reboot for kernel updates within a week or two, and review switch and access point firmware twice a year.

What should I back up on a home firewall?

Every configuration file that defines it: the firewall rule set, network and VLAN definitions, DHCP and DNS resolver configuration, IDS configuration and local rule changes, and sysctl settings. Keep a dated copy off the box and test restoring it.

Where Safeguard fits

Safeguard does not filter packets, run an IDS, filter DNS, manage switches or access points, or scan device firmware. Nothing in the build above depends on it. The honest connection is that a home firewall is a stack of open source software: a kernel, a packet filter, a resolver, an IDS engine and their libraries, and often a few container images for dashboards, log shippers or blocklist updaters that people run next to it.

For the code and container images you build or pull yourself, Safeguard's software composition analysis builds a dependency inventory, container scanning lists the package versions inside an image, and SBOM generation records the result in SPDX or CycloneDX. The platform shows which projects contain a given component and version, and vulnerability matching and reachability analysis help decide what to fix first. If you run a log dashboard or a DNS statistics exporter in a container beside your firewall, "which of my images still ship that vulnerable library" becomes a quick question rather than an afternoon of digging.

A rebuilt firewall or the containers beside it pull packages and libraries again, and the resolver, IDS engine and their dependencies all ship open source components that receive security fixes on someone else's schedule. The operating system packages on the firewall itself still come from your distribution's updates, but for the open source components you build and run around it, the approach below applies.

Fix it without waiting for upstream

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.

3. Gold Registry: start from components that are already fixed

registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingThe Gold scan GitHub Action in CI, plus Safeguard policies and gates
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch (early access)
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would help

These demos show dependency, SBOM and remediation workflows, not packet filtering, IDS or DNS filtering. You can download Safeguard and follow the CLI docs to try them on your own projects.

This demo sets up the Safeguard runner on your own machine, approves it with its device code and scans from the terminal. Pointed at a container image you run beside your firewall, such as a log dashboard or a blocklist updater, the same scan lists the packages and versions inside it and their known vulnerabilities, so you know which images to rebuild.

Click through it at your own pace, or open it full screen.

This demo is a real CLI session: ask it to fix the lodash CVE in a repository and watch it read the finding and the advisory, ask before each command, move lodash to 4.17.21, run npm install and npm test with your approval, and push nothing. The same approve each step flow applies when you bump a dependency in a small script or web dashboard you wrote to report firewall or DNS statistics.

Click through it at your own pace, or open it full screen.

This demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. You can then ask which of your projects or images ship a given package version and get the answer from your own scan data rather than from memory.

Click through it at your own pace, or open it full screen.

Quick check

You have LAN, IoT, guest, management and a lab VLAN, and your ISP lets you hint a prefix size. What should you ask for?

Five networks need five /64s, which takes 3 bits. RFC 7084 says to round up to the nearest nibble, so 4 bits, which means a /60 (16 /64 networks). The ISP may still delegate something different, and the router must accept it.

Your new nftables rules drop every ICMPv6 packet on the WAN. Web pages on some sites hang and IPv6 stops working after a while. Why?

Dropping Packet Too Big breaks path MTU discovery, so large packets on some paths never arrive, and dropping Router Advertisements and Neighbor Discovery stops IPv6 from working on the link. RFC 4890 lists these as traffic a firewall must not drop. Allow them explicitly, as in the Step 6 rule set.

Sources

Last reviewed: 23 October 2026. Option names follow the current online man pages (systemd 262, Suricata development documentation); check the man pages installed on your own system for the options your version supports.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.