Secure. Fast. One Gateway.
A self-hosted, one-click reverse proxy tunnel manager built for Raspberry Pi. Turn any Pi into a public gateway — no port forwarding, no VPS, no public IP required.
Install it with one copy-pasted command, open the web UI in your browser, click Install cloudflared, point a tunnel at your device, and share the public URL. Done — nothing is installed on your target device, and nothing is required from your visitors.
Internet User → Public URL (trycloudflare.com) → Cloudflare Edge
↑
Raspberry Pi (cloudflared tunnel) → 192.168.0.223
- Free public HTTPS URLs — no Cloudflare account required
- Works behind CGNAT / when you have no public IP (the Pi dials out)
- No router port-forwarding, no VPS, no money
TunnelOne is a single static Go binary with the web UI embedded inside it. It runs locally on the Pi and only manages the cloudflared process — nothing is ever installed on your target device.
Copy-paste this into a terminal on your Pi (systemd-based OS auto-installs it as a service):
curl -fsSL https://raw.githubusercontent.com/tamalmaity-dev/tunnelone/main/scripts/install.sh | bashThe script will:
- Detect the architecture (
arm64/armv7/amd64) - Download the matching single binary from the latest GitHub release
- Install it to
~/.tunnelone/bin - Start it (as a
systemdservice when running as root, otherwise foreground) - Print the web UI link — and open a browser for you if a desktop is present.
Open the link, click Install cloudflared, create a tunnel, point it at e.g. http://192.168.0.223, click Start, and share the public URL.
The installer pulls the latest binary from the
tamalmaity-dev/tunneloneGitHub release. After pushing (below), tag a release (v0.1.0) — the GitHub Actions workflow builds and attaches the Linux binaries automatically.
- Quick tunnels — instant
https://xxx.trycloudflare.comURL with no account - Named tunnels — Cloudflare-hosted tunnels via a token
- Custom domains — map a named tunnel to your own domain
- One-click cloudflared installer — auto-detects architecture and downloads the right binary
- Dashboard — live status of every tunnel
- Live logs — streamed over Server-Sent Events, with history
- Auto-restart / auto-start — resurrect crashed tunnels and bring tunnels back after a reboot (24/7)
- Settings — health-check interval, retry policy, log retention
- Single binary — the web UI is compiled into the Go binary
- TypeScript + Tailwind CSS frontend, embedded at build time
Requirements: Go 1.22+, Node 20+.
# build the web UI (TypeScript + Tailwind), then run the server
cd web && npm install && npm run build && cd ..
go run ./cmd/tunnelone # UI at http://localhost:9090Frontend dev server (hot reload, /api proxied to the Go backend):
cd web && npm run dev # http://localhost:5173make build # native binary
make build-pi # Raspberry Pi (linux/arm64)
make build-pi-armv7 # 32-bit Pi OS (armv7l)
make release # all three linux binaries into ./dist
make test # Go tests
make typecheck # TypeScript type check
make cleangit tag v0.1.0
git push origin v0.1.0The .github/workflows/release.yml workflow builds tunnelone-linux-{amd64,arm64,armv7} and attaches them to the GitHub release. The installer points at tamalmaity-dev/tunnelone in scripts/install.sh (override with TUNNELONE_REPO).
| Method | Path | Description |
|---|---|---|
GET |
/api/health |
Liveness probe |
GET |
/api/status |
System + tunnel summary |
GET |
/api/tunnels |
List tunnels |
POST |
/api/tunnels |
Create tunnel |
GET/PUT/DELETE |
/api/tunnels/{id} |
Get / update / delete |
POST |
/api/tunnels/{id}/start /stop /restart |
Control a tunnel |
GET |
/api/tunnels/{id}/logs |
SSE live log stream |
GET |
/api/tunnels/{id}/logs/history |
Previous log entries |
POST |
/api/install/cloudflared |
Install/update cloudflared |
GET |
/api/install/status |
cloudflared presence + version |
GET/PUT |
/api/settings |
Settings |
.
├── cmd/tunnelone/ entry point (wiring, graceful shutdown)
├── internal/
│ ├── config/ config load/save (env + file)
│ ├── database/ SQLite (pure-Go) + repos + migrations
│ ├── models/ Tunnel / Settings / LogEntry types
│ ├── cloudflared/ installer, process manager, parser, SSE hub
│ ├── server/ HTTP API + static file server
│ └── embed/ web build embedded via go:embed
├── web/ React + Vite frontend
├── scripts/install.sh systemd installer
└── docs/TunnelOne.md detailed design document
internal/cloudflared/Managerowns onecloudflaredsubprocess per tunnel.- Its output is parsed (
https://….trycloudflare.com,Registered tunnel connection,Lost connection) to drive live status. - Every line is appended to SQLite (ring-buffered) and streamed to the UI over SSE.
- On an unexpected exit the tunnel is restarted automatically when auto-restart is on; tunnels marked auto-start are relaunched when TunnelOne boots.
- The UI binds to
0.0.0.0so you can reach it across your LAN; put an auth reverse proxy in front if you expose it to the internet. - Any one with the public URL can access your device — share it sparingly.
- Cloudflare tokens are stored in the TunnelOne database; treat that directory as sensitive.
Built with ❤️ by Tamal Maity.
