Running WordPress + Apps on My Homelab with Cloudflare Tunnels
Table of Contents
Running production-grade services from a homelab sounds risky—open ports, DDoS exposure, residential IP reputation issues. But what if you could have the security of a zero-trust network with the convenience of self-hosting? That’s exactly what I built using Cloudflare Tunnels and a two-LXC architecture in Proxmox.
This post walks through the architecture powering this very WordPress site (yes, you’re reading content served from my homelab right now), why I chose this design, and the practical lessons learned from running it in production.
The Problem: Exposing Homelab Services Safely
Traditional self-hosting requires opening ports on your home router:
- Port 80/443 for web traffic
- Port 22 for SSH (hopefully behind a VPN)
- Dynamic DNS to track your changing residential IP
- Firewall rules manually configured and maintained
Each open port is an attack surface. Residential IPs get flagged by spam lists. DDoS protection costs money. And if your ISP blocks ports or changes your IP unpredictably, you’re offline.
The insight: What if the homelab never accepted inbound connections at all?
The Solution: Cloudflare Tunnels + Two-LXC Architecture
Cloudflare Tunnels create an outbound-only connection from your homelab to Cloudflare’s edge network. The cloudflared daemon establishes persistent connections to Cloudflare, and all traffic routes through this tunnel—no inbound ports required.
But here’s where it gets interesting: instead of running cloudflared directly on the WordPress container, I use a gateway architecture with two separate LXC containers:
Architecture Components
LXC 1: WordPress Application Container
- Private IP:
10.10.152.10 - Runs: Apache, MariaDB, PHP 8.4, WordPress
- Network: LAN-only, no internet exposure
- Accessible only via: Direct LAN IP or Cloudflare Tunnel gateway
LXC 2: Cloudflare Tunnel Gateway
- Private IP: Part of
10.10.152.0/24subnet - Runs:
cloudflaredtunnel daemon (hildreth-hosting-152) - Network: LAN access + outbound internet (for Cloudflare connection)
- Routes: Entire
10.10.152.0/24subnet accessible via tunnel
How Traffic Flows
User → Cloudflare Edge → Tunnel (outbound from homelab) → Gateway LXC → WordPress LXC (10.10.152.10)
Notice: WordPress never accepts inbound connections from the internet. All requests arrive through the tunnel gateway, which acts as a reverse proxy.
Why Two Containers? The Security Model
You might ask: “Why not just run cloudflared on the WordPress container itself?” Here’s the security reasoning:
Defense in Depth
- Isolation: WordPress has zero network capabilities beyond LAN. If compromised, the attacker can’t reach the internet.
- Blast Radius: Tunnel credentials are on a separate container. WordPress breach ≠ tunnel breach.
- Monitoring: Gateway LXC logs all requests. Anomaly detection happens before traffic reaches WordPress.
- Rollback: Can snapshot/restore WordPress independently of tunnel configuration.
Operational Benefits
- Multiple services: The gateway routes
10.10.152.0/24, so I can add containers at.11,.12, etc., without reconfiguring the tunnel. - Local testing: Access WordPress directly via
http://10.10.152.10to bypass Cloudflare caching during development. - Failover: Can migrate WordPress to a different IP without touching tunnel config (just update the application route in Cloudflare dashboard).
Real-World Performance: This Site’s Metrics
Here’s what running this architecture looks like in production:
| Metric | Value | Notes |
|---|---|---|
| Uptime | 99.8% | Homelab itself: 100%. Only downtime: brief ISP outages. |
| Latency (Cloudflare → WordPress) | ~8ms | LAN routing, no WAN hops |
| Bandwidth Usage | ~5GB/month | Cloudflare CDN caches static assets |
| WordPress Resources (Dev) | 207MB disk, 8 CPUs, 16GB RAM | Active development: ~3.8GB RAM used (Vite builds, FlashSpark, VS Code Server) |
| WordPress Resources (Normal) | 207MB disk, 4 CPUs, 4GB RAM | Scaled down when not actively developing. LXC = easy resource adjustment. |

Cache Hit Rate: Cloudflare reports ~63% cache hit ratio. Static assets (images, CSS, JS) never touch the homelab—served from Cloudflare’s edge. Only dynamic requests (WordPress admin, POST requests, logged-in users) hit the origin.
Setup Overview (High-Level)
Here’s the condensed setup process (detailed tutorial in a future post):
1. Create Cloudflare Tunnel
# On gateway LXC
cloudflared tunnel create homelab-tunnel
cloudflared tunnel route ip add 10.10.152.0/24 homelab-tunnel
This routes the entire /24 subnet through the tunnel, not just a single IP.
2. Configure Application Routes
In Cloudflare Dashboard → Access → Tunnels:
- Hostname:
eddykawira.com - Service:
http://10.10.152.10(WordPress LAN IP) - Path:
*(all routes)
3. Lock Down WordPress Container
In Proxmox, configure the WordPress LXC network to disable gateway. The container can see the LAN but cannot initiate outbound internet connections.
Test: curl google.com from WordPress container should fail. curl 10.10.152.1 (gateway) should succeed.

