One of the main goals behind Realm Labs is to run as much as possible on infrastructure I control.
That naturally led to an interesting question:
Could I host the Realm Labs website from my own homelab without exposing the server — or even an inbound port — directly to the internet?
The answer was Cloudflare Tunnel.
The finished setup runs WordPress locally in Docker while Cloudflare provides the secure public route to realmlabs.uk.
The basic architecture is:
Visitor
|
HTTPS
|
Cloudflare
|
Encrypted outbound tunnel
|
Realm Labs network
|
cloudflared
|
WordPress container
No inbound port forwarding is required.
What I Wanted to Achieve
The objectives were fairly simple:
- Host WordPress inside my own network
- Run the site using Docker
- Publish it using the
realmlabs.ukdomain - Provide HTTPS externally
- Avoid forwarding ports through the home router
- Keep the public-facing service separated from the rest of the homelab
WordPress itself was already running locally, so the main challenge was publishing it safely.
The Local WordPress Environment
Before making the site public, WordPress was running internally in Docker and could only be reached from inside the Realm Labs network.
The WordPress service was available on:
http://internal-wordpress-host:8082
That was fine while building the site, but obviously wasn’t useful to anyone outside the house.
The traditional solution would have been something like:
Internet
|
Router
|
Port Forward
|
WordPress Server
That would mean deliberately opening an inbound route through the router and then managing the security of that exposed service.
I wanted to avoid that altogether.
Why Cloudflare Tunnel?
Cloudflare Tunnel works differently.
Instead of accepting an inbound connection from the internet, cloudflared establishes an outbound connection from inside the network to Cloudflare.
Visitors connect to Cloudflare, and Cloudflare carries the request through that established tunnel to the internal service.
The resulting path becomes:
Visitor
|
HTTPS
|
Cloudflare Edge
|
Secure Cloudflare Tunnel
|
cloudflared
|
WordPress
The home router therefore doesn’t need an inbound port forwarded to the WordPress server.
That was the main reason I chose it.
It also gave me:
- Cloudflare-managed HTTPS at the public edge
- No conventional HTTP/HTTPS port forwarding
- A simple way to map the public hostname to an internal service
- An additional separation between the public website and the internal network
The Infrastructure Behind It
The website isn’t running on an isolated server in the corner of the house. It sits inside the wider Realm Labs infrastructure.
The core equipment had already been moved into a dedicated garage rack, with the lab connected using wired Gigabit Ethernet.
The network includes:
- TP-Link Deco X55 routing and mesh infrastructure
- Ethernet backhaul between Deco units
- HP ProCurve 2810-24G managed switching
- Docker-hosted services
- Synology-backed storage and other internal infrastructure
The managed switch also gives me the ability to segment services rather than treating every device in the homelab as part of one completely flat network.
That’s particularly useful when hosting something that is deliberately accessible from the public internet.
The website may be public.
The rest of the lab doesn’t need to be.
Creating the Cloudflare Tunnel
The tunnel itself was created through Cloudflare Zero Trust.
I created a new tunnel called:
RealmLabs
Cloudflare then generated the credentials required by cloudflared to establish the connection.
Rather than installing cloudflared directly onto the host, I chose to run it as another Docker container.
That keeps it consistent with the rest of the self-hosted environment and makes it easy to manage alongside the other services.
Running cloudflared in Docker
A minimal Docker Compose configuration looks like this:
version: "3.8"
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run --token YOUR_TUNNEL_TOKEN
Once deployed, the container establishes the outbound tunnel automatically.
The container logs are useful here because they provide a quick way to confirm that the tunnel has successfully connected to Cloudflare.
Protect the Tunnel Token
The example above shows the token directly for clarity.
For a real deployment, I wouldn’t keep a live Cloudflare tunnel token inside a Compose file that might later end up in source control or documentation.
Treat the token as a credential.
Use an environment variable, secret or other protected configuration method where possible.
Publishing realmlabs.uk
With the tunnel online, the next step was creating a public hostname in Cloudflare Zero Trust.
For Realm Labs the route was effectively:
Public hostname:
realmlabs.uk
Service:
HTTP
Internal destination:
internal-wordpress-host:8082
Cloudflare now knew that requests for:
https://realmlabs.uk
should be sent through the tunnel to the internal WordPress service.
This was the point where the locally hosted site became publicly reachable.
WordPress Still Thought It Was Local
Getting traffic through the tunnel was only part of the job.
WordPress had originally been configured using its internal HTTP address.
That meant it could still generate links and asset URLs based on the old local address, which isn’t what you want when visitors are accessing the site through:
https://realmlabs.uk
The WordPress URL settings therefore needed to match the public address.
Both were changed to:
WordPress Address:
https://realmlabs.uk
Site Address:
Realm Labs Home
After updating those values, WordPress began generating URLs using the correct HTTPS domain.
This fixed the mismatch between the internal Docker service and the public-facing website.
The Finished Architecture
The finished Realm Labs website now looks broadly like this:
Internet
|
|
Cloudflare Edge
|
|
Secure outbound tunnel
|
|
Realm Labs Network
|
cloudflared
|
|
WordPress :8082
in Docker
Behind that sits the wider physical infrastructure:
TP-Link Deco X55
|
Ethernet backbone
|
HP ProCurve 2810-24G
|
Homelab infrastructure
|
Docker services
The important distinction is that the website is being published through the Cloudflare Tunnel rather than through a conventional inbound router port forward.
Why I Prefer This Setup
The part I like most is that WordPress can remain an internal service from the network’s point of view.
I don’t need to make port 80 or 443 on the WordPress host directly reachable from the internet.
The public entry point is Cloudflare.
The tunnel connection originates from inside Realm Labs.
And the rest of the infrastructure can remain separated from the service being published.
It also fits neatly with how the rest of the lab is built: Docker services remain easy to move, rebuild and manage without changing the external URL visitors use.
What This Project Led To
Publishing Realm Labs became more than simply putting WordPress on the internet.
It tied together several parts of the homelab:
- Docker and Portainer
- Local networking
- Managed switching
- Cloudflare Zero Trust
- DNS
- HTTPS
- Network segmentation
- Self-hosted infrastructure
And, appropriately enough, the website documenting the homelab is itself running from the homelab.
Related Realm Labs Guides
If you’re exploring the infrastructure behind this setup, these are useful next steps:
- The Realm Labs Homelab — an overview of the infrastructure powering the site
- Self-Hosting at Home with Docker and Portainer
- Designing the Realm Labs Home Network
- Building the Storage Backbone with Synology
- Secure Remote Access with WireGuard
The wider infrastructure is covered in the Realm Labs Homelab project hub.

