Secure way to connect your applications to Cloudflare without opening inbound ports. Essential tool for exposing self-hosted services securely through Cloudflare's global network.
1. Prerequisites
2. Supported Operating Systems
This guide supports installation on:
3. Installation
Ubuntu/Debian
# Download 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 installation
cloudflared --version
RHEL/CentOS/Rocky Linux/AlmaLinux
# Download and install 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 installation
cloudflared --version
Docker Installation
# Run cloudflared in Docker
mkdir -p ~/.cloudflared
docker run -d \
--name cloudflared \
--restart unless-stopped \
-v ~/.cloudflared:/etc/cloudflared \
cloudflare/cloudflared:latest \
tunnel run
4. Configuration
Tunnel Setup
# Authenticate with Cloudflare
cloudflared tunnel login
# Create tunnel
cloudflared tunnel create myserver-tunnel
# List tunnels
cloudflared tunnel list
# Create configuration file
sudo mkdir -p /etc/cloudflared
sudo tee /etc/cloudflared/config.yml > /dev/null <<EOF
# Cloudflare Tunnel Configuration
tunnel: your-tunnel-id-here
credentials-file: /etc/cloudflared/your-tunnel-id.json
# Ingress rules
ingress:
# Home Assistant
- hostname: homeassistant.example.com
service: http://localhost:8123
# Nextcloud
- hostname: nextcloud.example.com
service: http://localhost:80
# Grafana
- hostname: grafana.example.com
service: http://localhost:3000
# Jellyfin media server
- hostname: jellyfin.example.com
service: http://localhost:8096
# Default catch-all (required)
- service: http_status:404
# Global configuration
warp-routing:
enabled: true
EOF
# Copy tunnel credentials
sudo cp ~/.cloudflared/your-tunnel-id.json /etc/cloudflared/
sudo chown cloudflared:cloudflared /etc/cloudflared/*
sudo chmod 600 /etc/cloudflared/*.json
DNS Configuration
# Create DNS records for tunnel
cloudflared tunnel route dns myserver-tunnel homeassistant.example.com
cloudflared tunnel route dns myserver-tunnel nextcloud.example.com
cloudflared tunnel route dns myserver-tunnel grafana.example.com
cloudflared tunnel route dns myserver-tunnel jellyfin.example.com
# Verify DNS records
dig homeassistant.example.com
SystemD Service
# Install as system service
sudo cloudflared service install
# Create custom service with security
sudo tee /etc/systemd/system/cloudflared.service > /dev/null <<EOF
[Unit]
Description=Cloudflare Tunnel
After=network.target
[Service]
Type=simple
User=cloudflared
Group=cloudflared
ExecStart=/usr/local/bin/cloudflared tunnel run
Restart=always
RestartSec=5s
StandardOutput=journal
StandardError=journal
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/log/cloudflared
[Install]
WantedBy=multi-user.target
EOF
# Create cloudflared user
sudo useradd --system --shell /bin/false cloudflared
sudo chown -R cloudflared:cloudflared /etc/cloudflared
sudo systemctl daemon-reload
sudo systemctl enable --now cloudflared
Security Configuration
Access Control with Cloudflare Access
# Configure Cloudflare Access policies via API or Dashboard
# Example: Restrict access to specific applications
# Create access policy for sensitive services
cat > access-policy.json <<EOF
{
"name": "Admin Access Only",
"decision": "allow",
"include": [
{
"email": ["admin@example.com"]
}
],
"require": [
{
"any_valid_service_token": {}
}
],
"exclude": []
}
EOF
# Apply to applications via Cloudflare dashboard:
# Security > Cloudflare Access > Applications
Network Security
# Configure local firewall (services now accessible only via Cloudflare)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
# Don't open ports 80/443/8096 etc. - only accessible via tunnel
# Monitor tunnel connections
sudo tee /usr/local/bin/tunnel-monitor.sh > /dev/null <<'EOF'
#!/bin/bash
LOG="/var/log/tunnel-monitor.log"
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a ${LOG}
}
# Check tunnel status
if systemctl is-active cloudflared >/dev/null; then
log_message "✓ Cloudflare tunnel running"
else
log_message "✗ Cloudflare tunnel not running"
fi
# Check tunnel connectivity
if cloudflared tunnel info myserver-tunnel >/dev/null 2>&1; then
log_message "✓ Tunnel connectivity OK"
else
log_message "⚠ Tunnel connectivity issues"
fi
log_message "Tunnel monitoring completed"
EOF
sudo chmod +x /usr/local/bin/tunnel-monitor.sh
echo "*/10 * * * * root /usr/local/bin/tunnel-monitor.sh" | sudo tee -a /etc/crontab
Additional Resources
---
Note: This guide is part of the HowToMgr collection.