TL;DR: Cloudflare offers two free VPN solutions: WARP (consumer privacy VPN using WireGuard) and Cloudflare Tunnel + Zero Trust (self-hosted VPN replacement for accessing your home network). This guide covers both approaches step-by-step, with Docker Compose configs, split-tunnel setup, and security hardening. Zero Trust is free for up to 50 users — enough for any homelab or small team.
Why Build Your Own VPN in 2026?
Commercial VPN providers make bold promises about privacy, but their centralized architecture creates a fundamental trust problem. You’re routing all your traffic through servers you don’t control, operated by companies whose revenue model depends on subscriber volume — not security audits. ExpressVPN, NordVPN, and Surfshark have all faced scrutiny over logging practices, jurisdiction shopping, and opaque ownership structures.
Cloudflare offers a different model. Instead of renting someone else’s VPN, you build your own using Cloudflare’s global Anycast network (330+ data centers in 120+ countries) as the transport layer. The result is a VPN that’s faster than most commercial alternatives, costs nothing, and gives you full control over access policies.
There are two distinct approaches, and you might want both:
Cloudflare WARP — A consumer VPN app that encrypts your device traffic using WireGuard. Install, toggle on, done. Best for: browsing privacy on public Wi-Fi.
Cloudflare Tunnel + Zero Trust — A self-hosted VPN replacement that lets you access your home network (NAS, Proxmox, Pi-hole, Docker services) from anywhere without opening a single firewall port. Best for: homelabbers, remote workers, small teams.
Part 1: Cloudflare WARP — The 5-Minute Privacy VPN
What WARP Actually Does
WARP is built on the WireGuard protocol — the same modern, lightweight VPN protocol that replaced IPSec and OpenVPN in most serious deployments. When you enable WARP, your device establishes an encrypted tunnel to the nearest Cloudflare data center. From there, your traffic exits onto the internet through Cloudflare’s network.
Key technical details:
Protocol: WireGuard (via Cloudflare’s BoringTun implementation in Rust)
DNS: Queries routed through 1.1.1.1 (Cloudflare’s privacy-first DNS resolver, audited by KPMG)
Encryption: ChaCha20-Poly1305 for data, Curve25519 for key exchange
Latency impact: Typically 1-5ms added (vs. 20-50ms for most commercial VPNs) because traffic routes to the nearest Anycast PoP
No IP selection: WARP doesn’t let you choose exit countries — it’s a privacy tool, not a geo-unblocking tool
Installation
WARP runs on every major platform through the 1.1.1.1 app:
Platform
Install Method
Windows
one.one.one.one → Download
macOS
one.one.one.one → Download
iOS
App Store → search “1.1.1.1”
Android
Play Store → search “1.1.1.1”
Linux
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-archive-keyring.gpg && echo "deb [signed-by=/usr/share/keyrings/cloudflare-archive-keyring.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list && sudo apt update && sudo apt install cloudflare-warp
After installing, launch the app and toggle WARP on. That’s it. Your DNS queries now go through 1.1.1.1 and your traffic is encrypted to Cloudflare’s edge.
WARP vs. WARP+ vs. Zero Trust
Feature
WARP (Free)
WARP+ ($)
Zero Trust WARP
Price
$0
~$5/month
Free (50 users)
Encryption
WireGuard
WireGuard
WireGuard
Speed optimization
Standard routing
Argo Smart Routing
Standard routing
Private network access
No
No
Yes
Access policies
No
No
Full ZTNA
DNS filtering
No
No
Gateway policies
For most people, free WARP is sufficient for everyday privacy. If you need remote access to your homelab, keep reading — Part 2 is where it gets interesting.
Part 2: Cloudflare Tunnel + Zero Trust — The Self-Hosted VPN Replacement
This is the setup that replaces WireGuard, OpenVPN, or Tailscale for accessing your home network. The architecture is elegant: a lightweight daemon called cloudflared runs inside your network and maintains an outbound-only encrypted tunnel to Cloudflare. Remote clients connect through Cloudflare’s network using the WARP client. No inbound ports. No dynamic DNS. No exposed IP address.
Architecture Overview
javascript
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
│ Remote Device │ │ Cloudflare Edge │ │ Home Network │
│ (WARP Client) │◄───────►│ 330+ PoPs globally │◄───────►│ (cloudflared) │
│ │ WireGuard│ │ Outbound │ │
│ Phone/Laptop │ Tunnel │ Zero Trust Policies │ Tunnel │ NAS/Docker/LAN │
└─────────────────┘ └──────────────────────┘ └─────────────────┘
### Prerequisites
- A **Cloudflare account** (free tier works)
- A **domain name** with DNS managed by Cloudflare (required for tunnel management)
- A server on your home network — any Linux box, Raspberry Pi, Synology NAS, or even a Docker container on TrueNAS
- **Docker + Docker Compose** (recommended) or bare-metal `cloudflared` installation
### Step 1: Create a Tunnel in the Zero Trust Dashboard
- Go to [one.dash.cloudflare.com](https://one.dash.cloudflare.com/) → Networks → Tunnels
- Click **Create a tunnel**
- Select **Cloudflared** as the connector type
- Name your tunnel (e.g., `homelab-tunnel`)
- Copy the **tunnel token** — you’ll need this for the Docker config
### Step 2: Deploy cloudflared with Docker Compose
Create a `docker-compose.yml` on your home server:
sql
`version: "3.8"
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared-tunnel
restart: unless-stopped
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
environment:
- TUNNEL_TOKEN=${TUNNEL_TOKEN}
network_mode: host # Required for private network routing
# Example: expose a local service
whoami:
image: traefik/whoami
container_name: whoami
ports:
- "8080:80"`
Create a .env file alongside it:
`TUNNEL_TOKEN=eyJhIjoiYWJj...your-token-here`
Start the tunnel:
`docker compose up -d
docker logs cloudflared-tunnel # Should show "Connection registered"`
Critical note: Use network_mode: host if you want to route traffic to your entire LAN subnet (192.168.x.0/24). Without it, cloudflared can only reach services within the Docker network.
Step 3: Expose Services via Public Hostnames
Back in the Zero Trust dashboard, under your tunnel’s Public Hostnames tab:
Click Add a public hostname
Set subdomain:
nas, domain:yourdomain.comService type:
HTTP, URL:localhost:5000(or wherever your service runs)Save
Cloudflare automatically creates a DNS record. Your NAS is now accessible at https://nas.yourdomain.com — with automatic SSL, DDoS protection, and Cloudflare WAF.
Step 4: Enable Private Network Routing (Full VPN Mode)
This is what turns a simple tunnel into a full VPN replacement. Instead of exposing individual services, you route an entire IP subnet through the tunnel.
In Zero Trust dashboard → Networks → Tunnels → your tunnel → Private Networks
Add your LAN CIDR:
10.0.0.x/24(adjust to your subnet)Go to Settings → WARP Client → Split Tunnels
Switch to Include mode and add
10.0.0.x/24
Now, any device running the WARP client (enrolled in your Zero Trust org) can access 192.168.1.x addresses as if they were on your home network. SSH into your server, access your NAS web UI, reach your Pi-hole dashboard — all without port forwarding.
Step 5: Enroll Client Devices
Install the 1.1.1.1 / WARP app on your phone or laptop
Go to Settings → Account → Login to Cloudflare Zero Trust
Enter your team name (set during Zero Trust setup)
Authenticate with the method you configured (email OTP, Google SSO, GitHub, etc.)
Enable Gateway with WARP mode
Test it: connect to mobile data (not your home Wi-Fi) and try accessing a LAN IP like http://10.0.0.x. If the router admin page loads, your VPN is working.
Step 6: Lock It Down — Zero Trust Access Policies
The “Zero Trust” part of this setup is what separates it from a traditional VPN. Instead of “anyone with the VPN key gets full network access,” you define granular policies:
bash
`Zero Trust Dashboard → Access → Applications → Add an Application
Application type: Self-hosted
Application domain: nas.yourdomain.com
Policy: Allow
Include: Emails ending in @yourdomain.com
Require: Country equals United States (optional geo-fence)
Session duration: 24 hours`
You can create different policies per service. Your Proxmox admin panel might require hardware key (FIDO2) authentication, while your Jellyfin media server only needs email OTP. This is **Zero Trust Network Access (ZTNA)** — the same architecture that Google BeyondCorp and Microsoft Entra use internally.
## Cloudflare Tunnel vs. Alternatives: Honest Comparison
Feature
Cloudflare Tunnel
WireGuard
Tailscale
OpenVPN
Price
Free (50 users)
Free
Free (100 devices)
Free
Open ports required
None
1 UDP port
None
1 UDP/TCP port
Setup complexity
Medium
Medium-High
Low
High
Works behind CG-NAT
Yes
Needs port forward
Yes
Needs port forward
Access control
Full ZTNA policies
Key-based only
ACLs + SSO
Cert-based
DDoS protection
Yes (Cloudflare)
No
No
No
SSL/TLS termination
Automatic
N/A
N/A
Manual
Trust model
Trust Cloudflare
Self-hosted
Trust Tailscale
Self-hosted
Best for
Web services + LAN
Pure privacy
Mesh networking
Enterprise legacy
**The honest tradeoff**: Cloudflare Tunnel routes your traffic through Cloudflare’s infrastructure. If you fundamentally distrust any third party touching your packets, self-hosted WireGuard is the purist choice. But for most homelabbers, the convenience of zero open ports + free DDoS protection + granular access policies makes Cloudflare Tunnel the pragmatic winner.
## Advanced: Multi-Service Docker Stack
Here’s a production-grade Docker Compose that exposes multiple services through a single tunnel:
sql
`version: "3.8"
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
environment:
- TUNNEL_TOKEN=${TUNNEL_TOKEN}
networks:
- tunnel
depends_on:
- nginx
nginx:
image: nginx:alpine
container_name: nginx-proxy
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks:
- tunnel
# Add your services here — they just need to be on the 'tunnel' network
# Configure public hostnames in the CF dashboard to point to nginx
networks:
tunnel:
name: cf-tunnel`
Map each service to a subdomain in the Zero Trust dashboard: grafana.yourdomain.com → http://nginx:3000, code.yourdomain.com → http://nginx:8443, etc.
Troubleshooting Common Issues
Tunnel shows “Disconnected” in the dashboard
Check Docker logs:
docker logs cloudflared-tunnelVerify your token hasn’t been rotated
Ensure outbound HTTPS (port 443) isn’t blocked by your router/ISP
If behind a corporate firewall,
cloudflaredalso supports HTTP/2 over port 7844
Private network routing doesn’t work
Confirm
network_mode: hostin Docker Compose (or use macvlan)Check that the CIDR in “Private Networks” matches your actual subnet
Verify Split Tunnels are set to Include mode (not Exclude)
On the client, run
warp-cli settingsto verify the private routes are active
WARP client won’t enroll
Double-check your team name in Zero Trust → Settings → Custom Pages
Ensure you’ve created a Device enrollment policy under Settings → WARP Client → Device enrollment permissions
Allow email domains or specific emails that can enroll
Security Hardening Checklist
☐ Enable Require Gateway in device enrollment — forces all enrolled devices through Cloudflare Gateway for DNS filtering
☐ Set session duration to 24h or less for sensitive services
☐ Require FIDO2/hardware keys for admin panels (Proxmox, router, etc.)
☐ Enable device posture checks: require screen lock, OS version, disk encryption
☐ Use Service Tokens (not user auth) for machine-to-machine tunnel access
☐ Monitor Access audit logs: Zero Trust → Logs → Access
☐ Never put your tunnel token in a public Git repository — use
.envfiles and.gitignore☐ Rotate tunnel tokens periodically via the dashboard
FAQ
Is Cloudflare Tunnel really free?
Yes. Cloudflare Zero Trust offers a free plan that includes tunnels, access policies, and WARP client enrollment for up to 50 users. There are no bandwidth limits on the free tier. Paid plans (starting at $7/user/month) add features like logpush, extended session management, and dedicated egress IPs.
Can Cloudflare see my traffic?
Cloudflare terminates TLS at their edge, so they technically could inspect unencrypted HTTP traffic passing through the tunnel. For HTTPS services, end-to-end encryption between your browser and origin server means Cloudflare sees metadata (domain, timing) but not content. If this is a concern, use WireGuard for a fully self-hosted solution where no third party touches your packets.
Does this work with Starlink / CG-NAT / mobile hotspots?
Yes — this is one of Cloudflare Tunnel’s biggest advantages. Since the tunnel is outbound-only, it works behind any NAT, including carrier-grade NAT (CG-NAT) used by Starlink, T-Mobile Home Internet, and most 4G/5G connections. No port forwarding needed.
Can I use this for site-to-site VPN?
Yes, using WARP Connector (currently in beta). Install cloudflared with WARP Connector mode on a device at each site, and Cloudflare routes traffic between subnets. This replaces traditional IPSec site-to-site tunnels.
Cloudflare Tunnel vs. Tailscale — which should I use?
Use Tailscale if your primary need is device-to-device mesh networking (see also our guide on home network segmentation with OPNsense) (accessing any device from any other device). Use Cloudflare Tunnel if you want to expose web services with automatic HTTPS and DDoS protection, or if you need granular ZTNA policies. Many homelabbers use both: Tailscale for device mesh, Cloudflare Tunnel for public-facing services.
References
Cloudflare. “Connect private networks.” Cloudflare One Documentation, 2026.
Cloudflare. “Cloudflare Tunnel setup guide.” Cloudflare Developers, 2026.
Donenfeld, Jason A. “WireGuard: Next Generation Kernel Network Tunnel.” NDSS Symposium, 2017.
Cloudflare. “Introducing WARP: Fixing Mobile Internet Performance and Security.” Cloudflare Blog, 2019.
Ward, Brendan and Harris, Rustam. “BoringTun: a userspace WireGuard implementation in Rust.” Cloudflare Blog, 2019.
Google. “BeyondCorp: A New Approach to Enterprise Security.” Google Cloud, 2014.
Top comments (0)