This is the first in a series of blog posts outlining how I have my lab configured for the purposes of leveraging on-prem resources (included AI models) from the public Internet. A core piece of technology I use for stitching all of my resources together (on and off-prem) is the Cloudflare Tunnel. This post is going through Cloudflare Tunnel setup that makes getting up and running quick and easy.
There are a few resources that are needed in order to get this setup and working.
- Cloudflare account: free
- Custom domain: jhgfdsa.com
- DNS configured via Cloudflare
- On-prem resource
- For this blog post, we’re just going to host a really simple website
- Docker installed
One of the biggest advantages of using Cloudflare Tunnel is that you don’t need to open any inbound ports on your firewall. The cloudflared daemon establishes an outbound-only connection to Cloudflare’s edge servers, which means your internal services remain completely hidden from direct internet access while still being publicly accessible through the tunnel. This significantly reduces your attack surface without requiring any firewall configuration changes โ pretty slick.
Additional Resources:
Table of Contents
Step 1: Test Webpage
I have a simple web page:
<!DOCTYPE html>
<html>
<head>
<title>Demo Site</title>
</head>
<body>
<div>
This is a test site for the purposes of the blog post.
</div>
</body>
</html>
And, I am hosting the site with a simple Python command:
python3 -m http.server 8080&
I have this entire install hosted on VMs. When I connect to the site from my host machine, I can see the site loads:

Step 2: Domain and Cloudflare
I already have a Cloudflare account and have my domain’s DNS being managed via Cloudflare:

If you do not have this, there are plenty of resources available to help you purchase a domain and have DNS pointed to Cloudflare. In fact, I believe you can even purchase a cheap domain via Cloudflare itself if needed.
Step 3: Create the tunnel
Within the Cloudflare portal, find Zero Trust in the left hand navigation

And then:

And here we can see we have tunnel configurations. Ignore my existing tunnel – we are going to create a new one by scratch.
Hit ‘Create Tunnel’

We’re going to go with Cloudflared

Give it a name and save the tunnel.

Select Docker and then copy the command
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token eyJhIjoiMz...
The copied command will have the full JWT. Paste into your terminal on your target machine – in this case, I am going to run it on the machine where I am hosting my webpage:

First, you will see Docker pulling the containers. If all goes well, you should see the connected tunnel:

Unfortunately, this won’t quite work for us. Since the tunnel is running within a docker container, accessing localhost/127.0.0.1 URLs on the host (where we are hosting our simple page) can be a challenge. There are several ways to make this work, but let’s go easy mode and tweak our docker command slightly:
docker run --network host cloudflare/cloudflared:latest tunnel --no-autoupdate run --token eyJhIjoiMz
Notice I added ‘–network host’ to the command. This allows the container to share the host’s network stack. It’s maybe a bit lazy mode, but it does the trick for the purposes of the lab.
Now, back in Cloudflare:

We can see the tunnel is connected. Hit Next and let’s configure the tunnel
Step 4: Configure Tunnel

Setting up the route – as long as you have your domain registered it will be available under ‘Domain’. From there, Subdomain and Path are optional but I am going to configure them here just to show they work. For the service config, this is how/where your on-prem hosted end of your tunnel should find a service it is going to serve up.
Since I am just running a simple page via python on 8080, 127.0.0.1:8080 via HTTP will work.
NOTE: Sometimes using localhost instead of 127.0.0.1 will cause the tunnel to try to use IPv6 which can be problematic depending on config. 127.0.0.1 forces IPv4.
Hit Complete Setup
NOTE: Once you have a tunnel established, you can route multiple services through the same tunnel connection. In the Cloudflare portal, just add additional “Public Hostnames” to your existing tunnel โ each with different subdomains or paths pointing to different internal services. For example, you could have blog.yourdomain.com pointing to your web server on port 8080, nas.yourdomain.com pointing to your NAS interface on port 5000, and homelab.yourdomain.com pointing to another service entirely. One tunnel, multiple services โ efficient and clean.
Step 5: Test

Bam! We’re live.
Step 6: Configure Tunnel to Always Run
docker run -d \
--restart unless-stopped \
--network host \
cloudflare/cloudflared:latest tunnel --no-autoupdate run --token eyJhIjoiMz...
We just need a few more tweaks to our docker command. First, –restart unless-stopped means that Docker will always restart our tunnel container. Second, -d so that our command detaches from the container and just lets it run:

And our site is still up!

Bonus: TLS/SSL Issue
If your on-prem service is not running HTTP but rather is running HTTPS, then the cert needs to be good. What if we don’t have a cert and don’t want to mess with it?
Back in the Cloudflare portal, here’s where we would have pointed to a local service that was running on HTTPS rather than HTTP:

After selecting HTTPS, the Advanced Settings will have additional options available:

TLS -> No TLS Verify -> On
This is what needs to be configured to allow the Cloudflare tunnel to ignore certificate errors and present the on-prem service. The good news here, however, is that Cloudflare is going to manage the cert for your public domain. So, you will access your service via HTTPS (https://jhgfdsa.com) and your traffic will be encrypted all the way to on-prem anyway.
Conclusion
The Cloudflare tunnel is a really nice tool to have in your back pocket for presenting on-prem hosted services to the public Internet. Of course, with any of these services make sure you are implementing appropriate security controls to avoid misuse/abuse.

