Overview
Tailscale provides two complementary features for exposing services: Serve and Funnel. Understanding the difference is crucial for properly securing your self-hosted applications.Tailscale Serve
Private access within your Tailscale network (Tailnet). Only authenticated devices can connect.
Tailscale Funnel
Public internet access. Anyone with the URL can connect, even without Tailscale.
Tailscale Serve
Tailscale Serve lets you route traffic from devices on your Tailscale network to local services. Think of it as private sharing - only members of your Tailnet can access the service.How Serve Works
Serve Configuration Example
This is the default configuration for all ScaleTail services:compose.yaml
The key setting is
"AllowFunnel": {"${TS_CERT_DOMAIN}:443": false}. This explicitly disables public internet access.Serve Configuration Breakdown
- TCP Configuration
- Web Handler
- Funnel Control
- Tailscale automatically provisions TLS certificates
- Uses your machine’s Tailscale hostname
- Certificates are valid only for your Tailnet
When to Use Serve
Use Tailscale Serve for:Personal Media Servers
Jellyfin, Plex, Navidrome - keep your media library private to family/friends
Admin Interfaces
Portainer, Dozzle, Homarr - administrative dashboards should never be public
Internal Tools
Development tools, internal wikis, documentation - for team/personal use only
Sensitive Services
Vaultwarden, Home Assistant, file managers - anything with personal data
Tailscale Funnel
Tailscale Funnel lets you route traffic from the public internet to local services. Anyone with the URL can access your service, even without Tailscale installed.How Funnel Works
Funnel Configuration Example
To enable public internet access, setAllowFunnel to true:
compose.yaml
When to Use Funnel
Use Tailscale Funnel for:Public Websites
Blogs, portfolios, landing pages - content meant for public consumption
Public APIs
Webhooks, API endpoints - services that need to receive external requests
Demo Applications
Sharing prototypes or demos with clients who don’t have Tailscale
Public Services
URL shorteners, paste bins - utilities meant for public use
Key Differences
- Access Control
- Security
- Use Cases
- Configuration
| Feature | Serve | Funnel |
|---|---|---|
| Who can access | Only Tailnet members | Anyone on the internet |
| Authentication | Tailscale login required | No authentication |
| Network | Private VPN only | Public internet + VPN |
| Device requirement | Must have Tailscale installed | No Tailscale needed |
Migration Between Serve and Funnel
Switching between Serve and Funnel is simple - just modify theAllowFunnel setting.
Making a Service Public (Serve → Funnel)
Enable Funnel in Tailscale Admin
Funnel must be enabled for your Tailnet:
- Go to Tailscale Admin Console
- Navigate to Settings > General
- Enable Funnel
Making a Service Private (Funnel → Serve)
Advanced Configurations
Path-Based Routing
Expose different paths with different access levels:Path-based access control is handled at the application level. Funnel is an all-or-nothing setting per domain.
Multiple Services with Different Access
Run multiple services with different Serve/Funnel configurations:Security Considerations
Serve Security
Serve Security
Strengths:
- ✅ Automatic authentication via Tailscale
- ✅ End-to-end encryption
- ✅ ACL support for fine-grained access
- ✅ No exposure to internet threats
- ⚠️ All Tailnet members can access unless ACLs restrict
- ⚠️ Compromised Tailscale credentials = compromised access
- ⚠️ Application-level auth still recommended for sensitive data
Funnel Security
Funnel Security
Strengths:
- ✅ HTTPS enforced automatically
- ✅ Tailscale provides DDoS mitigation
- ✅ Valid TLS certificates included
- ⚠️ No authentication - anyone with the URL can access
- ⚠️ Attack surface - exposed to all internet threats
- ⚠️ Application vulnerabilities - your app must handle all security
- ⚠️ Rate limiting - must be handled at application level
- Implement application-level authentication
- Add rate limiting and DDoS protection
- Regular security updates
- Monitor logs for suspicious activity
- Use Web Application Firewall (WAF) if possible
Hybrid Approach
Hybrid Approach
For maximum security with public access:
- Use Funnel for public accessibility
- Add authentication at the application level (OAuth, password, etc.)
- Implement rate limiting to prevent abuse
- Monitor access logs for suspicious activity
- Keep software updated to patch vulnerabilities
Common Scenarios
Scenario 1: Family Media Server
Goal: Share Jellyfin with family members only Solution: Use Serve- Family members install Tailscale
- Automatic authentication
- No exposure to internet
- Safe for personal media
Scenario 2: Public Portfolio Website
Goal: Share your portfolio with potential employers Solution: Use Funnel- No Tailscale installation required for viewers
- Public accessibility
- Professional presence
- Static content = low security risk
Scenario 3: Development Team Tool
Goal: Internal wiki for 5-person team Solution: Use Serve with ACLs- Team already uses Tailscale
- Fine-grained access control via ACLs
- Secure by default
- Easy onboarding/offboarding
Troubleshooting
Funnel not working after enabling
Funnel not working after enabling
Symptoms: Public access still blocked after setting
AllowFunnel: trueSolutions:- Verify Funnel is enabled in Tailscale Admin
- Check device has Funnel permissions in ACLs
- Restart Tailscale container:
docker compose restart tailscale - Check Tailscale logs:
docker compose logs tailscale - Test with:
tailscale serve status(exec into container)
Serve accessible from public internet
Serve accessible from public internet
Symptoms: Service accessible without Tailscale despite
AllowFunnel: falseSolutions:- Verify no port forwarding in router to the service
- Check no
ports:directive exposing service publicly - Confirm
AllowFunnelis actuallyfalsein config - Review firewall rules on host machine
Connection works on Tailscale but not public internet
Connection works on Tailscale but not public internet
Symptoms: Service works with Serve but Funnel shows errorsSolutions:
- Verify Funnel is enabled for your Tailnet
- Check application listens on 127.0.0.1 or 0.0.0.0 (not just localhost)
- Review application logs for errors
- Test internal access:
curl http://127.0.0.1:8096from Tailscale container
Best Practices
Default to Serve
Always start with Serve (private). Only enable Funnel when public access is explicitly required.
Defense in Depth
Even with Serve, implement application-level authentication for sensitive services.
Monitor Access
For Funnel services, monitor access logs and set up alerts for suspicious activity.
Use ACLs
With Serve, use Tailscale ACLs to restrict which Tailnet members can access specific services.
Document Decisions
Comment in your compose.yaml why each service uses Serve or Funnel.
Regular Audits
Periodically review which services are public and confirm they should remain so.
Next Steps
Environment Variables
Complete reference for configuring Tailscale behavior
Sidecar Pattern
Deep dive into the Docker sidecar architecture
Tailscale Setup
Configure authentication and manage your Tailnet
Deploy Services
Start deploying with Serve or Funnel