VPNSmith
self-host-vpn-headINFO

Tailscale Funnel: the one-word difference that puts your app on the public internet

Tailscale Serve shares a local service inside your tailnet. Tailscale Funnel puts it on the open internet, reachable by anyone. Same syntax, one word apart, opposite exposure. What Funnel actually does, its three allowed ports, what has to be enabled first, and when Cloudflare Tunnel fits better.

By Eric Gerard · Founder · VPNSmith - Self-host VPN & GDPR VPS specialist5 min readPhoto via Pexels

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.

Close-up of a developer's hands typing on a keyboard in front of a screen full of code
Close-up of a developer's hands typing on a keyboard in front of a screen full of code

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

Frequently asked questions

What is the difference between Tailscale Funnel and Tailscale Serve?
Serve shares a local service with the other devices in your tailnet only. Funnel routes traffic from the broader internet to that service, so anyone can reach it - including people with no Tailscale account. The documentation is explicit about the split: if you want to share only with your own devices, use Serve instead. The commands look almost identical, which is exactly why the mistake happens.
Which ports can Tailscale Funnel use?
Only three: 443, 8443 and 10000. That is a hard limit, not a default you can override. If your service must be reachable on another port publicly, Funnel is the wrong tool and you need a reverse proxy or a different tunnel.
Does Tailscale Funnel put authentication in front of my service?
No. Tailscale's documentation describes no built-in authentication layer for a Funnel endpoint - the point of Funnel is precisely to serve people who are not on your tailnet. Whatever login your application has is the only thing standing between the internet and it. If the app has no login, it is open.
Do I need a paid plan for Funnel?
No. Tailscale states that Funnel is available on all plans. What you do need is the funnel node attribute in your tailnet policy file, which controls which users are allowed to enable it - by default that is autogroup:member.
What hostname does a Funnel endpoint use?
One inside your tailnet's own domain, in the form tailnet-name.ts.net. Funnel cannot serve a custom domain of your own directly, which matters if you were planning to put a product on it.
Is Funnel fast enough to stream media?
Traffic over Funnel is subject to bandwidth limits that Tailscale describes as non-configurable. It is designed for sharing a service, not for sustained high-throughput delivery. For a media library shared with people outside your tailnet, expect the limit to be the constraint rather than your uplink.