Fall 2026brianmars.comBuilt at EGBT Technologies
ReferenceNetwork

UFW, tailnet only

A firewall that answers only on the Tailscale interface, with one narrow exception for containers reaching the front door.

Names and addresses are placeholders. The list says what each one stands for.

What it does here

Nothing on the box answers on the home network or the internet. The tailnet is the only way in, so no router ports are ever opened.

From the manual

Canonical setup

Deny incoming, allow outgoing, add the rules you need, then enable.

Defaults
sudo ufw default deny incoming
sudo ufw default allow outgoing

Upstream: UncomplicatedFirewall on the Ubuntu wiki.

Edited for this build

In this build

  1. Allow in on tailscale0One rule covers SSH and mosh's UDP range for anything arriving over the tailnet.
  2. WireGuard port 41641/udp openIt helps Tailscale make direct connections through NAT.
  3. No LAN fallbackDecided before enabling. If you want SSH over the home network for when Tailscale is down, add sudo ufw allow OpenSSH (and 60000:61000/udp for mosh) first.
  4. Containers may reach Caddy on 443From the Docker address range to the box's tailnet address only. Added once the runner needs it.
Rules and enable
sudo ufw allow in on tailscale0
sudo ufw allow 41641/udp
sudo ufw enable && sudo ufw status verbose
The container exception (part 4)
sudo ufw allow from 172.16.0.0/12 to 100.x.y.z port 443 proto tcp
Where it breaks

Seams

Where this meets the rest of the build. Each one leads with what you will actually see.

Containers meet the tailnet-only firewall

Symptomconnections from a container to https://git.dev.tailnet.example time out

A container reaching a service name goes out to the box's tailnet address and back in to Caddy, and the firewall drops that hairpin. The scoped rule above lets it through, only for the Docker range, only to 443. Check it with docker run --rm alpine sh -c 'nc -zv git.dev.tailnet.example 443'.

The firewall meets your session

Symptomssh stops answering after ufw enable

Confirm ssh youruser@devbox works over Tailscale in a second terminal before you walk away. Keep console access to the box until you have.

mDNS meets the firewall

Symptomdevbox.local stops resolving

UDP 5353 is blocked with everything else. Allow it, or use the MagicDNS name. See the base hardening card.

Verify

Checks
sudo ufw status verbose    # deny incoming; allow on tailscale0

From another tailnet device, ssh youruser@devbox still works. From a device on the home network that isn't on the tailnet, it doesn't.

Corrections and updates
  1. 17 Oct 2026First written. Verified on Debian 13.6.