-
Notifications
You must be signed in to change notification settings - Fork 0
My Process (Cloudflare Tunnel Setup)
So, after setting up my isolated user on my Raspberry Pi, I made sure that the user could only interact with Docker and only expose traffic through port 80. This is part of my security model: even if an intruder manages to reach the Pi or container somehow, that user has zero sudo/root permissions, and no way to break outside of the Docker ecosystem. It's literally only there to run containers and start the Cloudflare tunnel. That's it. Nothing else.
Why did I even use Cloudflare Tunnel?
Because I’m running a physical backend server (the Pi), and this method is honestly better and more modern than opening ports through the router. Port forwarding exposes your internal network, and if someone gets in through that port. But a Cloudflare tunnel does the opposite — it creates an outbound connection from your Pi to Cloudflare's edge, so no open ports at all on the network. The tunnel proxies the traffic back inward to your container like a reverse proxy.
Bonus:
- It gives free HTTPS.
- It hides my home IP address.
- Doesn’t affect my home network or NAT settings at all.
I’m also running:
- Tailscale (mesh VPN) to restrict access to only specific devices.
- Fail2Ban just in case anything goes wrong with that.
Security always comes first in anything I build.
I installed Cloudflare’s daemon — this is what creates and manages the tunnel.
The package is called cloudflared:
sudo apt install cloudflared -yThis is the actual connector. It’s what keeps the tunnel alive. Once installed, the first command you run is:
cloudflared tunnel loginThat opens a browser window, logs you into Cloudflare, and lets you pick the domain you want to use (mine is partagoemailbreach.xyz). This is what authorizes your device to manage tunnels for your domain.
BUT — here's where I hit a wall: Cloudflare wanted me to either pay for a personal domain plan or bring my own domain. So I bought one from Namecheap, and when I added it into Cloudflare, they gave me 2 nameservers to add into my Namecheap settings. After I updated the nameservers in Namecheap, Cloudflare was able to take control of the DNS. That was the verification step.
Once that was complete, the command:
cloudflared tunnel create emailbreach-tunnelDid two things:
- Gave me a tunnel credentials file: it looks like
<tunnel-id>.json, and lives in~/.cloudflared/ - Gave me a tunnel hostname like
emailbreach.cfargotunnel.com(Cloudflare generated)
Also at this point I had a file called cert.pem saved automatically after login — that’s proof this Pi is allowed to create/manage tunnels.
Next step was setting up the actual routing using a YAML config file:
sudo nano ~/.cloudflared/config.ymlHere’s what I wrote:
tunnel: emailbreach-tunnel
credentials-file: /home/pi/.cloudflared/<tunnel-id>.json
ingress:
- hostname: www.partagoemailbreach.xyz
service: http://localhost:____
- service: http_status:404
This config means:
- When someone visits my domain (
www.partagoemailbreach.xyz), it’s routed into my local container running on port 8080. - Any unmatched request returns a 404.
At this point, my tunnel was active.
And to make sure it always runs on boot:
sudo cloudflared service installThat sets it up as a systemd service. Done.
Once I had the domain (partagoemailbreach.xyz) from Namecheap, I had to:
-
Log into Namecheap → manage domain → set Custom Nameservers.
-
Paste the two nameservers Cloudflare gave me when I added the domain to my Cloudflare dashboard.
- Like
kevin.ns.cloudflare.comandtori.ns.cloudflare.com.
- Like
Once I did that, Cloudflare became the DNS manager.
Then I went into Cloudflare’s DNS tab and added a CNAME record:
| Field | Value |
|---|---|
| Type | CNAME |
| Name | www |
| Target | emailbreach.cfargotunnel.com |
| Proxy Status | Proxied (orange cloud) |
This maps www.partagoemailbreach.xyz to the tunnel’s hostname.
Docs: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/dns/
So here’s the thing. I was building Docker images for my Flask app and Nginx setup, and I noticed some images would fail or throw warnings when I pulled them.
It was something like:
no matching manifest for linux/arm64 in the manifest list entries
That’s when I realized that even though the Raspberry Pi 4/5 is 64-bit hardware, Docker wasn’t defaulting to arm64. Instead, it wanted arm32 (armv7) images.
So I ran this test to find out what worked:
docker pull --platform linux/arm/v7 busybox
docker run --rm busybox uname -mThat gave me:
armv7l
(https://stackoverflow.com/questions/78637225/can-i-build-arm32-docker-files-with-dockerhub#:~:text=Note%20I%20am,linux/arm/v7) Then I tested 64-bit:
docker pull --platform linux/arm64 busybox
docker run --rm busybox uname -mIf that command fails or gives you errors, then Docker isn’t ready for 64-bit containers on your Pi setup (because of the OS). My Pi runs Raspberry Pi OS Lite which is 32-bit even if the CPU is 64-bit.
So I stuck with arm32v7 builds.
Since I now knew I had to build arm32v7 images for both my Flask app and Nginx container, I added this to my GitHub Actions build workflow:
platforms: linux/arm/v7This forces Docker Buildx to create a 32-bit compatible image that runs fine on my Pi.