Two Tailscale commands differ by one word. tailscale serve shares a local service with the devices in your own tailnet. tailscale funnel routes traffic from the broader internet to that same service, reachable by anyone who has the URL.
Same syntax, opposite exposure. That is the whole article in two sentences, but the consequence of getting it backwards is a service you believed was private sitting on the open internet, so it is worth being precise about what Funnel does and does not do.
At a glance (details, commands and the own-domain setup are below):
tailscale serve | tailscale funnel | VPS + Caddy (own domain) | |
|---|---|---|---|
| Who can reach it | Devices in your tailnet only | Anyone with the URL | Anyone, behind your login |
| Address | machine.tailnet-name.ts.net | Same ts.net URL, now public | A domain you own |
| Public ports | None, nothing is public | 443, 8443 or 10000 only | Any port you open |
| Login in front | Tailnet membership | None | Yours (Caddy basic_auth) |
| Bandwidth | No Funnel cap | Capped by Tailscale, not configurable | The VPS port speed |
| Extra cost | None | None, every plan | A few euros a month |
Build your Serve or Funnel command
Answer three questions, get the command, the URL it gives and the cleanup. Runs in your browser, nothing is sent.
Your command
tailscale funnel 3000
Address it will answer on
https://<machine>.<tailnet-name>.ts.net
Check and undo
tailscale funnel status tailscale funnel reset
- Funnel puts no login in front of the service: anyone with the URL reaches your app. Only publish something that has its own authentication.
- Your tailnet policy file must grant the funnel node attribute (default: autogroup:member), and MagicDNS plus HTTPS certificates must be enabled in the admin console.
- Without --bg the share stops when you close the terminal: the safe choice for a quick test.
If you need your own domain, any port and no Funnel bandwidth cap, the right-hand column is the fix: a small VPS with a public IPv4 (Contabo, 5.50 € a month) plus Caddy, set up step by step further down.
What Funnel actually is
Tailscale's own description is direct: Funnel "lets you route traffic from the broader internet to a local service running on a device in your Tailscale network." The point is to share with people who are not on your tailnet and have no Tailscale account.
Serve is the inverse case, and the documentation says so plainly: if you want to share local services only with other devices in your tailnet, use Serve instead.
So the decision is not a technical preference, it is a question about your audience:
- Only your own machines and people you have invited to the tailnet → Serve.
- Anyone with the link, including strangers → Funnel.
If you cannot answer that question about a given service, do not run either command yet.
The three constraints that decide whether Funnel fits at all
Funnel is deliberately narrow, and its limits are documented rather than emergent. Three of them will settle most use cases before you write any config.
Ports: 443, 8443, 10000. That is the entire list. Funnel can only listen on those three. This is not a default to be overridden, if your service must be publicly reachable on some other port, Funnel is the wrong tool.
The hostname belongs to Tailscale. Funnel can only use DNS names in your tailnet's domain, of the form tailnet-name.ts.net. If you were planning to put a product, a client demo or anything brand-facing on a domain you own, that is not what this does.
Bandwidth is capped, and the cap is not yours to set. Tailscale states that traffic sent over a Funnel is subject to non-configurable bandwidth limits. For a config page or a small internal tool, irrelevant. For streaming a media library to relatives, the cap is likely to be the constraint rather than your uplink.

