How to Use Cloudflare Tunnel to Expose Your VPS Without Opening Firewall Ports

How to Use Cloudflare Tunnel to Expose Your VPS Without Opening Firewall Ports

Cloudflare Tunnel creates an outbound-only encrypted connection from your VPS to Cloudflare’s network — your services become accessible via Cloudflare’s edge without opening any inbound ports on your VPS firewall. No exposed port 80, no exposed port 443, no public IP directly reachable by attackers. Cloudflare terminates all public HTTPS connections and forwards traffic through the encrypted tunnel. This is the most security-hardened way to expose VPS services publicly.

Why Cloudflare Tunnel Over Standard Nginx + SSL

  • Zero inbound ports: UFW blocks all inbound traffic; the tunnel is outbound-only. Port scans, direct IP attacks, and DDoS against your VPS IP are all useless.
  • Automatic SSL: Cloudflare handles SSL certificates — no Certbot, no renewal management.
  • DDoS protection: Cloudflare absorbs attacks at its edge before traffic reaches your VPS.
  • No IP exposure: Your VPS IP is never publicly associated with your domain.
  • Free tier: Cloudflare Tunnel is free for personal and small business use.

Prerequisites

  • A Cloudflare account (free at cloudflare.com)
  • Your domain’s nameservers pointed to Cloudflare
  • A VPS running your services (locally on any port)

Step 1: Install cloudflared on VPS

wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

cloudflared version   # Verify installation

Step 2: Authenticate with Cloudflare

cloudflared tunnel login

This outputs a URL. Open it in your browser, log into Cloudflare, select your domain, and click Authorize. A certificate file is saved to ~/.cloudflared/cert.pem.

Step 3: Create a Named Tunnel

cloudflared tunnel create my-vps-tunnel

# Output includes your tunnel UUID:
# Created tunnel my-vps-tunnel with id abc12345-def6-7890-abcd-ef1234567890

# List all tunnels
cloudflared tunnel list

Step 4: Configure the Tunnel (Multi-Service)

nano ~/.cloudflared/config.yml
tunnel: abc12345-def6-7890-abcd-ef1234567890   # Your tunnel UUID
credentials-file: /root/.cloudflared/abc12345-def6-7890-abcd-ef1234567890.json

ingress:
  # Main website — Nginx backend on port 8080
  - hostname: yourdomain.com
    service: http://localhost:8080

  # API — FastAPI on port 8000
  - hostname: api.yourdomain.com
    service: http://localhost:8000

  # Grafana monitoring — port 3001
  - hostname: grafana.yourdomain.com
    service: http://localhost:3001

  # Gitea — port 3000
  - hostname: git.yourdomain.com
    service: http://localhost:3000

  # JupyterLab — port 8888 (with WebSocket support)
  - hostname: jupyter.yourdomain.com
    service: http://localhost:8888
    originRequest:
      noTLSVerify: false
      connectTimeout: 30s

  # Required: catch-all for unmatched hostnames
  - service: http_status:404

Step 5: Create DNS Records

# Creates CNAME records in Cloudflare DNS automatically
cloudflared tunnel route dns my-vps-tunnel yourdomain.com
cloudflared tunnel route dns my-vps-tunnel api.yourdomain.com
cloudflared tunnel route dns my-vps-tunnel grafana.yourdomain.com
cloudflared tunnel route dns my-vps-tunnel git.yourdomain.com
cloudflared tunnel route dns my-vps-tunnel jupyter.yourdomain.com

Step 6: Run as systemd Service

sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sudo systemctl status cloudflared

Step 7: Lock Down the Firewall Completely

With the tunnel running, close all inbound ports except SSH:

sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH from your IP only (most secure)
sudo ufw allow from YOUR_HOME_IP to any port 22

# Or allow SSH from anywhere (less secure but practical)
# sudo ufw limit 22/tcp

# Do NOT open ports 80 or 443 — tunnel handles all web traffic
sudo ufw enable
sudo ufw status verbose

WebSocket Configuration

For services using WebSockets (Jupyter, real-time apps, n8n), add to the ingress entry:

  - hostname: realtime.yourdomain.com
    service: http://localhost:4000
    originRequest:
      connectTimeout: 30s
      http2Origin: true   # Improves WebSocket performance

Also ensure WebSockets are enabled in Cloudflare Dashboard → your domain → Network → WebSockets (enabled by default).

Cloudflare Access: Add Authentication to Internal Tools

Combine Cloudflare Tunnel with Cloudflare Access to add SSO authentication to internal services without configuring auth in each app:

  1. Cloudflare Dashboard → Zero Trust → Access → Applications → Add application
  2. Enter the hostname (grafana.yourdomain.com)
  3. Set policy: allow specific email addresses, GitHub org members, or Google Workspace domain

Users are redirected to Cloudflare’s login page before reaching your service — no Grafana auth configuration required.

Monitor Tunnel Status

# View tunnel status and connection metrics
cloudflared tunnel info my-vps-tunnel

# View logs
sudo journalctl -u cloudflared -f

# Test tunnel connectivity
curl -I https://yourdomain.com

Getting Started

Cloudflare Tunnel works on any Ubuntu VPS at VPS.DO — no special networking required. The tunnel creates outbound connections to Cloudflare’s edge, which any standard VPS supports. Combine with KVM VPS running Coolify or Dokku for a zero-inbound-port deployment that is developer-friendly and security-hardened.

Conclusion

Cloudflare Tunnel eliminates the VPS’s public-facing attack surface: no open ports 80 or 443, no direct IP exposure, no SSL certificate management, and DDoS protection at Cloudflare’s edge. The outbound-only tunnel model means your VPS IP can be fully firewalled while all web services remain publicly accessible. For security-conscious deployments, this is a meaningful improvement over the traditional open-port-plus-SSL approach.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!