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
- Self-hosters running apps (Gitea, n8n, Nextcloud, Ollama Web UI) on a budget VPS who want secure public access.
- Developers tired of managing Nginx reverse proxies, Let’s Encrypt renewals, and firewall rules.
- Teams deploying internal tools that should only be accessible to specific users — not the entire internet.
- Anyone on a cheap VPS behind CGNAT or without a static IPv4 address.
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
| Approach | Open ports? | Free SSL? | DDoS protection? | Access control? | Complexity |
|---|---|---|---|---|---|
| Direct port exposure | Yes (22, 80, 443+) | Manual (Certbot) | None | Firewall rules only | Medium |
| Nginx + Let’s Encrypt | Yes (80, 443) | Yes (auto-renew) | None | Basic auth or IP rules | Medium-High |
| Cloudflare Tunnel | No | Yes (automatic) | Yes (free tier) | Zero Trust policies | Low |
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
- A VPS with at least 512 MB RAM and 1 vCPU (the tunnel daemon uses ~30 MB RAM).
- A domain name with DNS managed by Cloudflare (free plan works).
- A Cloudflare account (free tier is sufficient for most setups).
- 15 minutes of your time.
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.
| Provider | Best plan | Monthly cost | Specs | Best for |
|---|---|---|---|---|
| RackNerd | 1 GB KVM | $11–$14/yr | 1 GB RAM / 1 vCPU / 20 GB SSD | Lightweight apps, personal tools |
| Contabo | VPS S | $5/mo | 4 GB RAM / 2 vCPU / 50 GB SSD | Medium workloads, multiple apps |
| Hetzner | CX22 | €4/mo | 4 GB RAM / 2 vCPU / 40 GB SSD | European hosting, reliable network |
| DigitalOcean | Basic $6 | $6/mo | 1 GB RAM / 1 vCPU / 25 GB SSD | Beginners, good dashboard |
| Vultr | Cloud Compute | $6/mo | 1 GB RAM / 1 vCPU / 25 GB SSD | Global 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
- Go to Cloudflare Zero Trust Dashboard → Access → Applications.
- Click “Add an application” → Self-hosted.
- Enter your app domain (e.g.,
ollama.yourdomain.com). - Add a policy:
- Allow → Emails ending in
@yourdomain.com - Or: Allow → Specific email addresses
- Or: Allow → GitHub/Google/Okta login
- Allow → Emails ending in
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 type | Access policy | Reasoning |
|---|---|---|
| Public blog/site | No policy (public) | SEO needs open access |
| Gitea, Nextcloud | Email/GitHub login | Team access only |
| Ollama API, n8n | Strict email allowlist | Sensitive internal tools |
| Admin panels | One-time PIN + email | Maximum lockdown |
Cost Breakdown
| Component | Cost |
|---|---|
| Cloudflare Tunnel | Free |
| Cloudflare Zero Trust (up to 50 users) | Free |
| SSL certificates | Free (automatic) |
| DDoS protection | Free (included) |
| DNS hosting | Free |
| 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
- Single point of failure: If Cloudflare has an outage, your apps go offline. Rare but it happens (2–3 times per year, usually brief).
- Cloudflare sees your traffic: All HTTP traffic passes through Cloudflare unencrypted between their edge and your origin. If you handle sensitive data, add origin-to-tunnel TLS.
- Speed: Adds 10–30ms latency due to the extra hop through Cloudflare’s network. Fine for web apps, noticeable for real-time APIs.
- Bandwidth: Cloudflare’s free tier technically prohibits serving large non-HTML files (video streaming, large downloads). Use R2 or direct hosting for that.
- Vendor lock-in: Your DNS and tunnel are tied to Cloudflare. Migrating means reconfiguring everything.
Troubleshooting Common Issues
| Problem | Solution |
|---|---|
| Tunnel connects but site shows 502 | Your local service is not running or is on a different port. Check with curl localhost:PORT. |
| “Bad gateway” errors | Add noTLSVerify: true under the hostname in config if your app uses self-signed certs. |
| Tunnel disconnects frequently | Check VPS RAM — if the system swaps heavily, cloudflared gets killed. Upgrade to 1+ GB RAM. |
| DNS not resolving | Wait 2–5 minutes for propagation, or check you ran tunnel route dns for that hostname. |
| Zero Trust blocks you | Clear browser cookies for the domain and re-authenticate. |
What to Deploy Behind a Tunnel
Best candidates for this setup:
- n8n — workflow automation, needs web access for webhooks
- Gitea — self-hosted Git, team needs HTTPS access
- Nextcloud — file sync, mobile apps need public endpoint
- Ollama + Open WebUI — private AI chat, lock behind Zero Trust
- Uptime Kuma — monitoring dashboard, view from anywhere
- Vaultwarden — password manager, needs HTTPS for browser extensions
- Home Assistant — smart home, secure remote access
Summary
Cloudflare Tunnel is the easiest way to securely expose self-hosted apps on a budget VPS. You get:
- Zero open ports on your server
- Automatic SSL with no renewal headaches
- Built-in DDoS protection
- Zero Trust access control for sensitive apps
- All of this on the free Cloudflare plan
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.