Skip to content
Liberated Bread

Keep It Off the Internet

Most of the guides on this site end up in the same place: the device speaks a perfectly good local protocol, and the only thing that can take it away is a firmware update pushed from a server you don't control.

So don't let it reach the server.

This page is the shared how-to. Every device guide links here instead of re-explaining it, and the worked example at the bottom is the Roomba, because it's the case where getting the order wrong actually costs you something.

What you're blocking, and what you're not

You are blocking one thing: the device's ability to open connections to the internet. That's what carries over-the-air firmware updates, telemetry, and the vendor's remote-access path.

You are not blocking your own LAN. Local control — the whole point — is your phone or your Home Assistant box talking to the device across your own network. None of that leaves the house, and none of it should be touched.

Three things have to keep working:

  Why
Unicast traffic on your LAN The actual control channel. HTTP, MQTT, whatever the device speaks.
Multicast and broadcast on the device's subnet mDNS (5353), SSDP (1900) and vendor discovery probes are how anything finds the device in the first place.
DHCP, and often DNS A device that can't get a lease falls off the network entirely. Let it resolve names; it just won't be able to connect to them.

The honest limit of DNS-only blocking. Sinkholing hostnames in Pi-hole is the easiest thing on this page and the weakest. A device that ignores your DHCP-supplied resolver, hardcodes 8.8.8.8, or uses DNS-over-HTTPS walks straight past it. It's a fine belt; the firewall rule is the braces.

Blocking "just the update server" mostly doesn't work

The tempting version of this is to block one hostname and leave everything else alone, so the vendor app keeps working from outside the house. In practice:

Block outbound wholesale. Then decide, per device, whether you actually miss anything.

Pick a lever

Lever Strength Cost
Block WAN by IP or MAC Strong. The device simply can't route out. Needs a fixed address. Per-device bookkeeping.
Isolate a whole VLAN Strongest, and scales to every IoT device at once. Discovery stops crossing VLANs unless you fix it. Real work.
DNS sinkhole Weak on its own. Trivial. Good as a second layer or a first experiment.

If you have one Roomba, block by IP. If you have a house full of this stuff, build the VLAN — you'll do it once instead of thirty times.

Whichever you pick, give the device a fixed address first. A DHCP reservation is fine and is what every recipe below assumes; the point is that a rule targeting 192.168.1.50 is worthless if tomorrow the device is 192.168.1.83.


UniFi (Cloud Gateway, Dream Machine, UXG)

UniFi has moved this UI around repeatedly across Network 7, 8 and 9 releases, so what follows describes what the rule has to say, with the current setting names. If a menu has moved, you're looking for the rule, not the path.

1. Pin the address

Find the device under Client Devices, open it, and set a Fixed IP Address. UniFi writes a DHCP reservation.

2a. The simple version — one client, one rule

Settings → Security → Traffic & Firewall Rules → Traffic Rules → Create Entry

That's it. The device keeps its lease, keeps answering on the LAN, and gets nothing out of the WAN.

2b. The scalable version — a zone

UniFi Network 9 replaced the old rule list with zone-based firewalling: interfaces get grouped into zones and you write policies between zones.

  1. Settings → Networks — create an IoT VLAN and put the device on it.
  2. Settings → Security → Zone-Based Firewall — create an IoT zone and assign that network to it.
  3. Policy: Source IoT zone → Destination External (WAN) → Block.
  4. Policy: Source your trusted/internal zone → Destination IoT zone → Allow. This is stateful, so replies come back without a second rule — your phone and Home Assistant can reach in, the IoT zone can't reach out.

New zones block everything between themselves by default, so expect to add allows rather than blocks once you're in this mode.

The gotcha that catches everyone. Putting devices on their own VLAN stops discovery dead. Turn on Multicast DNS for the IoT network (**Settings → Networks → → Advanced**) so mDNS crosses the boundary.

That fixes mDNS and only mDNS. UniFi's reflector does not relay plain UDP broadcast, so a device found by a broadcast probe — a Roomba on UDP 5678, a TP-Link Kasa plug on 9999 — will not be discovered from another VLAN no matter what you enable. Either keep the controller on the same VLAN as the device, or skip discovery and configure it by its fixed IP.


MikroTik (RouterOS 7)

An address list plus two forward rules. Add devices to the list later and nothing else changes — this is the nice thing about doing it MikroTik's way.

# 1. Pin the address (adjust the server name to yours; defconf is the default)
/ip dhcp-server lease
add address=192.168.88.50 mac-address=AC:F4:73:AA:BB:CC server=defconf \
    comment="Roomba"

# 2. One list, however many devices
/ip firewall address-list
add list=no-internet address=192.168.88.50 comment="Roomba"

# 3. Drop anything from the list heading out of the WAN...
/ip firewall filter
add chain=forward action=drop src-address-list=no-internet \
    out-interface-list=WAN comment="no-internet: block outbound"

# 4. ...and anything from the WAN heading to it.
add chain=forward action=drop dst-address-list=no-internet \
    in-interface-list=WAN comment="no-internet: block inbound"

Two things to check:

Use chain=forward, not chain=input. input is traffic to the router itself, and dropping that takes away the device's DHCP and DNS.

Sinkholing a hostname as a second layer:

