TL;DR Summary
Cloudflare Tunnel (formerly Argo Tunnel) eliminates the need to open ports on your router or deal with dynamic DNS services. One small daemon runs on your server, creates an outbound connection to Cloudflare’s edge, and your services are accessible via HTTPS — no router configuration needed. I’ve been running it for 8 months across 6 services with zero maintenance.
The Problem with Traditional Remote Access
Most guides for accessing your homelab from outside your house go like this: set up port forwarding on your router, configure dynamic DNS because your ISP gives you a dynamic IP, cross your fingers that your IP doesn’t change while you’re away, and pray that your router’s firewall is solid enough.
Then you spend an hour setting up a VPN because you realized port forwarding your NAS is a terrible idea. Now you have two services to maintain and your phone is configured for both.
This is the path I walked. It works — but it’s fragile. Your public IP changes, the dynamic DNS update lag leaves you stranded, and every open port is a potential attack surface.
Then there’s the security problem: anything you expose is directly reachable from the internet. You can add fail2ban, rate limiting, and VPN-only access — but you’re still maintaining a moving pile of security layers.
Cloudflare Tunnel changes the model entirely.
How Cloudflare Tunnel Works
Instead of opening ports and hoping for the best, you install a tiny daemon (cloudflared) on your server. This daemon creates a persistent outbound WebSocket connection to Cloudflare’s edge network.
When someone tries to access your service, they hit your domain — which resolves to Cloudflare’s nearest edge node. Cloudflare routes the request through the tunnel’s established connection to your server, which sends the response back the same way.
The key detail: your server never accepts incoming connections from the internet. The outbound tunnel connection is initiated from inside your network. From the outside world, your server is invisible.
Step 1: Create a Cloudflare Account and Add Your Domain
If you don’t have a Cloudflare account, create one at dash.cloudflare.com. It’s free.
Add your domain — Cloudflare will give you nameservers to update at your registrar. This takes effect within a few hours typically.
Once your domain is active in Cloudflare, go to SSL/TLS → Overview and set encryption mode to Full (strict). This ensures all connections are encrypted end-to-end.
Step 2: Install cloudflared on Your Server
cloudflared is the daemon that manages your tunnel. It’s a single binary with no dependencies:
# Download the latest release (check https://github.com/cloudflare/cloudflared/releases)
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -O cloudflared
chmod +x cloudflared
sudo mv cloudflared /usr/local/bin/
# Verify it works
cloudflared --version
For Docker, you can also run it as a container:
services:
cloudflared:
image: cloudflare/cloudflared:latest
restart: unless-stopped
command: tunnel run --token YOUR_TOKEN_HERE
environment:
- TZ=America/New_York
network_mode: host
Step 3: Create Your First Tunnel
Log into your Cloudflare dashboard and go to Zero Trust → Networks → Tunnels. Click Create a tunnel and choose Cloudflared as the connector.
Give your tunnel a name — something like homelab or outpost. Copy the tunnel token shown; you’ll need it in the next step.
On your server:
cloudflared service install YOUR_TUNNEL_TOKEN
This registers the tunnel and starts the service. You can also run it manually:
cloudflared tunnel run --token YOUR_TUNNEL_TOKEN
Step 4: Route Services Through the Tunnel
Back in the Cloudflare dashboard, under your tunnel’s Public Hostname tab, add the services you want to expose.
For example, to expose a service running on your LAN at http://192.168.1.100:8123:
| Field | Value |
|---|---|
| Subdomain | homeassistant |
| Domain | yourdomain.com |
| Type | HTTP |
| URL | 192.168.1.100:8123 |
That’s it. Within seconds, https://homeassistant.yourdomain.com will route to your local Home Assistant instance. No router changes. No port forwarding.
Repeat for each service you want accessible:
| Service | Subdomain | Local URL |
|---|---|---|
| Home Assistant | ha |
192.168.1.100:8123 |
| Plex | plex |
192.168.1.101:32400 |
| AdGuard | dns |
192.168.1.50:53 |
| Jellyfin | media |
192.168.1.102:8096 |
| Uptime Kuma | monitor |
192.168.1.60:3001 |
Step 5: Lock Down with Access Policies
By default, your tunneled services are accessible to anyone who knows the URL. Cloudflare Access (free tier includes this) lets you add authentication.
Go to Zero Trust → Settings → Authentication and connect a login provider. For personal use, the easiest is adding your Google account or GitHub account as an identity provider.
Then create an Access Policy:
{
"name": "Protect Homelab",
"include": [
{
"kind": "include",
"email": {
"domain": "yourdomain.com"
}
}
]
}
Now when anyone visits https://ha.yourdomain.com, they’re redirected to a Cloudflare login page. Only authenticated users in your policy can proceed — and Cloudflare handles the session token.
This is significantly more secure than opening a port and relying on your service’s built-in auth. Even if Home Assistant has a zero-day, attackers can’t reach it without bypassing Cloudflare’s authentication first.
Step 6: Enable Automatic HTTPS Rewrites
Cloudflare can automatically fix mixed content issues when your internal services use HTTP but the external connection is HTTPS.
Go to SSL/TLS → Automatic HTTPS Rewrites and turn it On. Cloudflare will rewrite HTTP links in your responses to HTTPS so you don’t get mixed content warnings.
What I’ve Been Running
My current setup serves 6 services through a single tunnel on a $6/month VPS:
# Uptime Kuma monitoring
homeassistant.yourdomain.com → 192.168.1.100:8123
plex.yourdomain.com → 192.168.1.101:32400
jellyfin.yourdomain.com → 192.168.1.102:8096
monitor.yourdomain.com → 192.168.1.50:3001
dns.yourdomain.com → 192.168.1.50:53
git.yourdomain.com → 192.168.1.60:3000
All behind Cloudflare Access with Google OAuth. I’ve had zero unauthorized access attempts since removing the port forwards — the public internet simply can’t reach my services directly.
Troubleshooting
Tunnel won’t connect after router reboot:
cloudflared reconnect automatically. If it doesn’t, sudo systemctl restart cloudflared.
Service times out on mobile (works on WiFi):
Cloudflare’s HTTP/2 multiplexing can sometimes drop idle connections. In your tunnel config, set cloudflared to use HTTP/3: cloudflared tunnel run --url http://localhost:8080 --protocol http3.
SSL certificate warnings: Make sure your local service either has a valid certificate or is using HTTP (Cloudflare will handle the TLS termination at the edge). In Cloudflare SSL settings, set SSL to Flexible if your internal service is HTTP-only, or better yet, use a self-signed cert and set SSL to Full.
High latency on tunneled services: Cloudflare routes traffic through their nearest edge node. If you’re far from Cloudflare’s nearest datacenter, latency increases. For basic web access this is imperceptible; for real-time media streaming it can matter.
Key Takeaways
- Zero open ports — your server is invisible to the internet
- Automatic HTTPS — even if your local services run HTTP
- Built-in DDoS protection — Cloudflare absorbs attacks before they reach your network
- Access policies — add authentication without configuring it in every service
- Dead simple — set it up once and forget it
If you’re running any service you access remotely, Cloudflare Tunnel is the first thing to set up. It’s more secure than any port forwarding setup and requires less ongoing maintenance.
No dynamic DNS. No router ACLs. No worrying about your public IP.