Logo

Cloudflare Tunnel: expose without opening a port

Cloudflare Tunnel: expose without opening a port

Cloudflare Tunnel (formerly Argo Tunnel) creates an encrypted outbound connection between your server and Cloudflare. No port opening, no public IP, no VPN. Ideal for homelabs, or to hide a production server's origin IP.

Cloudflare Tunnel: expose without opening a port

Cloudflare Tunnel (formerly Argo Tunnel) creates an encrypted outbound connection between your server and Cloudflare. No port opening, no public IP, no VPN. Ideal for homelabs, or to hide a production server's origin IP.

Introduction

Cloudflare Tunnel:

  • Outbound connection from server to Cloudflare (no inbound)
  • Automatic TLS (Cloudflare cert)
  • No public IP required
  • Hides origin IP (anti-DDoS, anti-scraping)
  • Zero Trust access (auth via Google, GitHub, etc.)
  • Free up to 50 users
  • Works even behind strict NAT / firewall

Use cases:

  • Self-host Plex, NextCloud, Home Assistant
  • Expose internal VPS without opening firewall
  • Hide production VPS IP
  • Secure SSH access without open port 22

Prerequisites

  • Free Cloudflare account
  • Domain on Cloudflare (DNS must be managed by CF)
  • Linux VPS / server

Step 1: Install cloudflared

curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb
cloudflared --version

Step 2: Login to Cloudflare

cloudflared tunnel login

URL appears. Open it, authenticate on Cloudflare, select your domain.

Certificate downloaded to ~/.cloudflared/cert.pem.

Step 3: Create a tunnel

cloudflared tunnel create mysite

Output:

Tunnel credentials written to /root/.cloudflared/abc123-def456-ghi789.json
Created tunnel mysite with id abc123-def456-ghi789

Step 4: Tunnel configuration

~/.cloudflared/config.yml:

tunnel: abc123-def456-ghi789
credentials-file: /root/.cloudflared/abc123-def456-ghi789.json

ingress:
  - hostname: app.your-domain.com
    service: http://localhost:8080
  
  - hostname: api.your-domain.com
    service: http://localhost:3000
  
  - hostname: ssh.your-domain.com
    service: ssh://localhost:22
  
  # Mandatory catch-all
  - service: http_status:404

Step 5: Cloudflare DNS

cloudflared tunnel route dns mysite app.your-domain.com
cloudflared tunnel route dns mysite api.your-domain.com
cloudflared tunnel route dns mysite ssh.your-domain.com

Check Cloudflare DNS dashboard: CNAMEs created, proxied (orange cloud).

Step 6: Start the tunnel

cloudflared tunnel run mysite

Tunnel connects to Cloudflare. Visit https://app.your-domain.com: your local app is exposed, HTTPS, no open port.

Step 7: systemd service

sudo cloudflared service install
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared

Step 8: SSH access via tunnel

On client, ~/.ssh/config:

Host ssh.your-domain.com
    HostName ssh.your-domain.com
    User root
    ProxyCommand cloudflared access ssh --hostname %h

Install cloudflared locally too:

ssh ssh.your-domain.com

First time: browser opens for Cloudflare Zero Trust auth.

Step 9: Zero Trust Access

Cloudflare Dashboard > Zero Trust > Access > Applications > Add an application > Self-hosted.

Configure:

  • Application name: Mysite Admin
  • Session duration: 24h
  • Application domain: app.your-domain.com
  • Identity providers: Google / GitHub / Email OTP

Add policy:

  • Action: Allow
  • Rules: Emails ending in @your-domain.com

Now https://app.your-domain.com requires SSO login. Useful for Grafana, Portainer, etc.

Step 10: Multiple services on one tunnel

config.yml:

tunnel: abc123
credentials-file: /root/.cloudflared/abc123.json

ingress:
  - hostname: app.your-domain.com
    service: http://localhost:8080
  
  - hostname: blog.your-domain.com
    service: http://localhost:8081
    originRequest:
      noTLSVerify: true
  
  - hostname: ws.your-domain.com
    service: ws://localhost:9000
  
  - hostname: ssh.your-domain.com
    service: ssh://localhost:22
  
  - hostname: rdp.your-domain.com
    service: rdp://localhost:3389
  
  - hostname: db.your-domain.com
    service: tcp://localhost:5432
  
  - service: http_status:404
sudo systemctl restart cloudflared

Step 11: Quick tunnel (short-form)

For fast tests, no tunnel creation needed:

cloudflared tunnel --url http://localhost:8000

Generates temporary *.trycloudflare.com URL. Useful for debug / demo.

Step 12: Monitoring and metrics

/etc/cloudflared/config.yml:

metrics: 0.0.0.0:9090
sudo systemctl restart cloudflared
curl http://localhost:9090/metrics

Integrate with Prometheus to track latency, requests, errors.

Troubleshooting

"tunnel not found"

cloudflared tunnel list

Error 1033 / 1034 on domain

Tunnel not connected:

sudo systemctl status cloudflared
sudo journalctl -u cloudflared -f

"Failed to dial origin"

Local service not listening, or wrong port:

sudo ss -tunlp | grep 8080
curl http://localhost:8080

High latency

ingress:
  - hostname: app.your-domain.com
    service: http://localhost:8080
    originRequest:
      keepAliveTimeout: 30s
      connectTimeout: 10s

WebSocket closes

originRequest:
  noHappyEyeballs: true

Useful commands

cloudflared tunnel login

cloudflared tunnel list
cloudflared tunnel create name
cloudflared tunnel delete name
cloudflared tunnel info name

cloudflared tunnel run name
cloudflared tunnel run --config /path/config.yml name

cloudflared tunnel --url http://localhost:8000

cloudflared tunnel route dns tunnel-name sub.domain.com

sudo cloudflared service install
sudo cloudflared service uninstall
sudo systemctl restart cloudflared
sudo journalctl -u cloudflared -f

cloudflared access ssh --hostname ssh.your-domain.com

Conclusion

Cloudflare Tunnel gives you:

  • Secure exposure without open ports
  • Auto TLS and hidden origin IP
  • Zero Trust with SSO
  • Free for most use cases

Limits:

  • Cloudflare dependency (DNS + tunnel)
  • Added latency
  • Bandwidth limits on free plan

Going further:

  • Combine with Cloudflare Access for SSO auth
  • For SSH, check Cloudflare Tunnel + browser-based SSH
  • Self-hosted alternatives: Tailscale Funnel, frp, Wireguard + reverse proxy

Resources

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members