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.
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+ Mbps guaranteed
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→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.
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.
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.
★ Nuremberg GDPR datacenter · ✓ Dedicated IPv4 included · 200+ Mbps guaranteed
Self-host your VPN on your own VPS → ContaboFull root access · public IPv4 · pick your region→
