Tailscale Funnel: Expose Homelab Services Publicly Without Port Forwarding
You want to share a service from your homelab with someone who isn't on your network. Maybe it's a demo app you're building, a Grafana dashboard for a client, or a Jellyfin instance for your family. The traditional path is port forwarding, dynamic DNS, TLS certificates, and hoping your ISP hasn't put you behind CGNAT. If they have, you're stuck — no amount of router configuration will make inbound connections work. Even if port forwarding does work, you've just exposed your home IP to the world and opened a hole in your firewall. There has to be a better way.
Photo by GuerrillaBuzz on Unsplash
Tailscale Funnel is that better way. It's a feature built into Tailscale that lets you expose local services to the public internet through Tailscale's relay infrastructure. Anyone on the internet can reach your service at a https://machine-name.tailnet-name.ts.net URL — with automatic HTTPS, no domain name required, and no inbound ports. Your home IP stays hidden. It works through CGNAT, double NAT, and corporate firewalls. If your machine can reach the internet at all, Funnel works.

Why Funnel Instead of Port Forwarding
Port forwarding has been the default for decades, but it comes with real problems for homelabbers:
- Exposes your home IP: Anyone who visits your service can see your residential IP address. Combined with GeoIP databases, that narrows down your physical location significantly.
- Requires a static IP or DDNS: Residential IPs change. You need dynamic DNS and need to keep it updated.
- Blocked by CGNAT: Many ISPs (especially mobile and newer fiber providers) use carrier-grade NAT. You literally cannot port forward because you don't have a public IP.
- Firewall management: Every forwarded port is an attack surface. You need to keep track of what's open and ensure the services behind those ports are hardened.
- No automatic HTTPS: You need to set up Let's Encrypt, manage certificate renewal, and configure your reverse proxy.
Tailscale Funnel eliminates all of these. No router configuration, no DNS setup, no certificate management. You run one command and your service is live on the public internet with HTTPS.
How Funnel Works
When you enable Funnel for a service, Tailscale's DERP (Designated Encrypted Relay for Packets) relay servers accept incoming HTTPS connections from the public internet on your behalf. The request arrives at Tailscale's infrastructure, gets forwarded through the encrypted Tailscale tunnel to your machine, and your machine proxies it to the local service.
The flow looks like this:
Public user → https://mybox.tail1234.ts.net → Tailscale DERP relay → WireGuard tunnel → Your machine → localhost:8080
Key points:
- Your home IP is never exposed. DNS for
*.ts.netpoints to Tailscale's servers, not your home. - HTTPS is automatic. Tailscale provisions Let's Encrypt certificates for your
ts.netsubdomain. No configuration needed. - Traffic is encrypted end-to-end between Tailscale's relay and your machine via WireGuard.
- No inbound ports are required. The connection from your machine to the relay is outbound, so it works through any NAT or firewall.
Prerequisites
Before setting up Funnel, you need:
- Tailscale installed on the machine running the service you want to expose
- A Tailscale account (the free plan supports Funnel)
- HTTPS enabled on your tailnet (enabled by default on newer tailnets)
- A local service listening on a port (e.g., a web app on port 3000)
Funnel only works with HTTPS on port 443 from the public internet's perspective. Your local service can run on any port — Tailscale handles the mapping.
Like what you're reading? Subscribe to HomeLab Starter — free weekly guides in your inbox.
Setting Up Funnel
Step 1: Install Tailscale
If you don't already have Tailscale installed:
# Linux (one-line install)
curl -fsSL https://tailscale.com/install.sh | sh
# Start and authenticate
sudo tailscale up
Follow the authentication URL to add the device to your tailnet.
Step 2: Enable Funnel in the Admin Console
Funnel requires a policy change in your tailnet's ACL configuration. Go to the Tailscale admin console at login.tailscale.com, navigate to Access Controls, and add the Funnel policy:
{
"nodeAttrs": [
{
"target": ["autogroup:member"],
"attr": ["funnel"]
}
]
}
This allows all members of your tailnet to use Funnel. You can restrict it to specific devices by using device tags instead of autogroup:member.
Step 3: Enable HTTPS Certificates
In the admin console, go to DNS and ensure HTTPS Certificates is enabled. This lets Tailscale provision Let's Encrypt certificates for your devices.
Step 4: Start Funnel
With a service running on port 8080, expose it publicly:
# Expose local port 8080 to the public internet
sudo tailscale funnel 8080
That's it. Tailscale outputs the public URL:
Available on the internet:
https://mybox.tail1234.ts.net/
|-- proxy http://127.0.0.1:8080
Funnel started and running in the background.
Press Ctrl+C to stop.
Anyone on the internet can now visit https://mybox.tail1234.ts.net and reach your service.
Step 5: Configure Multiple Services with serve
For more complex setups, use tailscale serve to map paths and ports, then enable Funnel on top:
# Serve a local app on the Tailscale HTTPS endpoint
sudo tailscale serve --bg https+insecure://localhost:3000
# Serve static files from a directory
sudo tailscale serve --bg /docs /var/www/docs
# Serve a specific path to a different backend
sudo tailscale serve --bg /grafana http://localhost:3000
# Now enable Funnel to make it public
sudo tailscale funnel --bg 443
Check what's currently configured:
sudo tailscale serve status
Step 6: Run as a Persistent Background Service
By default, tailscale funnel runs in the foreground. Use the --bg flag to run it in the background, persisting across reboots:
# Start Funnel in the background (persists across reboots)
sudo tailscale funnel --bg 8080
To stop it:
sudo tailscale funnel --bg off
HTTPS and TLS Handling
One of Funnel's best features is zero-effort HTTPS. Here's how it works:
- Tailscale provisions a Let's Encrypt certificate for
machine-name.tailnet-name.ts.netautomatically. - The certificate is managed and renewed by Tailscale — you never touch it.
- The public-facing connection is HTTPS on port 443. No other ports are available for Funnel.
- The connection between the DERP relay and your machine is encrypted via WireGuard.
- The local connection (from Tailscale to your service on localhost) is unencrypted HTTP, which is fine because it never leaves your machine.
If your local service already uses HTTPS (self-signed or otherwise), use https+insecure:// in the serve config to connect to it without certificate verification:
sudo tailscale serve --bg https+insecure://localhost:8443
Limitations You Should Know
Funnel is powerful but has real constraints:
HTTPS on port 443 only. The public internet can only reach your Funnel on port 443. No custom ports, no TCP/UDP pass-through, no SSH access. This is web traffic only.
Bandwidth goes through Tailscale's relays. Unlike Tailscale's peer-to-peer connections, Funnel traffic always flows through DERP relays. This means your throughput is limited by the relay capacity. For a personal blog or dashboard, this is fine. For streaming 4K video to dozens of users, it's not.
You don't control the domain name. Your public URL is machine-name.tailnet-name.ts.net. You can't use a custom domain. If you need app.yourdomain.com, you need Cloudflare Tunnel or a similar reverse proxy.
Single node per URL. Each Funnel URL maps to one machine. There's no built-in load balancing or failover across multiple backend servers.
No authentication layer. Unlike Cloudflare Tunnel with Cloudflare Access, Funnel doesn't provide an authentication layer in front of your service. Your application needs to handle its own authentication. Anyone on the internet can reach the endpoint.
Rate limiting. Tailscale may apply rate limits to Funnel traffic. This isn't well-documented, but don't expect to run a high-traffic production site through it.
Comparison: Funnel vs Cloudflare Tunnel vs Port Forwarding vs ngrok
| Feature | Tailscale Funnel | Cloudflare Tunnel | Port Forwarding | ngrok |
|---|---|---|---|---|
| Custom domain | No (*.ts.net only) | Yes (your domain) | Yes (with DDNS) | Paid plans only |
| Domain/account required | Tailscale account | Cloudflare account + domain | Router access | ngrok account |
| Setup time | 1 minute | 15-30 minutes | 30-60 minutes | 5 minutes |
| HTTPS | Automatic | Automatic | Manual (Let's Encrypt) | Automatic |
| Home IP hidden | Yes | Yes | No | Yes |
| Works through CGNAT | Yes | Yes | No | Yes |
| DDoS protection | No | Yes | No | No |
| Auth layer included | No | Yes (Cloudflare Access) | No | Yes (paid) |
| Bandwidth | Relayed (limited) | Cloudflare edge | Your upload speed | Relayed (limited) |
| Custom ports | No (443 only) | Yes | Yes | Yes |
| Non-HTTP traffic | No | Limited (TCP with WARP) | Yes | Yes (TCP tunnels) |
| WebSocket support | Yes | Yes | Yes | Yes |
| Cost | Free | Free | Free | Free (limited) / $10+/mo |
| Persistence | Built-in (--bg) |
systemd service | Router config | Paid plans |
When to Choose Tailscale Funnel
- Quick demos and sharing: You need to show someone a local app in 60 seconds, no setup
- Already using Tailscale: If Tailscale is your remote access solution, Funnel is one command away
- No domain name: You don't own a domain and don't want to buy one
- Behind CGNAT: Your ISP doesn't give you a public IP
- Temporary access: Share a service for a meeting or a few days, then turn it off
When to Choose Cloudflare Tunnel Instead
- Custom domain needed: You want
app.yourdomain.com, not ats.netsubdomain - Authentication required: You need Cloudflare Access to gate who can reach your service
- DDoS protection: You're worried about attacks on your public endpoints
- Multiple services on subdomains: You want to expose 10+ services on clean subdomain URLs
- Production use: Your public service needs to handle real traffic reliably
When to Stick with Port Forwarding
- Non-HTTP services: Game servers, SSH, custom TCP/UDP protocols
- Maximum performance: No relay overhead, direct connection to your server
- No third-party dependency: You don't want to depend on Tailscale or Cloudflare being up
Security Considerations
Funnel puts your service on the public internet. Treat it accordingly:
Your application must handle authentication. Funnel provides no auth layer. If your service has a login page, great. If it doesn't, the entire internet can access whatever is behind that port.
Keep services updated. A publicly exposed Nextcloud or Grafana instance is a target. Apply security patches promptly.
Minimize your attack surface. Only expose services that are designed for public access. Don't funnel your Proxmox web UI or database admin panel.
Use application-level rate limiting. Since Funnel doesn't provide rate limiting at the network level, your application should handle brute-force protection, API rate limits, and abuse prevention.
Monitor access logs. Check your application's access logs for unusual traffic patterns. You're on the public internet now — scanners and bots will find you.
# Good candidates for Funnel
- A personal blog or portfolio site
- A demo app for a client
- A file sharing service with authentication (e.g., FileBrowser)
- A webhook receiver for CI/CD
# Bad candidates for Funnel
- Proxmox or hypervisor management UI
- Database admin tools (phpMyAdmin, pgAdmin)
- Anything without authentication
- High-bandwidth media streaming
Real-World Use Cases
Sharing a Dev Server with a Client
You're building a web app locally and want to show a client the progress:
# Your app runs on port 3000
npm run dev -- --port 3000
# Expose it publicly
sudo tailscale funnel 3000
Send the client the ts.net URL. They see your app in real time. When the meeting is over, Ctrl+C.
Webhook Receiver for GitHub or Stripe
Many APIs require a public HTTPS endpoint for webhooks. Instead of deploying to a server:
# Your webhook handler listens on port 4000
sudo tailscale funnel 4000
Set the webhook URL to https://mybox.tail1234.ts.net/webhooks in GitHub or Stripe. Webhooks hit your local machine directly.
Temporary File Sharing
Need to share files with someone quickly?
# Serve a directory of files
sudo tailscale serve --bg /share /home/user/shared-files
# Make it public
sudo tailscale funnel --bg 443
Send them the URL. Turn off Funnel when they've downloaded what they need.
Home Assistant Dashboard for Family
Give family members access to a Home Assistant dashboard without VPN setup or Tailscale on their devices:
sudo tailscale funnel 8123
They visit the URL in their browser and log in to Home Assistant. No app installation, no VPN configuration, no port forwarding on the router.
Funnel + Tailscale Serve: The Full Picture
Funnel and Serve are related but distinct:
tailscale servemakes a local service available over HTTPS to devices on your tailnet (private access).tailscale funnelextends that to the public internet (anyone can access it).
You can use serve without Funnel for private HTTPS access to your services across your tailnet. Adding Funnel on top makes the same endpoint public. Think of it as a visibility toggle: serve = private HTTPS, funnel = public HTTPS.
# Private only (tailnet members can access via HTTPS)
sudo tailscale serve --bg 8080
# Public (anyone on the internet can access)
sudo tailscale funnel --bg 8080
The Bottom Line
Tailscale Funnel occupies a specific niche: the fastest way to put a homelab service on the public internet with zero infrastructure setup. No domain, no DNS, no certificates, no port forwarding, no firewall rules. One command, and you're live.
It's not a replacement for Cloudflare Tunnel if you need custom domains, DDoS protection, or an authentication layer. It's not a replacement for port forwarding if you need raw TCP/UDP access or maximum bandwidth. But for quick sharing, webhook receivers, demos, and "I just need a public URL for this thing" scenarios, nothing else comes close to Funnel's simplicity.
If you're already running Tailscale for remote access to your homelab — and you probably should be — Funnel is sitting right there, one command away, waiting for the next time someone asks "can you send me a link to that?"