/ip dns static
add name=disc-prod.iot.irobotapi.com address=127.0.0.1 type=A

OPNsense and pfSense

Same shape on both. Order matters — firewall rules are evaluated top down and the first match wins, so the allow has to come first.

  1. **Services → DHCPv4 → ** — add a static mapping.
  2. Firewall → Aliases — make a Host alias, e.g. roomba, holding that IP. (Do this even for one device. When there are five, you edit the alias instead of the rules.)
  3. Firewall → Rules → LAN, in this order:
    • Pass — Source roomba, Destination LAN net. Keeps local control.
    • Block — Source roomba, Destination any. Everything else, including the internet.

If you have several internal subnets, use an RFC1918 alias as the pass destination instead of LAN net.


OpenWrt

uci add firewall rule
uci set firewall.@rule[-1].name='Block Roomba WAN'
uci set firewall.@rule[-1].src='lan'
uci set firewall.@rule[-1].dest='wan'
uci set firewall.@rule[-1].src_ip='192.168.1.50'
uci set firewall.@rule[-1].target='REJECT'
uci commit firewall
service firewall restart

REJECT rather than DROP on purpose: the device gets an immediate refusal instead of retrying a dead connection for minutes on end, which is easier on its battery and much easier to read in a packet capture.

Pin the lease in Network → DHCP and DNS → Static Leases, or in /etc/config/dhcp.


Firewalla

Open the device in the app, then Rules → Block → Internet. Firewalla understands the difference between internet and LAN traffic natively, so there's nothing else to get right.


A generic consumer router

Look for Access Control, Parental Controls, or Device Blocking. Most will block a device by MAC address.

Two caveats worth knowing before you rely on it:

If neither works, the DNS sinkhole below is your remaining option on that hardware.


DNS sinkholing (Pi-hole, AdGuard Home)

Add the vendor's hostnames to a blocklist and they resolve to nothing.

This is a fine second layer and a bad only layer, for the reason in the box near the top: it works exactly as long as the device uses the resolver you gave it. To make it harder to escape:


Check that it worked

Watch the drops. Every platform above logs them: UniFi under Insights, MikroTik with /log print if you add log=yes to the rules, OPNsense/pfSense under Firewall → Log Files. Seeing the device try and fail is the confirmation.

Watch the wire — from the router, not from your laptop. Run this on the router or AP, or on a machine fed by a mirrored/SPAN switch port:

# Everything the device sends that isn't staying on the LAN.
sudo tcpdump -ni eth0 host 192.168.1.50 and not net 192.168.1.0/24

Retries with no replies is a working block.

The location matters more than the filter. On switched Ethernet or Wi-Fi, an ordinary machine on the same subnet never receives traffic between the device and the gateway — the switch forwards those frames only to the port they are addressed to, and promiscuous mode does not change what the switch sends you. Capturing from your laptop therefore shows silence whether the block works or not, so silence there tells you nothing. Only the drop log above and a capture at the gateway can distinguish "blocked" from "not seen".

Then use the thing. Open Home Assistant, or the Liberated Bread app, and send a command. If it works, you're done: the device is alive, local, and can't be updated out from under you.


Worked example: an iRobot Roomba

Wi-Fi Roombas run an MQTT broker on the robot itself, which keeps working with no internet at all. The 2025 model line ships without that broker — which is a fairly direct demonstration of why you'd want to freeze the firmware on the one you have.

Do this in the right order. Get the robot's password first, block it second. One of the two ways to get that password goes through iRobot's servers, and a firewall rule added first turns a five-minute job into temporarily undoing your own work. The Roomba guide walks through both routes.

What the robot talks to

Port / protocol Where Blocking it
TCP 8883 (MQTT over TLS) The robot, on your LAN Leave alone. This is local control.
UDP 5678 (broadcast) Your LAN Leave alone. Discovery.
TCP 443 iRobot / AWS IoT Block. Cloud control, telemetry, firmware.
UDP 123 (NTP) Time servers See below.

The published hostnames — disc-prod.iot.irobotapi.com, unauth2.prod.iot.irobotapi.com, and the AWS IoT endpoint the robot's cloud side uses — are listed in our protocol spec as reported, not confirmed: nobody here has captured a robot's DNS traffic. If you're building a sinkhole list rather than a firewall rule, log the robot's own lookups for a day and use those. If you're doing it with a firewall rule, you don't need the list at all — which is the better reason to prefer one.

What you keep

What you lose

Two Roomba-specific notes

Time. iRobot lists NTP among the ports the robot uses, and local commands carry a timestamp from the sender. We haven't tested what a robot with no time source does. If yours starts behaving oddly after you block it, allow outbound UDP 123, or hand it your gateway's own NTP server via DHCP option 42, before you go looking for anything more exotic.

One client at a time. This isn't about the firewall, but it's the next thing that will confuse you: the robot accepts a single local connection, and a new one evicts the old. If the iRobot app stops working locally the moment Home Assistant connects, that's why — and it's why Home Assistant polls rather than holding the socket open.


The advice to firewall a Roomba so an over-the-air update can't take the local API away is koalazak's, from dorita980 — as is the local protocol that makes it worth doing. This page generalises it to the rest of the devices we cover.

Router UIs change. If a menu path here is wrong, the rule description above it still tells you what to build — and please open an issue.