If you just typed “OpenClaw Cloudflare tunnel setup for remote access” into your search bar, you probably want a straight answer, not marketing fluff. This post is exactly that: a reproducible recipe for exposing the OpenClaw Control UI to the internet through Cloudflare Tunnels. It covers installation, DNS wiring, Zero Trust authentication, and a few hardening tricks I wish somebody had told me earlier. The whole thing is free, survives CGNAT, and works great on low-power Raspberry Pi setups that live behind your ISP router.
Why use Cloudflare Tunnel instead of Tailscale?
Tailscale rocks for mesh networking, but sometimes you need a publicly routable URL—maybe you’re demoing an agent to a client or triggering workflows from Zapier. Cloudflare Tunnel gives you:
- No exposed port on your router. Outbound
httpstraffic only. - A real hostname under your own domain. Free wildcard SSL.
- Optional Zero Trust policies (OIDC, One-Time PIN, service auth).
- Price: $0 for up to 100 tunnels.
- No additional agent on your laptop; a browser is enough.
Trade-offs: latency is ~50 ms higher than a direct local connection, and you’re trusting Cloudflare’s edge to terminate TLS. For my home lab that’s fine; for regulated workloads, read your compliance docs.
Prerequisites
- An OpenClaw instance running locally (gateway on
localhost:3000by default). Version pinned at v0.37.2 as of this writing, Node 22.3 LTS. - A free Cloudflare account with a domain onboarded (the free plan works).
sudoaccess on the machine — examples assume Raspberry Pi OS 64-bit but any Debian/Ubuntu box works.- Approx. 15 minutes and coffee.
Step 1 — Install cloudflared on your Pi
Cloudflare provides native packages. The repo has pinned signatures; don’t curl random scripts.
# Add Cloudflare's GPG key
type -a curl >/dev/null || sudo apt install -y curl
sudo mkdir -p /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | \
sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
# Add the apt source (Debian/Ubuntu/PI OS)
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared bookworm main" | \
sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install -y cloudflared
# Verify version
cloudflared --version # expected >= 2024.6.0
If you’re on Alpine, Arch, or macOS, use their native package managers; binary parity is good across the board.
Step 2 — Authenticate and create the tunnel
Log in and let Cloudflare drop a cert into ~/.cloudflared:
cloudflared tunnel login
A browser pops up, you pick your domain, and a cert appears. Now create a named tunnel. I like to tie the name to the machine:
cloudflared tunnel create pi-openclaw
You’ll see something like:
Created tunnel 2c3313f5-e359-424f-bfee-79e4e0c6b90f
Copy the UUID; you’ll need it in the config file.
Step 3 — Wire up a DNS record
Still on the command line:
cloudflared tunnel route dns pi-openclaw claw.mydomain.com
This creates a CNAME that points claw.mydomain.com to a Cloudflare *.cfargotunnel.com record. DNS propagates instantly inside Cloudflare’s resolver network.
Step 4 — Run OpenClaw through the tunnel locally
We’ll map the private service http://localhost:3000 to the public hostname.
mkdir -p ~/.cloudflared
cat > ~/.cloudflared/config.yml <<'YAML'
tunnel: 2c3313f5-e359-424f-bfee-79e4e0c6b90f # UUID from step 2
credentials-file: /home/pi/.cloudflared/2c3313f5-e359-424f-bfee-79e4e0c6b90f.json
ingress:
- hostname: claw.mydomain.com
service: http://localhost:3000
- service: http_status:404
YAML
Test it:
cloudflared tunnel run pi-openclaw
Hit https://claw.mydomain.com from your phone’s LTE network. If OpenClaw’s login screen appears, you’ve punched through CGNAT without touching the router.
Step 5 — Lock it down with Zero Trust access policies
Exposing an AI agent with shell access is risky. Cloudflare’s free plan lets you enforce one policy per application; that’s enough for basic auth.
- Open the Cloudflare dashboard → Zero Trust → Access → Applications → Add Application.
- Choose Self-hosted, set Application name to
openclaw, Domain toclaw.mydomain.com. - Add a policy Require login. Methods:
- One-Time PIN (Cloudflare emails a code to any address on an allow-list).
- GitHub OIDC (my preference—most devs already have GitHub).
- Google, Microsoft, or any SAML 2.0 provider.
- Save.
Result: hitting the site now prompts for your chosen identity provider before traffic leaves Cloudflare’s edge. OpenClaw never sees unauthenticated requests.
If you really want old-school basic auth, Cloudflare Access also supports service tokens:
# Generate a token pair
a=$(uuidgen); b=$(uuidgen)
Add an Allow policy: “service token equals $a”. Keep $b as the secret; clients send both as CF-Access-Client-Id / CF-Access-Client-Secret headers. Most HTTP clients support that out of the box.
Step 6 — Hardening OpenClaw for the open internet
OpenClaw wasn’t built to be a public SaaS; it assumes you own the box. A few tweaks go a long way:
- Rotate the default auth token. In
~/.clawcloud/config.jsonset a long random string fortoken. Don’t commit this to Git. - Disable shell tool unless you need it. Comment it out in
agent-config.ymlor wrap it behind a role check. - Rate-limit POST requests with
express-rate-limitif you expose custom HTTP endpoints. - Enforce HTTPS in Express:
app.use((req, res, next) => {
if (req.headers['x-forwarded-proto'] !== 'https') {
return res.redirect(302, 'https://' + req.hostname + req.url);
}
next();
});
- Set CORS to your exact hostname:
const cors = require('cors');
app.use(cors({
origin: 'https://claw.mydomain.com',
credentials: true
}));
- Audit integrations: Composio tokens stored in env vars? Rotate them quarterly.
Step 7 — Make the tunnel persistent with systemd
The cloudflared Debian package ships a helper:
sudo cloudflared service install
This creates /etc/systemd/system/cloudflared.service pointing at your config.yml. Check:
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared
Reboot the Pi and make sure systemd reports “active (running)”. Cloudflare will also show a green heartbeat under Zero Trust → Tunnels.
Step 8 — Debugging common issues
Cloudflare Tunnel says “connection dropped” every few minutes
Your Pi’s time might drift. Install chrony or make sure systemd-timesyncd is enabled; TLS fails if the clock is off by >5 minutes.
OpenClaw UI loads but API calls 403
You’re hitting the API from JavaScript and the browser blocks CORS. Add Access-Control-Allow-Origin: https://claw.mydomain.com in Express or configure Cloudflare’s Transform Rules → Response Headers.
Zero Trust auth loops after GitHub login
Check that the application domain matches exactly the tunnel hostname; Cloudflare is case-sensitive and ignores www. prefixes.
IPv6 devices can’t connect
Nothing to fix: Cloudflare automatically serves dual-stack. If you toggled the IPv6 Compatibility flag off, turn it back on.
Step 9 — What this costs (spoiler: still free)
- Cloudflare Tunnel: unlimited bandwidth, 100 tunnels, 1000 DNS records.
- Zero Trust Access: first 50 users free. If you’re reading this, you’re probably using 1-2 seats.
- Domain renewal: $8–$12/year if you went with Cloudflare Registrar.
Compared to Tailscale’s free plan, Cloudflare wins on public accessibility and loses on private mesh capabilities. I keep both in my toolbox.
Next steps
At this point you have a live, authenticated URL for your OpenClaw instance. Ship the link to your team, trigger webhooks from GitHub Actions, or let your Telegram bot hit it directly. If you outgrow the Pi, drop the same config.yml onto an EC2 micro or a DigitalOcean droplet—Cloudflare doesn’t care where the origin lives. The important bit is that you never again need to beg an IT admin for port forwarding.
If you build something cool with this setup, please share in the GitHub Discussions. We’re collecting real-world deployment recipes and yours could be the next one we document.