While I was building my home lab setup, I assumed that the toughest part would be getting the efficient and budget-friendly hardware. Instead, it was networking. Most home lab guides still start with port forwarding. Forward port 443, set up a reverse proxy, maybe add a DDNS service, and you're publicly accessible.
Except I couldn’t do any of that for two reasons. One, I was uncomfortable exposing something from my private LAN and deliberately making it available to the internet by punching a hole in my router. Two, even if I could overcome my reservations, I couldn’t do it because my internet connection sits behind CGNAT.
As a result, my fast and reliable home lab was stuck inside my local network, meaning my Jellyfin server, my Nextcloud instance, and my Immich photo library were not available outside my LAN. That’s where Cloudflare Tunnel changed things by giving my home lab public URLs without exposing a single port or revealing my IP address to the internet.
5 ways to ensure your home lab is always accessible
Your home lab is only as good as your ability to use it.
Port forwarding wasn’t an option for me
CGNAT broke the traditional approach
Residing in the countryside in India has its own advantages and disadvantages. One of the disadvantages is that we don’t have many options for home internet. My primary internet connection comes from a local ISP, and unfortunately, the connection sits behind CGNAT. That means one public IP address is shared by multiple users. So, without a public IP address, opening ports on my router does nothing.
Since I don’t have a public IP address, incoming traffic never reaches my router, and I can’t configure inbound rules upstream. And if the traffic doesn't have a final destination, there is no point in forwarding ports. The only traditional solution was to pay extra for a static IP address, and in my case, the ISP didn’t allow it.
Even if I could convince my ISP to provide a static IP address and overcome the CGNAT issue, my IP address would be publicly accessible, and it would still be a risk to expose port 443 to the internet. That’s when I started looking for a solution to make my services accessible that wouldn’t require port forwarding at all.
Cloudflare Tunnel flips the direction of traffic
Outbound connection instead of inbound exposure
Cloudflare Tunnel changed things entirely. No router changes. No real port forwarding. Just publicly accessible URLs for each of my services running in my Docker stack. But the real question is, since I didn’t have a public IP address, how could it work?
Where a traditional setup waits for inbound traffic, Cloudflare (or cloudflared) establishes an outbound persistent connection (known as a tunnel) between your resources and Cloudflare’s global network. And that’s how it solves the CGNAT issue with a single reversal of traffic direction.
Cloudflared runs on your server and, in my case, inside a Docker container. Since it is running on my local host and is internally connected to local service URLs, it could establish an outbound connection to Cloudflare. Ultimately, Cloudflare becomes the public-facing reverse proxy to make the service publicly accessible. So, the flow would be User -> Cloudflare -> tunnel -> local service.
With this, I was able to access my home lab using public URLs, and no traditional networking steps like port forwarding or DDNS were required, and I didn’t expose my public IP either. I just needed to deploy cloudflared as a container, set up subdomains, and connect them in minutes.
At that point, my home lab was publicly reachable. Even behind CGNAT.
My setup: Jellyfin, Nextcloud, Immich
One tunnel, multiple subdomains, zero open ports
I have a simple DIY home server setup. I use an old Dell Latitude laptop as a Debian server. It acts as the main compute node. And for storage needs, I use an old Synology NAS. The NAS is purely used for storage; no additional load is put on it. The setup is used by my family only, and not all of them live under a single roof, which means users are not always on the same local network. So, publicly available URLs were a necessity.
Other than a few internal services, the most used services in my case are Jellyfin as a media server, Nextcloud as a local file server, and Immich as a photo library. Those services need public URLs to access outside my local network. These services are hosted inside separate Docker containers via Portainer. The best way to use Cloudflared was to host it in a separate container.
My domain was already inside the same Cloudflare account, so I just had to create a few subdomains and map them to the internal container ports via a config file. No ports were exposed externally, and my services were publicly available. There was a learning curve with the Cloudflare Zero Trust dashboard, and I had to fix a few minor hiccups like initial 502 errors due to misconfigured internal service URLs and WebSocket tweaks to fully adapt to the solution.
Here is how my config file looked:
tunnel: secret_token
credentials-file: /etc/cloudflared/secret_token.json
ingress:
- hostname: cloud.shekharvaidya.in
service: http://nextcloud:8081
- hostname: photos.shekharvaidya.in
service: http://immich:2283
- hostname: media.shekharvaidya.in
service: http://jellyfin:8096
- hostname: status.shekharvaidya.in
service: http://uptime-kuma:3001
- service: http_status:404
As a result, Jellyfin was performing well; I could access Nextcloud files outside my local network, and Immich photo uploads were working remotely on both Android and iOS devices.
It felt more secure than exposing my IP
Control, visibility, and instant shutdown
With port forwarding, your IP becomes publicly reachable and is prone to external risks like spoofing or unauthorized access. With Cloudflare Tunnel, your origin server is hidden, and traffic is proxied through Cloudflare’s global network.
Using Cloudflare comes with a few useful built-in advantages at no cost. Since the traffic is routed through Cloudflare, it can take advantage of automatic HTTPS with managed SSL certificates for secure access and basic distributed denial of service (DDoS) protection that helps mitigate unwanted traffic.
There are additional features such as Zero Trust dashboard logs and access policies, which improve visibility for debugging and add an extra layer of security through measures like email OTP. If you feel the need to disable the tunnel for debugging, it’s just a click away: no router rollback or ISP involvement is required.
With all these pros, there are a few limitations to consider. You need to keep in mind the fact that you are relying on a third-party proxy that requires a Cloudflare account, and if Cloudflare’s network is down, your users won’t be able to access your services. In case you are working on ultra-sensitive or fully self-contained setups, Cloudflare Tunnel might not be a reliable solution.
For my setup, the trade-offs were worth it.
I replaced Tailscale with raw WireGuard for a month, and here's what I learned
WireGuard didn’t fail me. My assumptions did.
I expected complexity. I got simplicity.
Most self-hosting guides assume that you control your public IP address. For a user like me who is running a home lab behind CGNAT, traditional port forwarding was never a realistic solution. Cloudflare Tunnel flipped the equation for me. I didn’t need a static IP or a single open port on the router, and my home lab was still accessible remotely.
It worked for me, but that doesn’t mean it will work in every scenario. If you want a fully self-contained setup or a setup with ultra-sensitive services, Cloudflare Tunnel would not be a good fit. For a small home lab, it removes ISP limitations and simplifies remote access without exposing your network.