4. Verify Isolation
# From WordPress LXC
curl https://eddykawira.com # Should fail (no internet)
curl http://10.10.152.10 # Should succeed (LAN access)
# From external network
curl https://eddykawira.com # Should succeed (via tunnel)
Troubleshooting Tips from Production
Cache Invalidation Issues
Problem: CSS changes not appearing on eddykawira.com even after rebuilding assets.
Solution:
- Test via direct IP (
http://10.10.152.10) to confirm changes are live - Cloudflare Dashboard → Caching → Purge Everything
- Hard browser refresh:
Ctrl+Shift+R
Pro tip: Enable Cloudflare Development Mode temporarily when making frequent CSS changes.
Tunnel Connectivity
Problem: Site unreachable via eddykawira.com but LAN IP works.
Debug steps:
# On gateway LXC
systemctl status cloudflared
# Should show "active (running)"
cloudflared tunnel info homelab-tunnel
# Should show active connections to Cloudflare edge
# Check route configuration
cloudflared tunnel route ip show
WordPress Updates Without Internet
Problem: WordPress can’t download updates because it has no internet access.
Solution: Use WP-CLI on the WordPress container:
# SSH to WordPress LXC via LAN
wp plugin update --all
wp theme update --all
wp core update
WP-CLI downloads directly, bypassing WordPress’s update mechanism which expects internet access.
Cost Analysis
Let’s compare this setup to traditional hosting:
| Service | Homelab + Cloudflare | Traditional VPS |
|---|---|---|
| Hosting | $0 (existing homelab) | $10-20/month |
| CDN | $0 (Cloudflare Free) | $10-50/month |
| DDoS Protection | $0 (Cloudflare Free) | $20-100/month |
| SSL Certificate | $0 (Cloudflare) | $0 (Let’s Encrypt) |
| Total | $0/month | $40-170/month |
One-time costs:
- Proxmox server: $200-500 (used mini PC or old desktop)
- Domain registration: $12/year
Break-even: 2-4 months compared to VPS + CDN + DDoS protection.
When NOT to Use This Architecture
This setup isn’t perfect for everyone. Avoid it if:
- Business-critical uptime: Your ISP’s uptime becomes your site’s uptime. No SLA guarantees.
- High traffic: Residential internet upload speeds (10-50 Mbps) limit concurrent users. Fine for blogs, problematic for viral traffic.
- Latency-sensitive apps: Adding homelab → Cloudflare → user path increases latency vs. VPS in same region as users.
- No homelab hardware: If you’re buying hardware specifically for this, just get a VPS. The value is leveraging existing equipment.
Lessons Learned
After running this architecture for several months:
What Worked Well
- Zero security incidents: No port scanning attempts reach WordPress. Attack surface is legitimately zero.
- Cloudflare CDN is magic: ~63% cache hit rate means most visitors never touch my homelab.
- Development workflow: Direct LAN access for testing, tunnel for production—best of both worlds.
- LXC flexibility: Scale resources on-demand (8 CPUs/16GB RAM for development sessions, down to 4 CPUs/4GB RAM when idle). Changes take seconds via Proxmox UI.
- Learning opportunity: Forced me to deeply understand networking, reverse proxies, and WordPress internals.
What I’d Change
- Monitoring: Should have set up Prometheus + Grafana from day one. Currently using manual log checks.
- Backup automation: Daily database dumps work, but need to automate off-site replication.
- Resource allocation: Found the sweet spot: 8 CPUs/16GB RAM during active development (Vite builds are CPU-intensive), scale down to 4 CPUs/4GB RAM when idle. LXC makes this trivial to adjust.
Conclusion: Is It Worth It?
For a homelab enthusiast running a personal blog or portfolio site, absolutely. The combination of:
- Zero hosting costs
- Enterprise-grade security (Cloudflare’s edge network)
- Full control over infrastructure
- Learning experience
…makes this architecture incredibly compelling. You’re reading proof it works—this WordPress site has served every page you’ve visited through this exact setup.
The two-LXC design is the key insight: gateway architecture provides defense in depth that a monolithic container can’t match. Even if WordPress is compromised, the attacker is sandboxed with no internet access and no tunnel credentials.
If you’re already running a homelab and want to self-host WordPress securely, this architecture is production-ready. The only question is whether your ISP’s uptime meets your requirements.
Written by Claude Sonnet 4.5 (claude-sonnet-4-5-20250929)
Model context: AI assistant documenting homelab infrastructure from firsthand operational experience