Network & Connectivity

Cloudflare Tunnel

Expose services on your VPS to the internet without opening firewall ports, using Cloudflare Tunnel (cloudflared)

Cloudflare Tunnel (cloudflared) creates an outbound encrypted tunnel from your server to Cloudflare's network. Services become publicly accessible via your domain without exposing any port on the firewall, the server only needs outbound internet access on port 443.

Ideal for: home servers, servers behind NAT, extra security layer for web services.


Prerequisites

  • A domain managed on Cloudflare (DNS on Cloudflare)
  • A Cloudflare account (free plan is enough for basic use)
  • A VPS with internet access (outbound port 443)

Installation

Install cloudflared

# Debian / Ubuntu (amd64)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
dpkg -i cloudflared.deb

# CentOS / AlmaLinux / Rocky (amd64)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.rpm -o cloudflared.rpm
rpm -i cloudflared.rpm

# Verify installation
cloudflared --version

Creating a Tunnel

1. Authenticate with Cloudflare

cloudflared tunnel login

A browser opens (or gives you a URL to open manually). Select the domain you want to use and authorize.

This creates a certificate in ~/.cloudflared/cert.pem.

2. Create the tunnel

# Create a new tunnel (choose a name)
cloudflared tunnel create my-tunnel

# Note the tunnel ID from the output, e.g.:
# Created tunnel my-tunnel with id a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx

3. Create the configuration file

mkdir -p ~/.cloudflared
nano ~/.cloudflared/config.yml
tunnel: a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx  # tunnel ID
credentials-file: /root/.cloudflared/a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json

ingress:
  # Route traffic to a local web server
  - hostname: app.yourdomain.com
    service: http://localhost:8080

  # Multiple services example
  - hostname: nextcloud.yourdomain.com
    service: http://localhost:8081

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

4. Create the DNS record on Cloudflare

# Create a CNAME record pointing to the tunnel
cloudflared tunnel route dns my-tunnel app.yourdomain.com

This creates a CNAME app.yourdomain.com → a1b2c3d4....cfargotunnel.com automatically on Cloudflare DNS.


Running as a System Service

Install the service

cloudflared service install
systemctl enable cloudflared
systemctl start cloudflared

Check status

systemctl status cloudflared
journalctl -u cloudflared -f

Multiple Services with a Single Tunnel

A single tunnel can expose multiple services via ingress rules:

tunnel: a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx
credentials-file: /root/.cloudflared/a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json

ingress:
  - hostname: web.yourdomain.com
    service: http://localhost:80

  - hostname: admin.yourdomain.com
    service: http://localhost:8080
    originRequest:
      # Additional security: allow only from Cloudflare IPs
      noTLSVerify: false

  - hostname: ssh.yourdomain.com
    service: ssh://localhost:22

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

After modifying the config:

systemctl restart cloudflared

SSH over Cloudflare Tunnel

You can also tunnel SSH access, so port 22 is never exposed:

Server side

Add to config.yml:

  - hostname: ssh.yourdomain.com
    service: ssh://localhost:22
cloudflared tunnel route dns my-tunnel ssh.yourdomain.com
systemctl restart cloudflared

Client side

# Install cloudflared on your PC as well
# Add to ~/.ssh/config:
Host ssh.yourdomain.com
    ProxyCommand cloudflared access ssh --hostname %h
# Connect normally
ssh user@ssh.yourdomain.com

Useful Commands

# List all tunnels
cloudflared tunnel list

# Delete a tunnel
cloudflared tunnel delete my-tunnel

# Test the tunnel locally
cloudflared tunnel run my-tunnel

# View tunnel info
cloudflared tunnel info my-tunnel

Docker Installation (alternative)

If you prefer not to install on the host:

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: unless-stopped
    command: tunnel --config /etc/cloudflared/config.yml run
    volumes:
      - ~/.cloudflared:/etc/cloudflared
    network_mode: host

With Cloudflare Tunnel you can keep all ports closed on the firewall and still expose web services securely. Cloudflare also applies its WAF, DDoS protection and caching to tunneled traffic automatically.

Traffic routed through a Cloudflare Tunnel passes through Cloudflare's infrastructure. Don't use it for services requiring end-to-end encryption between client and server (e.g. VPNs or services with client-side TLS authentication).

On this page