Back to Cheatsheets
Cheatsheet

Cloudflared Command Cheatsheet

cloudflared is the command-line client for Cloudflare Tunnel. It creates encrypted tunnels between your origin server and Cloudflare's edge network, eliminating the need for public IP addresses or open firewall ports.

Installation

Linux (amd64)

# Download latest cloudflared
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
sudo mv cloudflared-linux-amd64 /usr/local/bin/cloudflared
sudo chmod +x /usr/local/bin/cloudflared
 
# Verify
cloudflared version

Debian / Ubuntu (via apt)

# Add Cloudflare repository
sudo mkdir -p --mode=0755 /usr/share/keyrings
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 any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 
sudo apt-get update && sudo apt-get install cloudflared

macOS (Homebrew)

brew install cloudflared

Docker

docker run cloudflare/cloudflared --version

Windows (winget)

winget install --id Cloudflare.cloudflared

Authentication

# Authenticate with Cloudflare account
cloudflared tunnel login

This opens a browser window to authorize cloudflared. A certificate file (cert.pem) is saved to ~/.cloudflared/.

Tunnel Lifecycle

Create a Tunnel

cloudflared tunnel create <tunnel-name>

Creates a tunnel and generates a credentials file (<tunnel-id>.json) in ~/.cloudflared/.

List Tunnels

cloudflared tunnel list

Tunnel Info

cloudflared tunnel info <tunnel-name>

Delete a Tunnel

cloudflared tunnel delete <tunnel-name>

Cleanup (force delete with DNS)

cloudflared tunnel cleanup <tunnel-name>
cloudflared tunnel delete -f <tunnel-name>

DNS Configuration

Route DNS to Tunnel

# Route a subdomain
cloudflared tunnel route dns <tunnel-name> <subdomain.example.com>
 
# Route a wildcard
cloudflared tunnel route dns <tunnel-name> *.example.com
 
# Route the root domain
cloudflared tunnel route dns <tunnel-name> example.com

Route to Load Balancer

cloudflared tunnel route lb <tunnel-name> <lb-name> <lb-pool>

List Routes

cloudflared tunnel route ip show

Configuration File (config.yml)

The configuration file lives at ~/.cloudflared/<tunnel-id>.json (credentials) and you create ~/.cloudflared/config.yml for tunnel settings.

Basic config.yml

tunnel: <tunnel-name>
credentials-file: /root/.cloudflared/<tunnel-id>.json
 
ingress:
  - hostname: app.example.com
    service: http://localhost:3000
  - service: http_status:404

Multiple Hostnames

tunnel: my-tunnel
credentials-file: /root/.cloudflared/<uuid>.json
 
ingress:
  - hostname: blog.example.com
    service: http://localhost:8080
  - hostname: api.example.com
    service: http://localhost:4000
  - hostname: static.example.com
    service: http://localhost:9000
  - service: http_status:404

With Path-Based Routing

tunnel: my-tunnel
credentials-file: /root/.cloudflared/<uuid>.json
 
