What if you could expose a web app running on your home server, NAS, or Raspberry Pi to the internet — without opening any ports, without a static IP, and without touching your router? That's exactly what Cloudflare Tunnel does, and it's completely free.
Whether you're running a self-hosted wiki, a development environment, a home automation dashboard, or a personal API server, Cloudflare Tunnel creates a secure, encrypted connection from your machine to Cloudflare's global network — no inbound firewall rules required.
localhost:3000
outbound only
SSL + DDoS + WAF
app.yourdomain.com
What Is Cloudflare Tunnel?
Cloudflare Tunnel (formerly known as "Argo Tunnel") establishes an outbound-only encrypted connection from your server to Cloudflare's nearest edge server. Once the tunnel is running, Cloudflare proxies incoming requests through the tunnel to your local service — all without opening any inbound ports on your firewall.
This approach inverts the traditional model. Instead of your server listening for incoming connections (which requires open ports, a public IP, and firewall rules), your server initiates the connection outward to Cloudflare. The result is a dramatically smaller attack surface.
- No port forwarding or firewall changes needed
- No static or public IP address required — works behind NAT and CGNAT
- Automatic SSL certificates (Cloudflare handles them)
- Built-in DDoS protection and WAF
- Works from any network — home, office, cloud, or even mobile hotspot
- Multiple services through a single tunnel
Common Use Cases
Home Lab / Self-Hosted Apps
Expose Nextcloud, Gitea, Jellyfin, Vaultwarden, or any self-hosted app without opening your home router to the internet.
Development Environments
Share a local dev server with teammates or clients. Better than ngrok for persistent tunnels — free, faster, and your own domain.
IoT & Home Automation
Access Home Assistant, Grafana dashboards, or Pi-hole remotely without a VPN or exposing ports.
Internal Tools
Make internal dashboards, admin panels, or monitoring tools accessible from anywhere, with Cloudflare Access for authentication.
Prerequisites
Before getting started, make sure you have:
- A free Cloudflare account
- A domain added to your Cloudflare account (DNS must be managed by Cloudflare)
- A machine running the service you want to expose (any OS — Linux, macOS, Windows, Docker)
- The
cloudflaredCLI tool (we'll install it in the steps below)
Method 1: Dashboard Setup (Easiest)
The Cloudflare dashboard provides a guided wizard that generates the install command for you. This is the recommended method for most users.
Go to one.dash.cloudflare.com → Networks → Tunnels → Create a tunnel
Choose Cloudflared (the default connector). Give your tunnel a descriptive name like
home-server or dev-machine.
Cloudflare generates a one-line install command tailored to your OS. For Linux (Debian/Ubuntu):
$ curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
$ sudo dpkg -i cloudflared.deb
# Then run the connector with the token from the dashboard:
$ sudo cloudflared service install YOUR_TUNNEL_TOKEN
Back in the dashboard, click Add a public hostname:
- Subdomain:
app(or whatever you want) - Domain: Select your Cloudflare domain
- Service type:
HTTP - URL:
localhost:3000(or whatever port your app runs on)
Visit
https://app.yourdomain.com in your browser. Your local app is now live on the internet, with SSL, behind Cloudflare's WAF and DDoS protection.
Method 2: CLI Setup
For more control — or if you prefer terminal workflows — you can create and manage tunnels entirely from the command line.
$ cloudflared tunnel login
# Opens browser for OAuth — select your zone
# Step 2: Create a named tunnel
$ cloudflared tunnel create my-tunnel
Tunnel credentials written to /root/.cloudflared/a]b1c2d3-uuid.json
Created tunnel my-tunnel with id ab1c2d3e-4f56-7890-abcd-ef1234567890
# Step 3: Create DNS route
$ cloudflared tunnel route dns my-tunnel app.example.com
Added CNAME app.example.com which will route to this tunnel
Configuration File
Create ~/.cloudflared/config.yml to define your tunnel's routing rules:
tunnel: ab1c2d3e-4f56-7890-abcd-ef1234567890
credentials-file: /root/.cloudflared/ab1c2d3e-4f56-7890-abcd-ef1234567890.json
ingress:
- hostname: app.example.com
service: http://localhost:3000
- hostname: api.example.com
service: http://localhost:8080
- hostname: grafana.example.com
service: http://localhost:3000
- service: http_status:404
hostname field. This handles any request that doesn't match your defined hostnames. Using http_status:404 returns a clean 404 for unmatched requests.
Now start the tunnel:
INF Starting tunnel tunnelID=ab1c2d3e-4f56-7890-abcd-ef1234567890
INF Connection registered connIndex=0 location=AMS
INF Connection registered connIndex=1 location=FRA
INF Connection registered connIndex=2 location=CDG
INF Connection registered connIndex=3 location=LHR
Method 3: Docker
If your services already run in Docker, running the tunnel connector as a container makes perfect sense. This is especially clean for Docker Compose setups.
$ docker run -d --name cloudflared \
--restart unless-stopped \
cloudflare/cloudflared:latest \
tunnel --no-autoupdate run --token YOUR_TUNNEL_TOKEN
Docker Compose Example
version: "3.8"
services:
webapp:
image: nginx:alpine
volumes:
- ./html:/usr/share/nginx/html
tunnel:
image: cloudflare/cloudflared:latest
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
restart: unless-stopped
depends_on:
- webapp
http://webapp:80 instead of http://localhost:80, since they're on the same Docker network.
Running as a System Service
For production use, you'll want the tunnel to start automatically on boot and restart on failure. The cloudflared service install command (used in Method 1) does this automatically. For manual CLI setups, create a systemd unit file:
[Unit]
Description=Cloudflare Tunnel
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/cloudflared tunnel run
Restart=on-failure
RestartSec=5s
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target
$ sudo systemctl enable cloudflared
$ sudo systemctl start cloudflared
$ sudo systemctl status cloudflared
● cloudflared.service - Cloudflare Tunnel
Loaded: loaded
Active: active (running)
Multiple Services Through One Tunnel
One of the most powerful features of Cloudflare Tunnel is routing multiple services through a single tunnel. Each service gets its own subdomain, all managed through the same config.yml or dashboard configuration.
| Public Hostname | Local Service | Port | Use Case |
|---|---|---|---|
app.example.com |
React / Next.js app | 3000 | Main web application |
api.example.com |
Go / Node.js API | 8080 | Backend REST API |
grafana.example.com |
Grafana | 3000 | Monitoring dashboards |
home.example.com |
Home Assistant | 8123 | IoT / home automation |
git.example.com |
Gitea | 3000 | Self-hosted Git |
vault.example.com |
Vaultwarden | 8080 | Password manager |
ssh.example.com) or even RDP (rdp.example.com) through the same tunnel — all without opening any ports.
Security Best Practices
While Cloudflare Tunnel dramatically reduces your attack surface by eliminating open ports, there are additional security measures you should implement:
- Enable Cloudflare Access — Add authentication (email OTP, Google/GitHub SSO, or SAML) before visitors can reach your tunnel. Free for up to 50 users.
- Set up WAF rules — Use Cloudflare's Web Application Firewall to block common attack patterns on your tunnel endpoints.
- Use service tokens — For machine-to-machine access (CI/CD, APIs), create service tokens instead of user-based authentication.
- Never tunnel database ports — MySQL (3306), PostgreSQL (5432), Redis (6379) should NEVER be publicly exposed through a tunnel. Use Cloudflare Access + Warp for admin access if needed.
- Monitor tunnel health — Check the Zero Trust dashboard regularly for connection status, traffic anomalies, and authentication logs.
- Rotate tunnel tokens periodically — If a token is compromised, delete the tunnel and create a new one. The old token is instantly invalidated.
Tunnel vs Traditional Port Forwarding
Here's a comprehensive comparison to understand why tunnels are the modern approach:
| Feature | Traditional (Port Forward) | Cloudflare Tunnel |
|---|---|---|
| Open ports required | Yes (80, 443, custom) | None |
| Static/public IP needed | Yes | No |
| Works behind NAT/CGNAT | No | Yes |
| DDoS protection | Manual setup | Automatic |
| SSL certificate | Manual (Let's Encrypt) | Automatic |
| Firewall configuration | Required | None |
| Router configuration | Port forwarding rules | None |
| Built-in authentication | None | Cloudflare Access |
| Attack surface | Exposed ports | Zero inbound |
| Setup complexity | Moderate | Low (5 minutes) |
| Cost | Free | Free |
Troubleshooting Common Issues
| Problem | Cause | Fix |
|---|---|---|
| 502 Bad Gateway | Local service isn't running or wrong port | Verify the service is running: curl http://localhost:PORT |
| Tunnel shows "inactive" | cloudflared process not running |
Check systemd: systemctl status cloudflared |
| DNS resolution fails | CNAME record not created | Run: cloudflared tunnel route dns TUNNEL_NAME hostname |
| WebSocket connections fail | Missing config | Cloudflare Tunnel supports WebSockets by default — check your app's WebSocket path |
| Slow performance | Tunnel routing through distant edge | Check cloudflared tunnel info — connections should be to nearby locations |
Panelica + Cloudflare Tunnel
While Panelica is designed for dedicated servers and VPS instances with public IP addresses, Cloudflare Tunnel complements it perfectly for specific use cases:
Docker Apps via Tunnel
Running Docker containers on Panelica? Use Cloudflare Tunnel to expose them through custom domains without allocating additional IPs or opening extra ports. Panelica's Docker management + Cloudflare integration makes this seamless.
Dev-to-Production Bridge
Connect development servers behind NAT to production Cloudflare zones. Test on your local machine, expose via tunnel, and share with your team — all through your real domain.
Quick Start Checklist
Here's the fastest path to getting your first tunnel running:
- Sign up for Cloudflare and add your domain (free)
- Go to Zero Trust → Networks → Tunnels → Create
- Install
cloudflaredon your machine with the generated command - Add a public hostname pointing to your local service
- Visit your subdomain — your app is live
- Enable Cloudflare Access for authentication (strongly recommended)
- Add additional hostnames for more services as needed
Cloudflare Tunnel has fundamentally changed how self-hosted services can be exposed to the internet. No more port forwarding nightmares, no more dynamic DNS hacks, no more worrying about DDoS attacks hitting your home IP. Set it up once, and your services are globally available, secured by Cloudflare's infrastructure — all for free.