From Raspberry Pi to the Cloud: Publishing Kubernetes Dashboard Securely with Cloudflare Tunnel — hero banner

August 25, 2025·3 min read

Over the weekend I decided to take my Raspberry Pi 5 a step further and turn it into a Kubernetes playground. My goal was to get the Kubernetes Dashboard up and running and then make it securely accessible from the internet, without relying on port forwarding or static IPs. The tools I chose were K3s for the cluster, Cloudflare DNS for my domain (chrishouse.io), and Cloudflare Tunnel to publish the service safely.


Installing K3s

The first step was the cluster itself. I initially tried MicroK8s but hit snapd version issues, so I pivoted to K3s which installed quickly. On the Pi I ran:

curl -sfL https://get.k3s.io | sh -

A quick check confirmed my node was live:

sudo k3s kubectl get node

Deploying the Kubernetes Dashboard

Once the cluster was healthy, I deployed the Kubernetes Dashboard. The manifests are published by the project, so installation was straightforward. The service exposed itself via a NodePort on 30001, and I verified it was working locally:

sudo k3s kubectl -n kubernetes-dashboard get svc

Which returned:

kubernetes-dashboard   NodePort   10.43.6.40   <none>   443:30001/TCP

From there I tested directly on the Pi:

curl -vk https://localhost:30001

The output confirmed a TLS handshake with a self-signed certificate and an HTTP 200 response containing the HTML of the dashboard login page.


Adding Cloudflare DNS and Tunnel

The challenge came when I wanted to expose this beyond my LAN. My ISP provides a dynamic IP, my Orbi router doesn’t like giving up ports 80 and 443, and I didn’t want to maintain fragile port forwarding rules anyway. That’s when I turned to Cloudflare.

I had already registered a domain (chrishouse.io) and moved DNS into Cloudflare. From there I installed cloudflared on the Pi:

curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared jammy main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update
sudo apt install cloudflared -y

After installation, I authenticated it against my Cloudflare account:

cloudflared tunnel login

That opened a browser window where I logged in and selected my domain. Credentials were saved to ~/.cloudflared/cert.pem.

I created a named tunnel for the dashboard:

cloudflared tunnel create k3s-dashboard

And mapped it to my subdomain:

cloudflared tunnel route dns k3s-dashboard dashboard.chrishouse.io

This created a CNAME record in Cloudflare pointing dashboard.chrishouse.io at the tunnel rather than my home IP.


Configuring the Tunnel

The last piece was configuring the tunnel itself. I created a config file:

sudo nano /etc/cloudflared/config.yml

With the contents:

tunnel: k3s-dashboard
credentials-file: /home/chris/.cloudflared/<tunnel-id>.json

ingress:
  - hostname: dashboard.chrishouse.io
    service: https://localhost:30001
    originRequest:
      noTLSVerify: true
  - service: http_status:404

The critical setting here was noTLSVerify: true because the Dashboard serves HTTPS with a self-signed certificate. Without that, Cloudflared would reject the connection.

Finally, I installed it as a service:

sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared

And checked the logs with:

sudo journalctl -u cloudflared -f

The logs showed multiple connections registered to Cloudflare edge locations. At that point, opening https://dashboard.chrishouse.io from my browser worked flawlessly.

Instead of a self-signed cert, the connection terminated at Cloudflare’s edge with a valid certificate, and the dashboard login page loaded securely.


Closing Thoughts

What started as an experiment in running Kubernetes on a Raspberry Pi ended up being a deep dive into modern networking. By leveraging Cloudflare Tunnel, I didn’t have to expose my home IP, configure NAT rules on my router, or worry about whether my ISP changed my IP overnight. My Raspberry Pi simply maintained an outbound connection to Cloudflare’s global edge, and Cloudflare handled TLS termination and routing.

It’s remarkable that with a $60 Raspberry Pi, an open-source Kubernetes distribution, and a free Cloudflare account, I was able to publish a production-grade web application securely to the internet.

This approach doesn’t just apply to Kubernetes — the same pattern works for any internal service you want to access remotely without punching holes in your firewall.

As a related project, I’ve also been experimenting with Meshtastic and building a live mesh radio map for the Memphis area. That project, called Memphis Meshview, takes real-time MQTT packet data from local Meshtastic nodes and renders them on an interactive map. If you’d like to see a working example of how these lightweight IoT networks can be visualized in real time, you can check it out at https://meshview.chrishouse.io/map.

Beyond maps, it also provides a persistent conversation log where messages exchanged across the mesh network are stored and browsable at https://meshview.chrishouse.io/chat. It’s a live example of how these techniques can be used to make distributed, community-driven networks visible and interactive on the web.

Enjoyed this post? Give it a clap!

Comments