nfltr alongside Tailscale

People who run machines at home or in a small office often ask whether nfltr replaces Tailscale. It doesn't, and it isn't meant to. Tailscale is a mesh VPN for devices you own: you install its client on each device and sign in (what is Tailscale, read October 31, 2026), the devices form a private network (a tailnet), and traffic goes straight between them when the network allows. nfltr publishes single services: one port under a name, or one link, through a relay that every agent connects out to, with no network to join.

Both reach a machine that has no open port, so on paper they overlap. To see where they really overlap and where they don't, we ran both on the same home machine, then both at once, and checked four everyday cases with programs that pass or fail by exit code.

The setup

A box at home sits behind a router that forwards no port. It runs three services on loopback only: a web app, a database and an SSH server (in the test, small stand-ins). The owner's laptop is somewhere else. A friend has a browser and nothing installed.

Tailscale. The official Tailscale client runs on the box and on the laptop, both signed in to one tailnet. The client runs in userspace networking mode, as it does in containers: the tailnet's connections to the box reach its loopback ports, and programs on the laptop reach the tailnet through the client's local SOCKS5 proxy. The test has no internet, and Tailscale's own control server needs the internet and an account, so the control server is Headscale, the open-source implementation of it, with its built-in relay for peers that cannot reach each other directly. The clients are the same either way.

nfltr. On the box, one nfltr up file publishes the database under a name, end-to-end encrypted to its own certificate that the laptop pins, and gives the web app an unlisted HTTPS link with a password, for the friend. Nothing else is published:

agent: home
services:
  db:  { tcp: 5432, e2ee: true, e2ee_cert: e2ee.crt, e2ee_key: e2ee.key }
  web: { http: 8080, share_auth: "friend:${FRIEND_PASS}" }

On the laptop, nfltr up maps the box's database to a port on the laptop itself:

connect:
  db: { target: home/db, listen: "127.0.0.1:5432", pin: "${DB_PIN}" }

