Files
homelab-optimized/docs/infrastructure/cloudflare-tunnels.md
Gitea Mirror Bot 2ea7d71f94
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m3s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-04-05 10:53:12 UTC
2026-04-05 10:53:12 +00:00

543 lines
13 KiB
Markdown

# Cloudflare Tunnels Guide
**Last Updated:** 2026-01-29
This guide covers how to use Cloudflare Tunnels (cloudflared) to expose local services to the internet securely, without opening ports on your router.
## Table of Contents
- [What is Cloudflared?](#what-is-cloudflared)
- [Quick Temporary Tunnel](#quick-temporary-tunnel-no-account-needed)
- [Named Tunnel Setup](#named-tunnel-setup)
- [Docker Compose Setup](#docker-compose-setup-recommended)
- [Adding Authentication](#adding-authentication-cloudflare-access)
- [Common Use Cases](#common-use-cases)
- [Troubleshooting](#troubleshooting)
---
## What is Cloudflared?
**Cloudflared** is Cloudflare's tunnel client that creates a secure, encrypted connection between your local machine and Cloudflare's edge network. It allows you to expose local services to the internet **without opening ports on your router** or having a public IP.
### How It Works
```
Your Local Service → cloudflared → Cloudflare Edge → Public URL → Visitor's Browser
(port 8080) (outbound) (proxy/CDN) (your domain)
```
**Key insight:** cloudflared makes an OUTBOUND connection to Cloudflare, so you don't need to configure any firewall rules or port forwarding.
### Benefits
- ✅ No port forwarding required
- ✅ DDoS protection via Cloudflare
- ✅ Free SSL certificates
- ✅ Optional authentication (Cloudflare Access)
- ✅ Works behind CGNAT
- ✅ Multiple services on one tunnel
---
## Quick Temporary Tunnel (No Account Needed)
This is the fastest way to share something temporarily. No Cloudflare account required.
### Option 1: Using Docker (Easiest)
```bash
# Expose a local service running on port 8080
docker run --rm -it --network host cloudflare/cloudflared:latest tunnel --url http://localhost:8080
# Examples for specific services:
# Jellyfin
docker run --rm -it --network host cloudflare/cloudflared:latest tunnel --url http://localhost:8096
# Grafana
docker run --rm -it --network host cloudflare/cloudflared:latest tunnel --url http://localhost:3000
# Any web service
docker run --rm -it --network host cloudflare/cloudflared:latest tunnel --url http://localhost:PORT
```
### Option 2: Install cloudflared Directly
```bash
# On Debian/Ubuntu
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
sudo dpkg -i cloudflared.deb
# On macOS
brew install cloudflared
# On Windows (PowerShell)
winget install Cloudflare.cloudflared
# Then run:
cloudflared tunnel --url http://localhost:8080
```
### What You'll See
```
INF Thank you for trying Cloudflare Tunnel...
INF Your quick Tunnel has been created! Visit it at:
INF https://random-words-here.trycloudflare.com
```
Share that URL with your friend! When done, press **Ctrl+C** to close the tunnel.
### Quick Tunnel Limitations
- URL changes every time you restart
- No authentication
- No uptime guarantee
- Single service per tunnel
---
## Named Tunnel Setup
Named tunnels give you a **permanent, custom URL** on your own domain with optional authentication.
### Prerequisites
- Cloudflare account (free tier works)
- Domain on Cloudflare DNS (e.g., vish.gg, thevish.io)
- cloudflared installed
### Step 1: Install cloudflared
```bash
# For Synology/Debian/Ubuntu:
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared
# Verify installation
cloudflared --version
```
### Step 2: Authenticate with Cloudflare
```bash
cloudflared tunnel login
```
This will:
1. Open a browser (or provide a URL to visit)
2. Ask you to log into Cloudflare
3. Select which domain to authorize
4. Save a certificate to `~/.cloudflared/cert.pem`
### Step 3: Create a Named Tunnel
```bash
# Create a tunnel named "homelab"
cloudflared tunnel create homelab
```
Output:
```
Created tunnel homelab with id a1b2c3d4-e5f6-7890-abcd-ef1234567890
```
**Save that UUID!** It's your tunnel's unique identifier.
This also creates a credentials file at:
`~/.cloudflared/<TUNNEL_UUID>.json`
### Step 4: Create a Config File
Create `~/.cloudflared/config.yml`:
```yaml
# Tunnel UUID (from step 3)
tunnel: a1b2c3d4-e5f6-7890-abcd-ef1234567890
credentials-file: /root/.cloudflared/a1b2c3d4-e5f6-7890-abcd-ef1234567890.json
# Route traffic to local services
ingress:
# Jellyfin at jellyfin.vish.gg
- hostname: jellyfin.vish.gg
service: http://localhost:8096
# Paperless at docs.vish.gg
- hostname: docs.vish.gg
service: http://localhost:8000
# Grafana at grafana.vish.gg
- hostname: grafana.vish.gg
service: http://localhost:3000
# SSH access at ssh.vish.gg
- hostname: ssh.vish.gg
service: ssh://localhost:22
# Catch-all (required) - returns 404 for unmatched hostnames
- service: http_status:404
```
### Step 5: Create DNS Routes
For each hostname, create a DNS record pointing to your tunnel:
```bash
# Automatically create CNAME records
cloudflared tunnel route dns homelab jellyfin.vish.gg
cloudflared tunnel route dns homelab docs.vish.gg
cloudflared tunnel route dns homelab grafana.vish.gg
cloudflared tunnel route dns homelab ssh.vish.gg
```
This creates CNAME records pointing to `<TUNNEL_UUID>.cfargotunnel.com`
### Step 6: Run the Tunnel
```bash
# Test it first
cloudflared tunnel run homelab
# Or run with specific config file
cloudflared tunnel --config ~/.cloudflared/config.yml run homelab
```
### Step 7: Run as a Service (Persistent)
```bash
# Install as a systemd service
sudo cloudflared service install
# Start and enable
sudo systemctl start cloudflared
sudo systemctl enable cloudflared
# Check status
sudo systemctl status cloudflared
# View logs
sudo journalctl -u cloudflared -f
```
---
## Docker Compose Setup (Recommended)
For homelab use, running cloudflared as a Docker container is recommended.
### Directory Structure
```
cloudflared/
├── docker-compose.yml
├── config.yml
└── credentials.json # Copy from ~/.cloudflared/<UUID>.json
```
### docker-compose.yml
```yaml
version: "3.9"
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --config /etc/cloudflared/config.yml run
volumes:
- ./config.yml:/etc/cloudflared/config.yml:ro
- ./credentials.json:/etc/cloudflared/credentials.json:ro
networks:
- homelab
networks:
homelab:
external: true
```
### config.yml (Docker version)
```yaml
tunnel: a1b2c3d4-e5f6-7890-abcd-ef1234567890
credentials-file: /etc/cloudflared/credentials.json
ingress:
# Use container names when on same Docker network
- hostname: jellyfin.vish.gg
service: http://jellyfin:8096
- hostname: paperless.vish.gg
service: http://paperless-ngx:8000
- hostname: grafana.vish.gg
service: http://grafana:3000
# For services on the host network, use host IP
- hostname: portainer.vish.gg
service: http://192.168.0.200:9000
# Catch-all (required)
- service: http_status:404
```
### Deploy
```bash
cd cloudflared
docker-compose up -d
# Check logs
docker logs -f cloudflared
```
---
## Adding Authentication (Cloudflare Access)
Protect services with Cloudflare Access (free for up to 50 users).
### Setup via Dashboard
1. Go to **Cloudflare Dashboard****Zero Trust****Access****Applications**
2. Click **Add an Application****Self-hosted**
3. Configure:
- **Application name**: Grafana
- **Session duration**: 24 hours
- **Application domain**: `grafana.vish.gg`
4. Create a **Policy**:
- **Policy name**: Allow Me
- **Action**: Allow
- **Include**:
- Emails: `your-email@gmail.com`
- Or Emails ending in: `@yourdomain.com`
5. Save the application
### How It Works
```
Friend visits grafana.vish.gg
→ Cloudflare Access login page
→ Enters email
→ Receives one-time PIN via email
→ Enters PIN
→ Authenticated → Sees Grafana
```
### Authentication Options
| Method | Description |
|--------|-------------|
| One-time PIN | Email-based OTP (default) |
| Google/GitHub/etc. | OAuth integration |
| SAML/OIDC | Enterprise SSO |
| Service Token | For API/automated access |
| mTLS | Certificate-based |
---
## Common Use Cases
### Share Jellyfin for Movie Night
```bash
# Quick tunnel (temporary)
docker run --rm -it --network host cloudflare/cloudflared:latest tunnel --url http://localhost:8096
# Named tunnel (permanent)
# Add to config.yml:
# - hostname: watch.vish.gg
# service: http://localhost:8096
```
### Expose SSH Access
```yaml
# In config.yml
ingress:
- hostname: ssh.vish.gg
service: ssh://localhost:22
```
Client connects via:
```bash
# Install cloudflared on client
cloudflared access ssh --hostname ssh.vish.gg
```
Or configure SSH config (`~/.ssh/config`):
```
Host ssh.vish.gg
ProxyCommand cloudflared access ssh --hostname %h
```
### Expose RDP/VNC
```yaml
ingress:
- hostname: rdp.vish.gg
service: rdp://localhost:3389
- hostname: vnc.vish.gg
service: tcp://localhost:5900
```
### Multiple Services Example
```yaml
tunnel: your-tunnel-uuid
credentials-file: /etc/cloudflared/credentials.json
ingress:
# Media
- hostname: jellyfin.vish.gg
service: http://jellyfin:8096
- hostname: plex.vish.gg
service: http://plex:32400
# Productivity
- hostname: paperless.vish.gg
service: http://paperless:8000
- hostname: wiki.vish.gg
service: http://dokuwiki:80
# Development
- hostname: git.vish.gg
service: http://gitea:3000
- hostname: code.vish.gg
service: http://code-server:8080
# Monitoring
- hostname: grafana.vish.gg
service: http://grafana:3000
- hostname: uptime.vish.gg
service: http://uptime-kuma:3001
# Catch-all
- service: http_status:404
```
---
## Reference Commands
```bash
# Authentication
cloudflared tunnel login # Authenticate with Cloudflare
cloudflared tunnel logout # Remove authentication
# Tunnel Management
cloudflared tunnel list # List all tunnels
cloudflared tunnel info <name> # Get tunnel details
cloudflared tunnel create <name> # Create new tunnel
cloudflared tunnel delete <name> # Delete tunnel (must stop first)
# DNS Routes
cloudflared tunnel route dns <tunnel> <hostname> # Create DNS route
cloudflared tunnel route dns list # List all routes
# Running Tunnels
cloudflared tunnel run <name> # Run tunnel
cloudflared tunnel --config config.yml run # Run with config
cloudflared tunnel ingress validate # Validate config
# Debugging
cloudflared tunnel --loglevel debug run <name> # Debug logging
cloudflared tunnel info <name> # Tunnel info
```
---
## Troubleshooting
### Tunnel won't start
```bash
# Check config syntax
cloudflared tunnel ingress validate
# Run with debug logging
cloudflared tunnel --loglevel debug run homelab
```
### DNS not resolving
```bash
# Verify DNS route exists
cloudflared tunnel route dns list
# Check CNAME in Cloudflare dashboard
# Should point to: <UUID>.cfargotunnel.com
```
### Service unreachable
1. **Check service is running locally:**
```bash
curl http://localhost:8080
```
2. **Check Docker networking:**
- If using container names, ensure same Docker network
- If using localhost, use `--network host` or host IP
3. **Check ingress rules order:**
- More specific rules should come before catch-all
- Catch-all (`http_status:404`) must be last
### Certificate errors
```bash
# Re-authenticate
cloudflared tunnel login
# Check cert exists
ls -la ~/.cloudflared/cert.pem
```
### View tunnel metrics
Cloudflare provides metrics at:
- Dashboard → Zero Trust → Tunnels → Select tunnel → Metrics
---
## Quick vs Named Tunnel Comparison
| Feature | Quick Tunnel | Named Tunnel |
|---------|--------------|--------------|
| URL | `random.trycloudflare.com` | `app.yourdomain.com` |
| Cloudflare Account | ❌ Not needed | ✅ Required |
| Persistence | ❌ Dies with process | ✅ Permanent |
| Custom domain | ❌ No | ✅ Yes |
| Multiple services | ❌ One per tunnel | ✅ Many via ingress |
| Authentication | ❌ None | ✅ Cloudflare Access |
| Setup time | 10 seconds | 10 minutes |
| Best for | Quick demos | Production |
---
## Security Best Practices
1. **Always use HTTPS** - Cloudflare handles this automatically
2. **Enable Cloudflare Access** for sensitive services
3. **Use service tokens** for automated/API access
4. **Monitor tunnel logs** for suspicious activity
5. **Rotate credentials** periodically
6. **Limit ingress rules** to only what's needed
---
## Related Documentation
- [Cloudflare Tunnel Docs](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/)
- [Cloudflare Access Docs](https://developers.cloudflare.com/cloudflare-one/policies/access/)
- [Zero Trust Dashboard](https://one.dash.cloudflare.com/)
---
*Last Updated: 2026-01-29*