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 --versionCreating a Tunnel
1. Authenticate with Cloudflare
cloudflared tunnel loginA 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-xxxxxxxxxxxx3. Create the configuration file
mkdir -p ~/.cloudflared
nano ~/.cloudflared/config.ymltunnel: 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:4044. Create the DNS record on Cloudflare
# Create a CNAME record pointing to the tunnel
cloudflared tunnel route dns my-tunnel app.yourdomain.comThis 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 cloudflaredCheck status
systemctl status cloudflared
journalctl -u cloudflared -fMultiple 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:404After modifying the config:
systemctl restart cloudflaredSSH 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:22cloudflared tunnel route dns my-tunnel ssh.yourdomain.com
systemctl restart cloudflaredClient 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.comUseful 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-tunnelDocker 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: hostWith 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).