ingress:
  - hostname: app.example.com
    path: /api/*
    service: http://localhost:4000
  - hostname: app.example.com
    path: /*
    service: http://localhost:3000
  - service: http_status:404

HTTPS Backend (self-signed)

tunnel: my-tunnel
credentials-file: /root/.cloudflared/<uuid>.json
 
ingress:
  - hostname: app.example.com
    service: https://localhost:443
    originRequest:
      noTLSVerify: true
  - service: http_status:404

SSH / TCP via Tunnel

tunnel: my-tunnel
credentials-file: /root/.cloudflared/<uuid>.json
 
ingress:
  - hostname: ssh.example.com
    service: ssh://localhost:22
  - service: http_status:404

RDP via Tunnel

tunnel: my-tunnel
credentials-file: /root/.cloudflared/<uuid>.json
 
ingress:
  - hostname: rdp.example.com
    service: tcp://localhost:3389
  - service: http_status:404

Note: TCP services (SSH, RDP) require Cloudflare Zero Trust and the Cloudflare WARP client or browser-based terminal to access.

Run Tunnel

Run (foreground)

cloudflared tunnel run <tunnel-name>

Run with config file

cloudflared tunnel --config ~/.cloudflared/config.yml run

Run as a Service (systemd)

# Install as a systemd service
sudo cloudflared service install
 
# Start service
sudo systemctl start cloudflared
 
# Enable at boot
sudo systemctl enable cloudflared
 
# Check status
sudo systemctl status cloudflared
 
# View logs
sudo journalctl -u cloudflared -f

Service Management

# Stop service
sudo systemctl stop cloudflared
 
# Restart service
sudo systemctl restart cloudflared
 
# Uninstall service
sudo cloudflared service uninstall

Run as a Service (Docker)

# Create config directory
mkdir -p ~/.cloudflared
 
# Copy credentials file
cp /path/to/<tunnel-id>.json ~/.cloudflared/
 
# Create config.yml in ~/.cloudflared/
 
# Run Docker container
docker run -d \
  --name cloudflare-tunnel \
  --restart unless-stopped \
  -v ~/.cloudflared:/etc/cloudflared \
  cloudflare/cloudflared tunnel --config /etc/cloudflared/config.yml run

Quick Tunnels (TryCloudflare)

Create a temporary tunnel without authentication — great for testing local servers.

# Expose local server on port 3000
cloudflared tunnel --url http://localhost:3000
 
# Expose with a specific port (no command)
cloudflared tunnel --url localhost:8080

This generates a random <random>.trycloudflare.com URL that's publicly accessible until the process is stopped.

Limitations:

  • No custom domains
  • No load balancing
  • No access policies
  • Temporary (process must stay running)
  • Rate-limited

Advanced Operations

Multiple Tunnels

Run multiple tunnels simultaneously for different services:

# Tunnel for web app
cloudflared tunnel run web-app
 
# Tunnel for API
cloudflared tunnel run api-gateway

Tunnel with Metrics Server

tunnel: my-tunnel
credentials-file: /root/.cloudflared/<uuid>.json
metrics: 0.0.0.0:2000
 
ingress:
  - hostname: app.example.com
    service: http://localhost:3000
  - service: http_status:404

Graceful Shutdown

# Send SIGINT (Ctrl+C) for graceful shutdown
kill -SIGINT <cloudflared-pid>

Environment Variables

# Set log level
export TUNNEL_LOGLEVEL=info          # debug, info, warn, error
 
# Set metrics port
export TUNNEL_METRICS=0.0.0.0:2000
 
# Set retries
export TUNNEL_RETRIES=5
 
# Set transport protocol
export TUNNEL_TRANSPORT_PROTOCOL=auto  # auto, http2, quic

Protocol Options

tunnel: my-tunnel
credentials-file: /root/.cloudflared/<uuid>.json
protocol: quic      # auto, http2, quic (auto is default)
 
# or via flag:
# cloudflared tunnel run --protocol quic my-tunnel

QUIC is generally recommended for better performance and reliability.

Load Balancing

Create a Load Balancer Pool

# Through Cloudflare Dashboard or API
# Then route tunnel to it
cloudflared tunnel route lb <tunnel-name> <lb-name> <pool-name>

Multiple Origins per Tunnel

tunnel: my-tunnel
credentials-file: /root/.cloudflared/<uuid>.json
 
ingress:
  - hostname: app.example.com
    service: http://localhost:3000
  - hostname: app.example.com
    service: http://localhost:3001
  - service: http_status:404

Monitoring & Logging

View Real-Time Logs

cloudflared tunnel run <tunnel-name> --loglevel debug
 
# Or with systemd
journalctl -u cloudflared -f -n 100

Metrics Endpoint

Access http://localhost:2000/metrics (or your configured metrics port) to view Prometheus metrics including:

  • Active connections
  • Bytes transferred
  • Error rates
  • Tunnel uptime

Troubleshooting

Tunnel Not Connecting

# Verify authentication
cloudflared tunnel list
 
# Check DNS propagation
dig +short <subdomain.example.com>
 
# Verify tunnel credentials exist
ls -la ~/.cloudflared/*.json
 
# Run with debug logging
cloudflared tunnel run <tunnel-name> --loglevel debug

Common Errors

ErrorSolution
failed to connect to CloudflareCheck firewall / outbound connectivity (ports 7844, 80, 443)
No DNS records foundRun cloudflared tunnel route dns to add DNS
cert.pem not foundRun cloudflared tunnel login
Tunnel not foundVerify tunnel name with cloudflared tunnel list
Connection reset by peerNetwork interruption — cloudflared auto-retries
400 bad requestCheck ingress rules in config.yml
Failed to load configValidate YAML syntax in config.yml

Firewall / Network Requirements

Outbound connectivity required:

  • Tunnel connections: TCP port 7844 (QUIC) or 80/443 (HTTP/2 fallback)
  • API access: api.cloudflare.com (HTTPS)
  • Metrics: Local port 2000 (optional, for monitoring)

Health Check

# Check tunnel status
cloudflared tunnel info <tunnel-name>
 
# Monitor through Cloudflare Dashboard
# Zero Trust > Networks > Tunnels

Upgrade

# Linux (manual)
sudo cloudflared update
 
# Debian/Ubuntu
sudo apt-get update && sudo apt-get install --only-upgrade cloudflared
 
# macOS
brew upgrade cloudflared
 
# Docker
docker pull cloudflare/cloudflared
docker stop cloudflare-tunnel && docker rm cloudflare-tunnel
# Re-run with same config

Full Example: End-to-End Setup

# 1. Install
sudo apt-get install cloudflared
 
# 2. Authenticate
cloudflared tunnel login
 
# 3. Create tunnel
cloudflared tunnel create my-first-tunnel
# Output: Created tunnel my-first-tunnel with id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
 
# 4. Create config file
cat > ~/.cloudflared/config.yml << 'EOF'
tunnel: my-first-tunnel
credentials-file: /root/.cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json
 
ingress:
  - hostname: app.example.com
    service: http://localhost:3000
  - service: http_status:404
EOF
 
# 5. Route DNS
cloudflared tunnel route dns my-first-tunnel app.example.com
 
# 6. Run
cloudflared tunnel run my-first-tunnel
 
# 7. (Optional) Install as service
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared

Tunnel Migration

Move a Tunnel to a New Server

# On old server
# Copy credentials file
cat ~/.cloudflared/<tunnel-id>.json
 
# On new server
# Install cloudflared and authenticate
cloudflared tunnel login
 
# Create the credentials file manually
# Copy the JSON content from the old server
vim ~/.cloudflared/<tunnel-id>.json
 
# Copy config
scp user@old-server:~/.cloudflared/config.yml ~/.cloudflared/
 
# Update credentials-file path in config.yml if needed
 
# Run the tunnel
cloudflared tunnel run <tunnel-name>

Note: The tunnel credentials file is tied to the tunnel, not the server. You can copy it to any machine.

Useful One-Liners

# Quick expose local development server
cloudflared tunnel --url http://localhost:3000
 
# Run tunnel with custom log level
cloudflared tunnel run --loglevel debug my-tunnel
 
# Check version and update availability
cloudflared version
 
# Show all available commands
cloudflared --help
 
# Run with specific protocol
cloudflared tunnel run --protocol quic my-tunnel
 
# Test config syntax (dry run)
cloudflared tunnel run --config path/to/config.yml my-tunnel