Tailscale Serve vs Funnel Explained
Tailscale Serve keeps a service on your tailnet; Funnel puts it on the public internet. Real commands, TLS setup, and the gotchas that cause 404s.

Tailscale Serve vs Funnel Explained
Jellyfin is finally running on the homelab, and a family member who doesn't own a laptop with Tailscale on it wants to watch something tonight. tailscale serve won't get them in. tailscale funnel might — but which one, and what else breaks if you pick wrong?
TL;DR
- Serve shares a local service with your tailnet only — nothing leaves your private network.
- Funnel extends that to the public internet, for one service you deliberately choose.
- Both need MagicDNS and HTTPS certificates enabled first, or the URL 404s even though the command "succeeded."
- Funnel only speaks on ports 443, 8443, or 10000 — nothing else.
- Scope Funnel to a single path, not the whole host, if the service exposes more than one thing.
Serve vs Funnel in one sentence
Both commands are part of the same tailscale serve subsystem — Funnel is a flag on top of Serve, not a separate tool. Tailscale's own announcement frames Serve as "share within your tailnet" and Funnel as "share with the world."
| Serve | Funnel | |
|---|---|---|
| Audience | Devices on your tailnet only | Anyone with the URL, on the public internet |
| Auth required | Tailscale identity (WireGuard) | None — it's a public HTTPS endpoint |
| TLS | Automatic tailnet cert | Automatic public cert |
| Ports | Any local port you choose | 443, 8443, or 10000 only |
| Typical use | Admin dashboards, Immich, Grafana | One service for someone outside your tailnet |
| Risk if misused | Low — still gated by tailnet membership | Exposes exactly what you serve, to anyone |
Prerequisites both features share
Neither command works stand-alone. Per Tailscale's HTTPS setup docs and the Tailscale Funnel feature docs, you need, in order:
- MagicDNS enabled for the tailnet (Admin console → DNS).
- HTTPS Certificates enabled (Admin console → DNS → HTTPS Certificates), which publishes your tailnet's DNS name to a public ledger — acknowledge that prompt.
- A
funnelnode attribute in the tailnet policy file — the CLI adds this for you automatically the first time you runtailscale funnel, but if your policy file is locked down or managed separately, add it by hand:"nodeAttrs": [{"target": ["autogroup:member"], "attr": ["funnel"]}]. Serve doesn't need this attribute — it's Funnel-only.
Warning
Skip step 2 and tailscale funnel will report success in the terminal, then the URL 404s or TLS-errors in the browser. This exact sequence — command exits clean, URL doesn't work — is the single most common Funnel complaint on the Unraid forums. Enable HTTPS certificates before touching either command.
Tailscale Serve: keep it on the tailnet
Serve maps a local port to a path on your tailnet's HTTPS endpoint. For Immich running on port 2283:
tailscale serve https / http://localhost:2283
That's it — https://<your-device>.<tailnet-name>.ts.net/ now reaches Immich, but only from devices logged into your tailnet. No open port on your router, no reverse proxy, no cert to renew by hand.
Use Serve for anything you don't want outside your household: admin panels, Grafana, Proxmox's own web UI, a staging build. The access control is "is this device on my tailnet," which for most homelabs is already tighter than a password.
Tailscale Funnel: hand out a public link
Funnel takes the same Serve mapping and additionally opens it to the internet. Two commands, run once:
tailscale funnel 443 on
tailscale serve https / http://localhost:8096
The tailscale serve CLI reference and tailscale funnel CLI reference confirm the pattern: serve defines what gets shown, funnel flips the switch on whether it's public. Running tailscale funnel status afterward lists every currently-funneled port, which is worth checking periodically so you know exactly what's public.
That maps Jellyfin (port 8096) to a public HTTPS URL. Send the link to whoever needs it — they don't install Tailscale, don't need an account, and don't see anything else on the box.
Note
Funnel traffic reaches your app as proxied traffic, not as a tailnet peer — app-level logs won't show the visitor's real IP the way tailnet traffic does. That's expected, not a bug.
Path-scoped Funnel: don't expose the whole host
If a service exposes more than one route — a file server, a reverse-proxied set of apps on one machine — funneling the host root makes everything on that port public, not just the one thing you meant to share. Scope it to a single path instead:
tailscale serve https:443/shared/ http://localhost:8080/shared/
tailscale funnel 443 on
Only /shared/ becomes reachable from the public internet; every other path on that same local port stays tailnet-only, because Serve's path mapping is what Funnel exposes — it never exposes the raw port.
Making it stick — and undoing it
Run either command with --bg to survive a reboot or tailscale down && tailscale up:
tailscale funnel --bg 443 on
Without --bg, the mapping only lasts for the terminal session. Per the tailscale funnel CLI reference, the config is written to disk and tailscaled restores it on startup — so --bg is what you want for anything long-lived, like Jellyfin.
To undo everything and start clean:
tailscale funnel reset
tailscale serve reset
Or append off to a specific mapping to remove just that one:
tailscale funnel 443 off
Checking what's actually exposed right now
It's easy to funnel something for a one-off and forget it's still public weeks later. tailscale funnel status lists every active funnel on the current node; tailscale serve status shows the full set of Serve mappings, public or not. Run both after any homelab change — a new container, a reverse proxy swap, a docker compose up that quietly reused an old port — and confirm the list matches what you intend to be reachable from outside your tailnet. Treat an unexpected entry the same as an unexpected open port on your router: investigate before you assume it's harmless.
Decision rule: which one should you actually use
Default to Serve. It costs nothing in exposure and covers the vast majority of homelab sharing — anything you'd otherwise reach by opening a port or standing up a reverse proxy. Reach for Funnel only for the one specific service a person outside your tailnet genuinely needs, and scope it to a path rather than a whole host. If you're tempted to Funnel something "just in case someone needs it later," that's a sign it should stay on Serve until an actual outside visitor exists.
For the networking layer underneath both features, see our guide on installing Tailscale on a homelab and, if you want outbound traffic routed through home instead of split-tunneled, how Tailscale exit nodes work. The complete mesh VPN guide covers where Serve and Funnel sit in the wider Tailscale feature set.
FAQ
Is Tailscale Funnel free?
Yes — Funnel is available on Tailscale's free personal plan, same as Serve. Both are part of the core product, not a paid add-on.
Does Tailscale Funnel expose my whole device to the internet?
No. Funnel only exposes the specific port and path you map with tailscale serve — everything else on the device stays reachable only from your tailnet, exactly as before.
Why does my Tailscale Funnel URL return a 404?
Almost always because MagicDNS or HTTPS Certificates aren't enabled yet in the tailnet admin console. Enable both before running tailscale funnel, per Tailscale's HTTPS setup docs.
Can I use Tailscale Funnel without MagicDNS?
No — Funnel depends on the tailnet DNS name that MagicDNS assigns, and on the HTTPS certificate tied to that name. Both must be enabled first.
More from Self-Hosting & Privacy

Stoat, Spacebar, and Matrix/Element compared for self-hosting a Discord replacement: RAM needs, voice/video maturity, Discord bot compatibility, and the real migration friction for an existing community.

You edit .env, restart the container, and the old value is still there - no error anywhere. The exact precedence order between env_file, environment, and your shell, reproduced on a real stack, plus the fix when Compose won't pick up the change.

Every password manager says it: "zero-knowledge." Here is what that actually means technically, how client-side key derivation works, and a 5-point checklist to verify any vendor is claim for yourself.
Stay in the loop
Get the latest articles delivered to your inbox. No spam, unsubscribe anytime.
Riester Rente Steuererklärung: Anlage AV
How to file Anlage AV correctly for a Riester Rente: what triggers the Günstigerprüfung, the 2025 change that moved child allowances to Anlage Kind, and what I actually entered on my own form.
Continue Reading