← Back to all docs

Tailscale Funnel vs Production Infrastructure

Is Tailscale Funnel Suitable for Production?

Short answer: No, not for serious production workloads.

Tailscale Funnel is excellent for what it is — a quick, zero-config way to expose a local service to the internet. But it has hard limitations that make it unsuitable for production:

Limitation Why It Matters
Your URL is tied to your Tailscale machine name You can't use a custom domain like myapp.com
Single machine only No load balancing, no failover
Tailscale is a dependency If Tailscale's infrastructure has an outage, your app goes down
Not designed for high traffic No CDN, no DDoS protection, no caching
No SLA Tailscale Funnel is a convenience feature, not an infrastructure product
Certificate tied to Tailscale You can't bring your own TLS cert

Tailscale Funnel is perfect for: demos, internal tools, hobby projects, sharing a dev server with a client temporarily. Stop there.


What Production Infrastructure Looks Like

The standard production stack for a self-hosted app (on a VPS, mini PC, or home server) is:

User's Browser
      │  HTTPS (your domain, e.g. myapp.com)
      ▼
Cloudflare  ◄─── DNS + DDoS protection + CDN + TLS termination
      │  HTTP or HTTPS (proxied)
      ▼
Your Server (public IP)
      │
      ▼
nginx  ◄─── Reverse proxy, port 80/443
      │
      ▼
Node.js app :3000

The three core components are:

  1. A domain name — you own myapp.com
  2. Cloudflare — sits in front of your server, handles DNS, TLS, and protection
  3. Let's Encrypt / Certbot — free TLS certificates on your server (or let Cloudflare handle TLS entirely)

Component 1: A Domain Name

Buy a domain from any registrar: Namecheap, Cloudflare Registrar (cheapest, no markup), Porkbun, Google Domains (now Squarespace).

Cloudflare Registrar (cloudflare.com/products/registrar) sells at wholesale cost — recommended if you're already using Cloudflare.


Component 2: Cloudflare Setup

Cloudflare acts as your DNS provider and sits as a reverse proxy in front of your server. This gives you:

How to Set Up Cloudflare

1. Create a free Cloudflare account at cloudflare.com

2. Add your domain

3. Update your domain's nameservers

4. Add a DNS A record pointing to your server

5. Set SSL/TLS mode


Component 3: TLS Certificate on Your Server (Let's Encrypt)

Even with Cloudflare handling the browser-facing TLS, your server needs its own certificate for the Cloudflare → server leg (required for Full Strict mode).

Certbot is the standard tool for getting free Let's Encrypt certificates.

Install Certbot (on Ubuntu/Debian VPS)

sudo apt update
sudo apt install certbot python3-certbot-nginx -y

Get a Certificate

sudo certbot --nginx -d myapp.com -d www.myapp.com

Certbot will:

  1. Verify you control the domain (via HTTP challenge)
  2. Issue a certificate from Let's Encrypt
  3. Automatically edit your nginx config to use HTTPS
  4. Set up auto-renewal (certificates expire every 90 days)

Verify auto-renewal works:

sudo certbot renew --dry-run

nginx Config with TLS

After Certbot runs, your nginx config will look like this:

server {
    listen 80;
    server_name myapp.com www.myapp.com;
    return 301 https://$host$request_uri;  # redirect HTTP → HTTPS
}

server {
    listen 443 ssl;
    server_name myapp.com www.myapp.com;

    ssl_certificate     /etc/letsencrypt/live/myapp.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;

    # Cloudflare recommended: only allow Cloudflare IPs
    # (optional but hardens your server)
    include /etc/nginx/cloudflare-ips.conf;
    deny all;

    location / {
        proxy_pass         http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $http_cf_connecting_ip;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection 'upgrade';
    }
}

Note: $http_cf_connecting_ip — when Cloudflare proxies traffic, the real client IP comes in the CF-Connecting-IP header. Use this instead of $remote_addr so your app logs real IPs, not Cloudflare's.


Component 4: Keeping Your App Running (Process Manager)

In production, your Node app must restart automatically if it crashes or the server reboots. Use PM2:

npm install -g pm2

# Start your app
pm2 start server.js --name myapp

# Save the process list and enable startup on boot
pm2 save
pm2 startup   # follow the printed instruction to register with systemd

PM2 gives you:


Summary: Full Production Checklist

[ ] Buy a domain (Cloudflare Registrar recommended)
[ ] Point domain nameservers to Cloudflare
[ ] Add A record in Cloudflare DNS → your server IP (Proxied)
[ ] Set Cloudflare SSL/TLS to Full (strict)
[ ] Install Certbot on server, get Let's Encrypt certificate
[ ] Configure nginx with TLS + reverse proxy to your app
[ ] Optionally restrict nginx to Cloudflare IPs only
[ ] Use PM2 (Node) or systemd to keep your app running
[ ] Set up PM2 startup script for auto-start on reboot

What About This Mini PC Specifically?

This setup assumes your server has a static public IP. A mini PC at home or on a local network typically sits behind a home router with:

If that's the case, your options are:

Option How
Rent a cheap VPS (recommended) DigitalOcean/Hetzner/Vultr from ~$4–6/month. Deploy your app there instead. Static IP included.
Cloudflare Tunnel Like Tailscale Funnel but Cloudflare-grade. No public IP needed, works behind CGNAT, ties into your own domain. Free tier available.
Port forwarding + DDNS Forward port 80/443 on your router to this PC. Use a DDNS service (Cloudflare DDNS, DuckDNS) to keep your domain pointing at your dynamic IP. Fragile.

Cloudflare Tunnel (Best of Both Worlds)

If you want to keep using this mini PC but need production-grade infrastructure, Cloudflare Tunnel is the right tool — it's essentially Tailscale Funnel but:

# Install cloudflared
winget install Cloudflare.cloudflared

# Authenticate
cloudflared tunnel login

# Create a tunnel
cloudflared tunnel create myapp

# Route your domain to the tunnel
cloudflared tunnel route dns myapp myapp.com

# Run the tunnel (points to your local Node app)
cloudflared tunnel run --url http://localhost:3000 myapp

Then run as a Windows service so it starts on boot:

cloudflared service install

Your app becomes available at https://myapp.com — fully proxied through Cloudflare, with all the DDoS protection, TLS, and analytics that comes with it.