The database has its own certificate (any self-signed one; its pin is in the box's output), so the friend's link keeps the share's public certificate. Update, November 1, 2026: until v2.1.49 a certificate applied to every service in one nfltr up process, so the box needed two files, one per service, and the measurements below were taken that way on October 31; our check of this example now runs the one file above and passes.

Case 1: your laptop on your home machine

Both work, and neither needs a port forward. In the test, the router had no inbound rule in either setup, and none of the ports we tried on the home's public address answered.

The laptop then timed 200 echoes over one open connection to the database and 50 new connections, each until the database's first line arrived. The home router adds 10 ms each way.

echo p50 / p95 new connection p50 / p95 path
on the box itself < 0.1 / < 0.1 ms < 0.1 / < 0.1 ms loopback
Tailscale 23.3 / 25.3 ms 47.1 / 49.8 ms direct between the two devices
nfltr 23.3 / 25.0 ms 72.7 / 76.7 ms through the relay, end-to-end encrypted

On an open connection the two are the same: 20 ms of it is the router's delay. A new connection is 25 ms slower through nfltr, because each one is a new end-to-end TLS session through the relay, while the tailnet's encrypted session between the two devices is already up. In this test the relay sat next to the laptop. A relay far from both machines adds its distance to every round trip, while Tailscale's direct path adds none. When two devices can't connect directly, Tailscale relays their traffic through its DERP servers, "a fallback option when a direct connection isn't possible" (DERP servers, read October 31, 2026).

This is where the two differ most. A tailnet is for devices that run the client, so it offers two other routes. Tailscale Funnel publishes a service to the internet for people who don't use Tailscale, on ports 443, 8443 or 10000; its documentation calls it beta, with bandwidth limits, and names no login for visitors, so the app has to bring its own (Funnel, read October 31, 2026). Sharing a device with another tailnet asks the recipient to accept from a Tailscale account, as an admin of their own tailnet (sharing, same date). We tested neither route here; both are described from Tailscale's documentation.

With nfltr, the friend gets an unlisted HTTPS link and a password. In the test, the link with the password reached the web app, the link without it or with a wrong one got 401, and a guessed link name was refused. TLS for the link ends at nfltr up on the box, so the relay forwards bytes it cannot read. A link ends after a day without a visit; the next start of nfltr up prints a new one.

Case 3: one port, not the whole machine

A device on a tailnet is reachable on every port unless a policy says otherwise. In the test, with no policy, the laptop reached both the database and the SSH port on the box. One rule in the tailnet's policy file fixes that:

{ "acls": [ { "action": "accept", "src": ["me@"], "dst": ["home@:5432"] } ] }

With it loaded, the database still answered and the SSH port did not. So Tailscale does per-port access well; it is a policy you write and keep for the whole tailnet.

nfltr starts from the other end: only what the file publishes exists. nfltr dial home/db with the account key and pin opened a session; with a wrong key it exited 2, with a wrong pin 7, and nfltr dial home/ssh exited 3, because the box publishes no such service. Nothing on the box listened beyond loopback.

Case 4: both at once

The box ran the Tailscale client and nfltr up together. The laptop reached the database over the tailnet and through nfltr, and the friend's link answered. Then we stopped Tailscale on the box, and the link still answered. The two don't touch each other: Tailscale handles your devices, and nfltr hands one service to someone outside them.

The setup, counted

Tailscale nfltr
lines of configuration 16 with Tailscale's control server (42 with the open-source one we ran) 24 with the hosted relay (28 with the private one we ran)
on each device the client, signed in nfltr up and the account key
for one port only a policy rule nothing: publish only that port
for someone with nothing installed Funnel (public), or a share to their tailnet a link and a password
on the box's network, outside loopback the client's UDP sockets and one TCP listener of its own; nothing forwarded to them nothing

What it costs

nfltr is free until further notice. To show what relaying this kind of traffic costs whoever runs the relay, we priced it with our cost ledger at public cloud list prices: three agents connected all month, 2 GiB a month through the database mapping and 1,000 visits to the friend's link come to 0.27 USD a month at Google Cloud's list prices and 0.0029 EUR at Hetzner's.

Tailscale's list prices, read on October 31, 2026 from its pricing page: Personal costs nothing, for up to 6 users, and the page says it is "only suitable for non-commercial use of Tailscale"; Standard is 8 USD and Premium 18 USD per user a month. Headscale is free software and needs a server that every device can reach, for example a 4 USD a month VM.

Evidence: Tailscale and nfltr on one home machine

Setup
Every machine a Linux container on one Mac (Apple Silicon) with Docker, two runs on October 31, 2026; the numbers are from the second run, and the first agreed within 1.5 ms on every percentile. A home network behind a router with 10 ms of added delay each way (tc netem) and no port forwarded; a laptop, a friend, the control server and the relay on an outside network with no internet. Tailscale client 1.104.4 (the official container image, userspace networking mode) with Headscale 0.29.7 as the control server and its built-in relay; nfltr from our main branch with a private relay. The link's certificate was self-signed where the hosted relay gets one from Let's Encrypt.
Numbers
Echo p50 23.3 ms through both (p95 25.3 Tailscale, 25.0 nfltr); new connection p50 47.1 ms Tailscale (direct), 72.7 ms nfltr; ports answering on the home's public address 0 and 0; router inbound rules 0 and 0; with no tailnet policy the SSH port reached, with one rule refused; nfltr dial exit codes 0 (right key and pin), 2 (wrong key), 7 (wrong pin), 3 (unpublished service); the link 200 with the password, 401 without or with a wrong one, a guessed name refused; the link answering with Tailscale stopped; 0.27 USD a month at Google Cloud list prices, 0.0029 EUR at Hetzner's, for the assumed month above.
Reproduce
(a) With the released binaries: run the two files above with nfltr up (v2.1.49 or later; the pin is in the box's output), connect to 127.0.0.1:5432 on the laptop, and open the link the box prints in any browser. (b) Our check, in the closed nfltr repository, runs all three setups in containers and judges each claim by an exit code.
Limits
One host, so the only distance is the delay we added and there is no loss; the relay sat next to the laptop, and both peers could reach each other directly. Userspace mode adds a SOCKS5 hop on the laptop that the desktop client doesn't have. Funnel and device sharing were not tested. Two runs are no distribution. The cost covers relaying only, at list prices, for an assumed month; prices change, and each one names the date it was read.

Is it for you?

Fits if you need to give one service to someone, not a device to a network: a database to your own laptop, a web app to a friend or a client who installs nothing, a service on a machine you can't or won't join to a VPN. If your devices are already on a tailnet, nfltr fits beside it for the people outside it, as case 4 shows.

Not ready for what Tailscale is built for. nfltr is no mesh VPN: no address per device, no names for every device (MagicDNS "automatically registers DNS names for devices in your network", MagicDNS), and no direct path between devices, since every session crosses the relay. Many devices that all reach each other, one policy for all of them, or an admin who routes a whole subnet to the tailnet, so that "devices connect to your tailnet without installing the Tailscale client" (subnet routers), are better served by Tailscale (both pages read October 31, 2026). For a whole LAN, nfltr has nfltr wg serve, which reaches only the destinations one machine on that LAN allows.

Try next: install nfltr and sign in on both machines, then run nfltr up with the files above. The nfltr up reference covers services:, share links and connect:; Reach a private LAN covers nfltr wg. Part 5 of this series measures the same mapping for a model server.

← All posts