Run OpenCode locally and access it from anywhere. octunnel exposes your local OpenCode server to the internet via Cloudflare Tunnel — in one command.
Caution
Your OpenCode server is publicly accessible via the tunnel URL. Anyone with the link has full access to your server — do not share it publicly.
Quick Tunnel (default) is the safer option: the URL is random, ephemeral, not indexed by search engines, and rotates every time you restart. As long as you don't share the URL, no one else can discover it.
Named Tunnel uses your own domain with a permanent DNS record. Consider adding Cloudflare Access for zero-trust authentication.
# Quick install (macOS / Linux)
curl -fsSL https://raw.githubusercontent.com/chabinhwang/octunnel/main/install.sh | bash
# Homebrew
brew install chabinhwang/tap/octunnel
# Go
go install github.com/chabinhwang/octunnel@latest| Platform | Status |
|---|---|
| macOS | Fully supported |
| Linux | Fully supported |
| Windows | Not yet supported (requires Unix syscalls) |
macOS
npm install -g opencode
brew install cloudflared
# lsof is pre-installedLinux (Debian/Ubuntu)
npm install -g opencode
# cloudflared
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 $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared
# clipboard (optional, for URL auto-copy)
sudo apt install xclip # or xsel
# lsof is pre-installed on most distrosLinux (Arch)
npm install -g opencode
pacman -S cloudflared xclip
# lsof is pre-installedWindows (not yet supported)
Windows is not supported in the current version due to Unix-specific process management (Setpgid, lsof, signal handling). Contributions welcome.
If you want to prepare the prerequisites for future support:
npm install -g opencode
winget install Cloudflare.cloudflared # or: choco install cloudflared
# clipboard: built-in 'clip' command
# port detection: netstat -ano | findstr LISTENING# Homebrew
brew update && brew upgrade octunnel
# Quick install (re-run to overwrite with latest)
curl -fsSL https://raw.githubusercontent.com/chabinhwang/octunnel/main/install.sh | bash
# Go
go install github.com/chabinhwang/octunnel@latestoctunnelThis will:
- Start
opencode serve - Detect the local port
- Open a Cloudflare Quick Tunnel (
*.trycloudflare.com) - Copy the public URL to your clipboard
- Display a QR code in the terminal
No login or configuration needed.
Each command prints the next step to guide you through the setup flow:
# 1. Login to Cloudflare (opens browser)
octunnel login
# 2. Create tunnel + connect DNS
octunnel auth
# 3. Run with your fixed domain
octunnel runoctunnel switch domainRe-login to a different Cloudflare domain and update DNS routing.
| Command | Description |
|---|---|
octunnel |
Quick Tunnel — instant public URL, no auth needed |
octunnel login |
Login to Cloudflare + set base domain |
octunnel auth |
Create Named Tunnel + DNS route |
octunnel run |
Run Named Tunnel with fixed domain |
octunnel switch domain |
Change to a different domain |
octunnel reset |
Reset config and delete Cloudflare tunnel (CNAME must be removed manually) |
octunnel remove |
Completely uninstall octunnel data and delete tunnel |
All state is stored in ~/.octunnel/config.json (atomic writes with .bak backup).
{
"certPemPath": "/Users/me/.cloudflared/cert.pem",
"baseDomain": "example.com",
"tunnelId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"tunnelName": "octunnel",
"credentialsFilePath": "/Users/me/.cloudflared/xxxxxxxx.json",
"hostname": "open.example.com",
"operationStatus": "completed",
"currentPhase": "hostname_saved",
"mode": "named"
}The cloudflared tunnel config is written to a separate file at ~/.octunnel/cloudflared.yml — octunnel never touches ~/.cloudflared/config.yml. This file is passed via the --config flag when running cloudflared tunnel run.
octunnel is designed to handle interruptions gracefully:
| Scenario | Behavior |
|---|---|
Ctrl+C during octunnel |
Processes cleaned up, state saved as "interrupted" |
Ctrl+C during octunnel login |
Cert deleted, forces fresh login on next run |
| Tunnel created but DNS not routed | octunnel auth resumes from DNS step |
opencode alive but cloudflared died |
Reuses opencode, restarts cloudflared only |
cloudflared alive but opencode died |
Kills orphan cloudflared, starts fresh |
| Both processes alive from previous run | Verifies process names (not just PIDs), reuses both, displays existing URL |
| Config file corrupted | Restores from .bak backup |
cloudflared.yml write failure |
Atomic write (tmp + rename) prevents partial writes |
| Duplicate tunnel name | Auto-retries with octunnel1, octunnel2, ... |
| Stale lock file | Auto-cleaned only when PID is confirmed dead; if PID is alive (even a different process), user must manually remove the lock file |
The login command uses a conservative recovery policy:
- If login state is even slightly unclear (cert exists but no domain, or vice versa), the existing
cert.pemis deleted and a fresh login is performed. octunnel loginalways performs a freshcloudflared tunnel login— it never silently reuses an existing cert.- Only a fully complete state (cert + domain both valid) is considered "done".
~/.octunnel/octunnel.lock prevents concurrent octunnel instances. Stale locks are auto-cleaned only when the recorded PID is confirmed dead. If the PID is still alive (even if it belongs to a different process), the lock is not removed automatically — you must delete it manually.
# 1. Remove config, tunnel, and all octunnel data
octunnel remove
# 2. Remove the binary
brew uninstall octunnel # if installed via Homebrew
# or
rm $(which octunnel) # if installed via curl or go installNote:
octunnel removedeletes the Cloudflare tunnel but does NOT delete CNAME DNS records. Remove them manually from the Cloudflare dashboard.
- Windows not supported — Unix-specific syscalls (
Setpgid,lsof, POSIX signals) are required - Quick Tunnel URLs are temporary and change every run
cloudflared tunnel loginrequires a browser — headless environments need manual cert setup- Port detection relies on
lsofoutput parsing (macOS/Linux) - Clipboard:
pbcopy(macOS),xclip/xsel(Linux) — Windowsclipnot yet wired - Process detection uses
psandpgrep(pre-installed on macOS/Linux) - The tool does not use the Cloudflare API — all operations go through
cloudflaredCLI
MIT