The part people skip: there is no login in front of it
This is the reason the Serve/Funnel confusion matters more than a typo normally would.
Tailscale's documentation describes no authentication layer standing in front of a Funnel endpoint, and that is by design, because the entire purpose is to serve people who are not on your tailnet and therefore cannot be authenticated by it. Funnel gives you a public HTTPS entry point; it does not give you a gate.
The practical consequence: whatever login your application has is the only thing between the internet and your data. A dashboard with no password, a file browser that trusts the local network, an admin panel that was only ever reachable from your LAN, all of these become open the moment they are funnelled rather than served.
Before enabling it on anything, the honest test is one question: would I be comfortable posting this URL publicly? If the answer is no, the service needs its own authentication first, or it needs Serve rather than Funnel.
Enabling it: the policy file comes first
Funnel is not on by default per node. It requires a funnel node attribute in your tailnet policy file, which is what tells Tailscale which users in your tailnet are allowed to use it. The default grants that to autogroup:member.
That indirection is a feature worth understanding rather than clicking past: it means Funnel is an administrative decision at the tailnet level, not just a command someone runs on a laptop. In a shared tailnet, whoever controls the policy file controls whether anyone can expose services publicly at all.
★ Nuremberg GDPR datacenter · ✓ Dedicated IPv4 included · 200 Mbit/s port
A VPS with its own public IP, no tunnel needed → ContaboPublic IPv4 · You control the firewall, the ports and the reverse proxy · No third-party bandwidth cap→The commands, side by side
Both commands take the same target: a port, a localhost:port address or a full URL of a service running on the machine. These are the forms listed by tailscale funnel --help on Tailscale 1.104.1 (18 October 2026). Nothing changes in 1.104.2, the stable release of 21 October 2026: its changelog only lists the Go 1.27.3 build that distrusts the compromised eMudhra TLS intermediate certificates (Tailscale security bulletin TS-2026-013) and two Windows client fixes, so update the client, the commands below stay the same:
# Inside the tailnet only: https://<machine>.<tailnet-name>.ts.net
tailscale serve --bg 3000
# On the public internet, same URL, reachable by anyone
tailscale funnel --bg 3000
# Public, on one of the two other allowed ports
tailscale funnel --bg --https=8443 3000
# See what is currently exposed, then remove it
tailscale funnel status
tailscale funnel reset
Without --bg the share runs in the foreground and stops when you close the terminal, which is the safer default for a quick test. With --bg it survives logouts and reboots until you run reset, so make tailscale funnel status part of your routine on any machine where you have used it. Before the first run, MagicDNS and HTTPS certificates must be enabled for the tailnet in the admin console, because Funnel serves a real certificate for the ts.net name.
Tailscale Funnel with your own domain: the VPS reverse-proxy pattern
Funnel will not answer on app.example.com, and a CNAME pointing at your ts.net name does not fix it: the certificate is issued for the ts.net hostname, so browsers reject the custom name. The pattern that gives you a domain you own, any port, no Funnel bandwidth cap and a login you control is a small public server that joins your tailnet and forwards traffic to the machine at home over it.
- Rent a small VPS with a public IPv4 and install Tailscale on it (
curl -fsSL https://tailscale.com/install.sh | sh, thensudo tailscale up). It now reaches your home machine on its100.x.y.ztailnet address. - Point an A record for
app.example.comat the VPS public IP and open ports 80 and 443 on its firewall, nothing else. - Install Caddy on the VPS and give it this Caddyfile (validated with
caddy validateon Caddy 2.11.7):
app.example.com {
basic_auth {
# generate the hash with: caddy hash-password
alice $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG
}
reverse_proxy 100.101.102.103:3000
}
Caddy obtains the Let's Encrypt certificate for your domain by itself, and the basic_auth block puts the gate in front that Funnel never had. Replace 100.101.102.103 with the tailnet IP shown by tailscale ip -4 on the home machine, and make sure the service there listens on that interface, not only on 127.0.0.1. The home network still opens no port: the VPS is the only public face, and the traffic between the two travels inside WireGuard.
The honest cost: a few euros a month and one more machine to keep patched. A 1 vCPU, 1 GB box is plenty for a reverse proxy; the limit becomes the VPS port speed rather than a cap set by someone else. If you would rather not run a public server at all, a tunnel product built around custom domains is the alternative, compared below.
★ Nuremberg GDPR datacenter · ✓ Dedicated IPv4 included · 200 Mbit/s port
The public front for your own domain → Contabo VPSPublic IPv4 · Unlimited fair-use traffic · Joins your tailnet in two commands→If you would rather have a US host with a 30-day money-back guarantee, Ultahost VPS Basic is 5.52 $ a month on a 24-month term (1 GB RAM, dedicated IPv4, read on 21 October 2026), enough for the Caddy reverse proxy described above.
When Funnel is the right answer, and when it is not
Funnel fits when you want to hand someone outside your tailnet a working HTTPS URL with minimum ceremony: a webhook endpoint a third-party service must reach, a preview of something you are building, a temporary share. Certificates and the public entry point are handled for you, and it works on every plan.
Funnel does not fit when you need a domain you own, a port outside the three allowed, sustained throughput, or an authentication layer you control. Those requirements point elsewhere, a reverse proxy on a host with its own public IP, or a tunnel product built around custom domains, is the better shape. That comparison has its own trade-off worth reading first: Pangolin vs Cloudflare Tunnel turns on who owns the machine your traffic exits from.
And for the case people most often mean when they reach for Funnel, I want my own devices to reach this service from anywhere, neither Funnel nor a public tunnel is the answer. That is what the tailnet already does, and what an exit node or a plain self-hosted WireGuard server does without exposing anything publicly at all.
Keep the commands at hand: our free Tailscale & Headscale Cheat Sheet (4-page PDF, direct download, no sign-up) gathers status and ping, exit node, subnet router, Serve and Funnel, the Headscale config, users, keys and ACLs, and the usual failure causes. Download the cheat sheet.
The bottom line
serve and funnel are one word apart in the terminal and opposite in what they publish. Serve keeps a service inside the tailnet; Funnel puts it on the internet, on one of three ports, under a ts.net hostname, with a bandwidth cap and nothing authenticating visitors.
Decide which audience you actually mean before typing either. If the answer is "my own machines", you never needed a public endpoint, and the safest exposure is the one you never opened.
Affiliate link
★ Nuremberg GDPR datacenter · ✓ Dedicated IPv4 included · 200 Mbit/s port
Self-host your VPN on your own VPS → ContaboCloud VPS 4 · 5,50 €/mo · Card or PayPal · orders from outside the EU accepted→Price checked 17 Oct 2026, 24-month term


