Cloudflare Tunnel - Expose Services Without Public IP¶
Problem Statement¶
You have services running on machines without public IP addresses (ephemeral environments, home labs, corporate networks behind NAT) and need to make them accessible via HTTPS with custom domains.
Common scenarios:
- iximiuz Labs playgrounds (no public IP, 8-hour sessions)
- Home server behind router NAT
- Corporate network with firewall restrictions
- Development environments in containers
Traditional solutions:
- Port forwarding (requires router access, security risk)
- VPS with reverse proxy (costs money, extra infrastructure)
- ngrok/localtunnel (limited free tier, random URLs)
Limitations:
- No static public IP
- Can't configure inbound firewall rules
- Need professional custom domain (not random URLs)
- Require HTTPS with valid certificates
Solution: Cloudflare Tunnel¶
What it is: Secure outbound connection from your service to Cloudflare's edge network.
Key advantage: Outbound-only connection (no inbound ports needed).
How it works:
Internet User
↓
Cloudflare Edge (HTTPS termination)
↓
Cloudflare Tunnel (encrypted connection)
↓
Your Service (localhost:8080)
Benefits:
- No public IP required
- No port forwarding needed
- Free HTTPS certificates
- Custom domain support
- DDoS protection included
- Zero Trust security
Prerequisites¶
Requirements¶
- Cloudflare account (free tier works)
- Domain registered (can be purchased through Cloudflare or external registrar)
- Domain DNS managed by Cloudflare (nameservers pointed to Cloudflare)
- Service running locally (e.g., Jenkins on port 8080)
Verify DNS Setup¶
Check your domain nameservers:
dig +short NS ibtisam-iq.com
# Should show Cloudflare nameservers:
# bob.ns.cloudflare.com
# sue.ns.cloudflare.com
Setup Steps¶
Step 1: Create Tunnel¶
Via Cloudflare Dashboard:
- Login to Cloudflare Dashboard
- Navigate to: Zero Trust → Networks → Tunnels
- Click: Create a tunnel
- Choose: Cloudflared
- Name your tunnel:
jenkins-tunnel(or descriptive name) - Click: Save tunnel
Result: Cloudflare generates a unique tunnel token (keep this secure).
Step 2: Install cloudflared¶
On your server (Ubuntu/Debian):
# Download and install
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared
sudo mv cloudflared /usr/local/bin/
sudo chmod +x /usr/local/bin/cloudflared
# Verify installation
cloudflared version
Alternative (package manager):
# Add Cloudflare GPG key
sudo mkdir -p /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
# Add repository
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
# Install
sudo apt update
sudo apt install cloudflared
Step 3: Install Tunnel as System Service¶
Using tunnel token:
# Install tunnel (creates systemd service)
sudo cloudflared service install eyJhIjoiOTM4NDU2...YOUR_TOKEN_HERE
# Service is automatically enabled and started
# Verify status
sudo systemctl status cloudflared
# Check logs
sudo journalctl -u cloudflared -f
Service management:
# Start
sudo systemctl start cloudflared
# Stop
sudo systemctl stop cloudflared
# Restart
sudo systemctl restart cloudflared
# Enable on boot
sudo systemctl enable cloudflared
# Disable
sudo systemctl disable cloudflared
Step 4: Configure Public Hostname (Route)¶
In Cloudflare Dashboard:
- Go to your tunnel: jenkins-tunnel
- Navigate to: Public Hostname tab
- Click: Add a public hostname
Configure route:
Subdomain: jenkins
Domain: ibtisam-iq.com (select your domain)
Path: (leave empty)
Service:
Type: HTTP
URL: localhost:8080
- Click: Save hostname
Result: Your service is now accessible at https://jenkins.ibtisam-iq.com
How It Works¶
Architecture¶
┌─────────────┐
│ Internet │
│ User │
└──────┬──────┘
│ HTTPS Request
│ https://jenkins.ibtisam-iq.com
▼
┌─────────────────────────────────┐
│ Cloudflare Edge Network │
│ - DNS resolution │
│ - HTTPS termination │
│ - DDoS protection │
└──────┬──────────────────────────┘
│ Encrypted Tunnel
│ (Outbound connection only)
▼
┌─────────────────────────────────┐
│ Your Server (No public IP) │
│ ┌──────────────────────┐ │
│ │ cloudflared daemon │ │
│ └──────────┬───────────┘ │
│ │ │
│ │ HTTP │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ Jenkins :8080 │ │
│ └──────────────────────┘ │
└─────────────────────────────────┘
Traffic Flow¶
Inbound request:
- User requests:
https://jenkins.ibtisam-iq.com - DNS resolves to Cloudflare IP
- Request hits Cloudflare edge
- Cloudflare forwards via tunnel to cloudflared
- cloudflared proxies to localhost:8080
- Jenkins responds
- Response flows back through tunnel
- User receives response
Key point: All connections are outbound from your server (firewall-friendly).
Adding Multiple Services¶
One Tunnel, Multiple Routes¶
You can expose multiple services using the same tunnel:
In Cloudflare Dashboard:
Route 1: Jenkins
Subdomain: jenkins
Domain: ibtisam-iq.com
Path: (empty)
URL: localhost:8080
Route 2: SonarQube
Subdomain: sonar
Domain: ibtisam-iq.com
Path: (empty)
URL: localhost:9000
Route 3: Nexus
Subdomain: nexus
Domain: ibtisam-iq.com
Path: (empty)
URL: localhost:8081
Result:
https://jenkins.ibtisam-iq.com→ Jenkinshttps://sonar.ibtisam-iq.com→ SonarQubehttps://nexus.ibtisam-iq.com→ Nexus
Note: All routes use the same cloudflared service and tunnel token.
Security Considerations¶
Tunnel Token Management¶
The token is sensitive: Anyone with the token can create routes to your services.
Best practices:
- Store securely: Use secrets manager (Doppler, Vault, AWS Secrets Manager)
- Never commit to Git: Add to .gitignore
- Rotate if compromised: Generate new tunnel token
- Limit access: Only install on trusted machines
Example using Doppler:
# Store token
doppler secrets set CLOUDFLARE_TUNNEL_TOKEN="eyJhIjoiOTM4..."
# Retrieve and install
TOKEN=$(doppler secrets get CLOUDFLARE_TUNNEL_TOKEN --plain)
sudo cloudflared service install $TOKEN
Access Control¶
Cloudflare Zero Trust options:
- Add authentication (Google SSO, GitHub, email OTP)
- IP allowlist/blocklist
- Geographic restrictions
- Rate limiting
Configure in: Cloudflare Dashboard → Zero Trust → Access → Applications
Troubleshooting¶
Check Service Status¶
# Service status
sudo systemctl status cloudflared
# View logs
sudo journalctl -u cloudflared -f
# Test connectivity
curl https://jenkins.ibtisam-iq.com
Common Issues¶
Issue: Tunnel shows "Disconnected"
Solution:
# Restart service
sudo systemctl restart cloudflared
# Check logs for errors
sudo journalctl -u cloudflared -n 50
Issue: 404 or 502 error
Possible causes:
- Service not running on specified port
- Incorrect URL in route configuration
- Firewall blocking localhost connections
Verify:
# Check if service is running
curl http://localhost:8080
# Check listening ports
sudo ss -tulpn | grep 8080
Issue: DNS not resolving
Solution:
# Check DNS propagation
dig +short jenkins.ibtisam-iq.com
# Clear local DNS cache
sudo systemd-resolve --flush-caches
# Try different DNS server
dig @8.8.8.8 jenkins.ibtisam-iq.com
Comparison with Alternatives¶
Cloudflare Tunnel vs Traditional Methods¶
| Feature | Cloudflare Tunnel | Port Forwarding | VPS + Reverse Proxy | ngrok |
|---|---|---|---|---|
| Public IP needed | No | No | Yes | No |
| Router access | No | Yes | N/A | No |
| Cost | Free | Free | $5-10/month | Free (limited) |
| Custom domain | Yes | Yes | Yes | Paid only |
| HTTPS certificate | Automatic | Manual | Manual | Automatic |
| DDoS protection | Included | No | Manual setup | Limited |
| Setup complexity | Low | Medium | High | Very Low |
| Security | Zero Trust | Firewall rules | Manual hardening | Basic |
Use Cases¶
Development Environments¶
Scenario: Share work-in-progress with team or clients.
# Development server
npm run dev # localhost:3000
# Expose via tunnel
# Route: dev.ibtisam-iq.com → localhost:3000
# Share: https://dev.ibtisam-iq.com
CI/CD Services (Jenkins, GitLab)¶
Scenario: Access Jenkins from anywhere, receive GitHub webhooks.
# Jenkins running on iximiuz Labs
# No public IP, 8-hour sessions
# Expose via tunnel
# Route: jenkins.ibtisam-iq.com → localhost:8080
# GitHub webhook: https://jenkins.ibtisam-iq.com/github-webhook/
Home Lab Services¶
Scenario: Access home services while traveling.
# Home Assistant, Plex, NAS
# Behind residential NAT
# Expose via tunnel
# Routes:
# - home.ibtisam-iq.com → localhost:8123
# - plex.ibtisam-iq.com → localhost:32400
Best Practices¶
Naming Conventions¶
Tunnel names: Descriptive and environment-specific
jenkins-tunnel
production-tunnel
home-lab-tunnel
Subdomain names: Service-specific
jenkins.ibtisam-iq.com
sonar.ibtisam-iq.com
nexus.ibtisam-iq.com
Monitoring¶
Check tunnel health:
# View tunnel status in dashboard
# Cloudflare → Zero Trust → Networks → Tunnels
# Check service uptime
sudo systemctl status cloudflared
# Monitor logs
sudo journalctl -u cloudflared -f
Cost Analysis¶
Free Tier Includes:¶
- Unlimited tunnels
- Unlimited routes per tunnel
- Automatic HTTPS certificates
- DDoS protection (unmetered)
- 50 users for Zero Trust
- Basic analytics
When to Upgrade:¶
Cloudflare Teams (paid):
- More Zero Trust users (beyond 50)
- Advanced security policies
- Device posture checks
- Audit logs retention
Cost: Starting at $7/user/month
For personal projects and small teams: Free tier is sufficient.
Quick Reference¶
Essential Commands¶
# Install tunnel
sudo cloudflared service install <TOKEN>
# Service management
sudo systemctl {start|stop|restart|status} cloudflared
# View logs
sudo journalctl -u cloudflared -f
# Version
cloudflared version
# Update cloudflared
sudo cloudflared update
# Uninstall
sudo cloudflared service uninstall
Configuration Locations¶
# Service file
/etc/systemd/system/cloudflared.service
# Credentials
/etc/cloudflared/
~/.cloudflared/
# Logs
sudo journalctl -u cloudflared
Summary¶
Cloudflare Tunnel solves the problem of exposing services without public IP addresses by creating secure outbound connections to Cloudflare's edge network.
Key advantages:
- No public IP or port forwarding required
- Automatic HTTPS with valid certificates
- Custom domain support
- Free for unlimited tunnels and routes
- Built-in DDoS protection
- Works in restrictive networks (outbound-only)
Typical workflow:
- Create tunnel in Cloudflare Dashboard
- Install cloudflared on your server
- Add public hostname routes
- Access via custom domain with HTTPS
Perfect for:
- Ephemeral environments (iximiuz Labs, containers)
- Home labs behind NAT
- Development sharing
- CI/CD services
- Learning and experimentation