How to Expose Your VPS Securely with Cloudflare Tunnel (Zero Trust) in 2026

One-sentence verdict: Cloudflare Tunnel lets you expose any VPS service to the internet without opening a single port, giving you free SSL, DDoS protection, and Zero Trust access control — ideal for self-hosted apps on cheap VPS plans that lack static IPs or decent firewalls.

Who This Guide Is For

If you only need static site hosting, Cloudflare Pages is simpler. This guide is for dynamic apps running on your own server.

Why Cloudflare Tunnel Instead of Traditional Port Forwarding

ApproachOpen ports?Free SSL?DDoS protection?Access control?Complexity
Direct port exposureYes (22, 80, 443+)Manual (Certbot)NoneFirewall rules onlyMedium
Nginx + Let’s EncryptYes (80, 443)Yes (auto-renew)NoneBasic auth or IP rulesMedium-High
Cloudflare TunnelNoYes (automatic)Yes (free tier)Zero Trust policiesLow

The key insight: with Cloudflare Tunnel, your VPS firewall can block ALL inbound traffic. The tunnel creates an outbound-only connection from your server to Cloudflare’s edge. Traffic flows: User → Cloudflare Edge → Tunnel → Your VPS (localhost). Your real IP is never exposed.

What You Need

VPS Recommendations for Tunnel Workloads

Since Cloudflare handles SSL termination and DDoS filtering, you can pick the cheapest VPS that runs your app. The tunnel itself adds negligible overhead.

ProviderBest planMonthly costSpecsBest for
RackNerd1 GB KVM$11–$14/yr1 GB RAM / 1 vCPU / 20 GB SSDLightweight apps, personal tools
ContaboVPS S$5/mo4 GB RAM / 2 vCPU / 50 GB SSDMedium workloads, multiple apps
HetznerCX22€4/mo4 GB RAM / 2 vCPU / 40 GB SSDEuropean hosting, reliable network
DigitalOceanBasic $6$6/mo1 GB RAM / 1 vCPU / 25 GB SSDBeginners, good dashboard
VultrCloud Compute$6/mo1 GB RAM / 1 vCPU / 25 GB SSDGlobal locations, hourly billing

For running multiple self-hosted apps behind one tunnel, 4 GB RAM (Contabo or Hetzner) is the sweet spot.

Step-by-Step Setup

Step 1: Install cloudflared on Your VPS

SSH into your VPS and install the Cloudflare Tunnel daemon:

# Debian/Ubuntu
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install -y cloudflared
# CentOS/RHEL/AlmaLinux
sudo rpm -i https://pkg.cloudflare.com/cloudflared-stable-linux-amd64.rpm

Verify installation:

cloudflared --version

Step 2: Authenticate with Cloudflare

cloudflared tunnel login

This opens a browser link. If you are on a headless VPS, copy the URL it prints and open it in your local browser. Select the domain you want to use, and Cloudflare saves a certificate to ~/.cloudflared/cert.pem.

Step 3: Create a Tunnel

cloudflared tunnel create my-vps-tunnel

This generates a tunnel UUID and credentials file. Note the UUID — you will need it for configuration.

Step 4: Configure the Tunnel

Create the config file at ~/.cloudflared/config.yml:

tunnel: YOUR-TUNNEL-UUID
credentials-file: /root/.cloudflared/YOUR-TUNNEL-UUID.json

ingress:
  # Route app.yourdomain.com to a local web app on port 3000
  - hostname: app.yourdomain.com
    service: http://localhost:3000

  # Route git.yourdomain.com to Gitea on port 3100
  - hostname: git.yourdomain.com
    service: http://localhost:3100

  # Route ollama.yourdomain.com to Ollama API
  - hostname: ollama.yourdomain.com
    service: http://localhost:11434

  # Catch-all (required)
  - service: http_status:404

Each hostname maps to a different local service. You can expose as many apps as you want through one tunnel.

Step 5: Create DNS Records

cloudflared tunnel route dns my-vps-tunnel app.yourdomain.com
cloudflared tunnel route dns my-vps-tunnel git.yourdomain.com
cloudflared tunnel route dns my-vps-tunnel ollama.yourdomain.com

This creates CNAME records pointing to your tunnel automatically.

Step 6: Run the Tunnel

Test it first:

cloudflared tunnel run my-vps-tunnel

If everything works, install it as a systemd service for persistence:

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

Step 7: Lock Down Your VPS Firewall

Now that all traffic flows through the tunnel, block everything except SSH (for emergency access):

# UFW (Ubuntu/Debian)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable

Your apps are now accessible ONLY through Cloudflare. No one can hit them directly, even if they find your IP.

Adding Zero Trust Access Control

This is where Cloudflare Tunnel beats every other solution. You can require authentication before anyone reaches your apps — for free.

Set Up an Access Policy

  1. Go to Cloudflare Zero Trust Dashboard → Access → Applications.
  2. Click “Add an application” → Self-hosted.
  3. Enter your app domain (e.g., ollama.yourdomain.com).
  4. Add a policy:
    • Allow → Emails ending in @yourdomain.com
    • Or: Allow → Specific email addresses
    • Or: Allow → GitHub/Google/Okta login

Now anyone visiting ollama.yourdomain.com sees a Cloudflare login page first. Only authorized users get through. Your Ollama API is no longer open to the world.

When to Use Zero Trust vs Public Access

App typeAccess policyReasoning
Public blog/siteNo policy (public)SEO needs open access
Gitea, NextcloudEmail/GitHub loginTeam access only
Ollama API, n8nStrict email allowlistSensitive internal tools
Admin panelsOne-time PIN + emailMaximum lockdown

Cost Breakdown

ComponentCost
Cloudflare TunnelFree
Cloudflare Zero Trust (up to 50 users)Free
SSL certificatesFree (automatic)
DDoS protectionFree (included)
DNS hostingFree
VPS (your choice)$5–$15/mo
Total$5–$15/mo

You get enterprise-grade security infrastructure for the price of a cheap VPS.

Risks and Limitations

Troubleshooting Common Issues

ProblemSolution
Tunnel connects but site shows 502Your local service is not running or is on a different port. Check with curl localhost:PORT.
“Bad gateway” errorsAdd noTLSVerify: true under the hostname in config if your app uses self-signed certs.
Tunnel disconnects frequentlyCheck VPS RAM — if the system swaps heavily, cloudflared gets killed. Upgrade to 1+ GB RAM.
DNS not resolvingWait 2–5 minutes for propagation, or check you ran tunnel route dns for that hostname.
Zero Trust blocks youClear browser cookies for the domain and re-authenticate.

What to Deploy Behind a Tunnel

Best candidates for this setup:

Summary

Cloudflare Tunnel is the easiest way to securely expose self-hosted apps on a budget VPS. You get:

The 15-minute setup pays for itself immediately in reduced maintenance time. No more fighting with Nginx configs, certbot cron jobs, or firewall rules. Point your app at localhost, define the tunnel routing, and Cloudflare handles the rest.

For most self-hosters on a budget, this is now the default way to put apps online. Pair a $5/month VPS with Cloudflare Tunnel and you have infrastructure that rivals setups costing 10x more.