| Crates.io | ytunnel |
| lib.rs | ytunnel |
| version | 0.5.0 |
| created_at | 2026-01-20 20:12:45.329065+00 |
| updated_at | 2026-01-22 07:30:01.274148+00 |
| description | TUI-first CLI for managing Cloudflare Tunnels with custom domains |
| homepage | https://github.com/yetidevworks/ytunnel |
| repository | https://github.com/yetidevworks/ytunnel |
| max_upload_size | |
| id | 2057456 |
| size | 309,218 |
___ ___ _______ __
| | |_ _|.--.--.-----.-----.-----.| |
\ / | | | | | | | -__|| |
|___| |___| |_____|__|__|__|__|_____||__|
Cloudflare tunnels made easy!
A TUI-first CLI for managing Cloudflare Tunnels with custom domains. Think ngrok, but using your own Cloudflare domain with persistent URLs and a dashboard to manage them.
Supported Platforms: macOS and Linux
cloudflared - Cloudflare's tunnel daemon
macOS:
brew install cloudflare/cloudflare/cloudflared
Linux (Debian/Ubuntu):
curl -L https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/cloudflare-archive-keyring.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
Linux (other):
# Download the latest release from https://github.com/cloudflare/cloudflared/releases
sudo cp cloudflared /usr/local/bin/
sudo chmod +x /usr/local/bin/cloudflared
Note: You only need to install cloudflared. Do NOT run it as a system service. YTunnel manages cloudflared processes directly.
Cloudflare API Token with these permissions:
Create one at: https://dash.cloudflare.com/profile/api-tokens
A domain managed by Cloudflare (free tier works)
brew install yetidevworks/ytunnel/ytunnel
cargo install ytunnel
git clone https://github.com/yetidevworks/ytunnel
cd ytunnel
cargo install --path .
Download from GitHub Releases.
# First-time setup
ytunnel init
# Open the TUI dashboard
ytunnel
# Or add a tunnel directly from CLI
ytunnel add myapp localhost:3000 --start
YTunnel is a management tool, not a daemon itself. Here's how the pieces fit together:
┌─────────────────────────────────────────────────────────────────┐
│ ytunnel (CLI/TUI) │
│ Management tool - runs only when you invoke it │
└─────────────────────────────────────────────────────────────────┘
│
Creates & manages configs for
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ launchd (macOS) / systemd (Linux) │
│ System service manager - always running │
│ │
│ macOS: ~/Library/LaunchAgents/com.ytunnel.<account>.<name>.plist │
│ Linux: ~/.config/systemd/user/ytunnel-<account>-<name>.service │
└─────────────────────────────────────────────────────────────────┘
│
Starts/stops/monitors
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ cloudflared processes │
│ One process per tunnel - runs in background │
│ │
│ • cloudflared tunnel --config myapp.yml run │
│ • cloudflared tunnel --config api.yml run │
└─────────────────────────────────────────────────────────────────┘
│
Connects to
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Cloudflare Edge │
│ Routes traffic to your tunnels │
└─────────────────────────────────────────────────────────────────┘
| Mode | How it runs | Survives reboot? | Use case |
|---|---|---|---|
Ephemeral (ytunnel run) |
Foreground process | No | Quick testing, one-off tunnels |
Persistent (ytunnel add --start) |
launchd/systemd | Yes* | Production, always-on services |
*Tunnels don't auto-start by default. They start when you run ytunnel start and keep running until you ytunnel stop or reboot. To auto-start on login, press A in the TUI to toggle auto-start (⟳ indicator shows when enabled).
When you run ytunnel add myapp localhost:3000 --start:
myapp.yourdomain.com → tunneltunnels.tomlThe service file tells launchd/systemd to run cloudflared with your config. Logs go to the logs directory.
Run ytunnel with no arguments to open the interactive dashboard:
┌─ Tunnels (3) ─────────────────────┬─ Logs: myapp ─────────────────────────────────┐
│ ● myapp myapp.example.com │ 2024-01-20 10:30:15 INF Starting tunnel │
│ ● api api.example.com │ 2024-01-20 10:30:16 INF Connection registered │
│ ○ staging staging.example.com │ 2024-01-20 10:30:17 INF Tunnel connected │
│ │ 2024-01-20 10:30:18 INF Route propagated │
│ │ 2024-01-20 10:30:21 INF Request served GET / │
│ ├─ Metrics ─────────────────────────────────────┤
│ │ Requests: 1,247 Errors: 3 Active: 2 │
│ │ Health: ✓ healthy │
│ │ HA Connections: 4 Edge: dfw08, den01 │
│ │ Status Codes: 200:1198 304:42 404:3 500:4 │
│ │ Traffic: ▁▂▃▅▆▄▃▂▁▂▃▄▅▆▇█▆▅▄▃▂▁▂▃▄▅▆▇ │
├───────────────────────────────────┴───────────────────────────────────────────────┤
│ Started myapp │
│ [a]dd [s]tart [S]top [R]estart [c]opy [o]pen [h]ealth [d]elete [r]efresh [q]uit │
└───────────────────────────────────────────────────────────────────────────────────┘
Status indicators:
● Running (green)○ Stopped (yellow)✗ Error (red)⟳ Auto-start enabled (cyan, shown after hostname)Keyboard shortcuts:
| Key | Action |
|---|---|
a |
Add a new tunnel |
s |
Start selected tunnel |
S |
Stop selected tunnel |
R |
Restart tunnel (updates daemon config) |
c |
Copy tunnel URL to clipboard |
o |
Open tunnel URL in browser |
h |
Check tunnel health |
A |
Toggle auto-start on login (⟳ = enabled) |
d |
Delete selected tunnel |
m |
Import ephemeral tunnel as managed |
; |
Cycle through accounts (when multiple configured) |
r |
Refresh status |
↑/↓ or j/k |
Navigate list |
q |
Quit |
Tunnels continue running in the background after you close the TUI.
For running tunnels, the TUI displays live metrics from cloudflared's Prometheus endpoint:
dfw08 = Dallas)Metrics auto-refresh every 5 seconds. Health checks run every 30 seconds. Use h for immediate health check.
When a tunnel goes down or comes back up, ytunnel sends a system notification. This helps you catch issues even when the TUI isn't visible.
terminal-notifier (if installed) or osascriptnotify-send (requires libnotify)Ephemeral tunnels (created with ytunnel run) also appear in the TUI marked as [ephemeral]. You can:
m) to add daemon control# Add a tunnel (doesn't start it)
ytunnel add myapp localhost:3000
# Add and start immediately
ytunnel add myapp localhost:3000 --start
# Use a specific zone
ytunnel add api localhost:8080 -z dev.example.com
# Start/stop/restart tunnels
ytunnel start myapp
ytunnel stop myapp
ytunnel restart myapp # Stop, update config, start
# View logs
ytunnel logs myapp # Last 50 lines
ytunnel logs myapp -n 100 # Last 100 lines
ytunnel logs myapp -f # Follow (like tail -f)
# List all tunnels with status
ytunnel list
# Delete a tunnel
ytunnel delete myapp
# Reset all configuration (start fresh)
ytunnel reset
ytunnel reset -y # Skip confirmation
For quick one-off tunnels that stop when you press Ctrl+C:
# Auto-generated subdomain (ytunnel-abc123.example.com)
ytunnel run localhost:3000
# Named subdomain (myapp.example.com)
ytunnel run myapp localhost:3000
# Different zone
ytunnel run api -z dev.example.com localhost:8080
# Add a new account (interactive)
ytunnel init
# List all configured accounts
ytunnel account list
# Set the default account
ytunnel account select production
# or
ytunnel account default production
# Remove an account
ytunnel account remove old-account
# Use a specific account for any command
ytunnel add myapp localhost:3000 --account production
ytunnel list --account dev
# List available zones
ytunnel zones
# Change default zone
ytunnel zones default dev.example.com
macOS:
| Path | Purpose |
|---|---|
~/Library/Application Support/ytunnel/config.toml |
API credentials and zones |
~/Library/Application Support/ytunnel/tunnels.toml |
Persistent tunnel state |
~/Library/Application Support/ytunnel/<tunnel-id>.json |
Cloudflare tunnel credentials |
~/Library/Application Support/ytunnel/tunnel-configs/<name>.yml |
cloudflared config files |
~/Library/Application Support/ytunnel/logs/<name>.log |
Tunnel daemon logs |
~/Library/LaunchAgents/com.ytunnel.<account>.<name>.plist |
launchd service files |
Linux:
| Path | Purpose |
|---|---|
~/.config/ytunnel/config.toml |
API credentials and zones |
~/.config/ytunnel/tunnels.toml |
Persistent tunnel state |
~/.config/ytunnel/<tunnel-id>.json |
Cloudflare tunnel credentials |
~/.config/ytunnel/tunnel-configs/<name>.yml |
cloudflared config files |
~/.config/ytunnel/logs/<name>.log |
Tunnel daemon logs |
~/.config/systemd/user/ytunnel-<account>-<name>.service |
systemd service files |
Config file location: ~/Library/Application Support/ytunnel/config.toml (macOS) or ~/.config/ytunnel/config.toml (Linux):
selected_account = "dev"
[[accounts]]
name = "dev"
api_token = "your-token"
account_id = "your-account-id"
default_zone_id = "zone-id"
default_zone_name = "example.com"
[[accounts.zones]]
id = "zone-id"
name = "example.com"
[[accounts]]
name = "production"
api_token = "another-token"
account_id = "another-account-id"
default_zone_id = "prod-zone-id"
default_zone_name = "mysite.io"
[[accounts.zones]]
id = "prod-zone-id"
name = "mysite.io"
tunnels.toml (same directory as config.toml):
[[tunnels]]
name = "myapp"
account_name = "dev"
target = "localhost:3000"
zone_id = "abc123"
zone_name = "example.com"
hostname = "myapp.example.com"
tunnel_id = "cf-tunnel-id"
enabled = true
auto_start = false # Set to true to start on login
# Via ytunnel
ytunnel list
# Via system service manager
launchctl list | grep ytunnel # macOS
systemctl --user list-units 'ytunnel-*' # Linux
# In TUI: select tunnel and view right pane
# Or directly
tail -f ~/Library/Application\ Support/ytunnel/logs/myapp.log # macOS
tail -f ~/.config/ytunnel/logs/myapp.log # Linux
cloudflared --versioncloudflared tunnel --config <config-path> runmacOS:
# Stop (replace <account> with your account name, e.g., "dev")
launchctl unload ~/Library/LaunchAgents/com.ytunnel.<account>.myapp.plist
# Start
launchctl load ~/Library/LaunchAgents/com.ytunnel.<account>.myapp.plist
# Remove completely
launchctl unload ~/Library/LaunchAgents/com.ytunnel.<account>.myapp.plist
rm ~/Library/LaunchAgents/com.ytunnel.<account>.myapp.plist
Linux:
# Stop (replace <account> with your account name, e.g., "dev")
systemctl --user stop ytunnel-<account>-myapp.service
# Start
systemctl --user start ytunnel-<account>-myapp.service
# Remove completely
systemctl --user stop ytunnel-<account>-myapp.service
systemctl --user disable ytunnel-<account>-myapp.service
rm ~/.config/systemd/user/ytunnel-<account>-myapp.service
systemctl --user daemon-reload
MIT