A one-click tray tool for managing Google Cloud SQL Auth Proxy and SSH tunnels — on both macOS (via a SwiftBar menu bar plugin) and Linux (via a system-tray indicator + systemd service). Ships with a tunnel CLI too.
- Start/stop Cloud SQL Proxy and SSH tunnels from your menu bar / system tray
- Color-coded status: all connected / partial / none
- Per-tunnel and bulk start/stop controls
- Copy connection strings to the clipboard
- Auto-refreshing status (every 5 seconds)
- A
tunnelCLI for terminal-only workflows - Configure any number of tunnels via a simple JSON file
Each entry in tunnels.json maps a remote database (or SSH forward) to a local port. Tunnel Toggle launches cloud-sql-proxy (or ssh -N) per tunnel, tracks each process by PID, and reflects live status in a tray icon. The tray front-ends and the CLI all drive the same helper scripts under lib/, so status stays consistent however you interact with it.
| Platform | Front-end | Installer |
|---|---|---|
| macOS | SwiftBar menu bar plugin | ./install.sh |
| Linux | StatusNotifierItem tray daemon (Python/GTK) + systemd user service | ./install-linux.sh |
| Any | tunnel CLI |
none — run it directly |
The tray daemon is a small Python/GTK app that publishes a StatusNotifierItem. Install the runtime bits with your package manager:
# Arch
sudo pacman -S --needed jq python-gobject gtk3 libayatana-appindicator
# Debian / Ubuntu
sudo apt install jq python3-gi gir1.2-gtk-3.0 gir1.2-ayatanaappindicator3-0.1
# Fedora
sudo dnf install jq python3-gobject gtk3 libayatana-appindicator-gtk3You also need cloud-sql-proxy (v2) on your PATH for SQL tunnels, and the gcloud CLI authenticated with Application Default Credentials:
gcloud auth application-default loginTray requirement: the indicator needs an SNI-capable system tray. KDE Plasma shows it natively; GNOME needs the AppIndicator and KStatusNotifierItem extension; wlroots compositors (Sway, Hyprland) need a bar with an SNI tray module such as Waybar. No tray? Use the
tunnelCLI instead — the daemon is optional.
cp tunnels.json.example tunnels.json
# edit tunnels.json with your tunnels (see Configuration below)./install-linux.shThis validates your dependencies and config, then writes a tunnel-tray.service systemd user unit pointing at wherever you cloned the repo, and enables it. The tray icon appears immediately and starts on every login.
Useful commands:
systemctl --user status tunnel-tray.service # is the tray running?
journalctl --user -u tunnel-tray.service -f # tray logs
systemctl --user restart tunnel-tray.service # after editing tunnels.jsonEditing tunnels.json requires a tray restart (the menu is built at startup): systemctl --user restart tunnel-tray.service.
Install the prerequisites via Homebrew:
brew install jq
brew install cloud-sql-proxy # for SQL tunnels
brew install --cask swiftbarAuthenticate ADC for SQL tunnels:
gcloud auth application-default loginThen:
cp tunnels.json.example tunnels.json
# edit tunnels.json with your tunnels
./install.shLaunch SwiftBar and, when prompted, point its plugin folder at the location the installer reports. The Tunnel Toggle appears in your menu bar.
All tunnel definitions live in tunnels.json (gitignored, so your details stay local).
{
"proxy_binary": "/home/you/.local/bin/cloud-sql-proxy",
"tunnels": [
{
"name": "dev",
"label": "Dev",
"instance": "my-project:us-central1:my-db-dev",
"port": 3307
}
],
"ssh_tunnels": [
{
"name": "cortex",
"label": "C",
"forward": "-L 8420:127.0.0.1:8420",
"host": "cortex@example.com",
"opts": ""
}
]
}| Field | Required | Description |
|---|---|---|
name |
Yes | Short identifier (used in the CLI and filenames, e.g. dev) |
instance |
Yes | GCP connection string: project:region:instance |
port |
Yes | Local port to bind (e.g. 3307) |
label |
No | Display name in the tray (defaults to capitalized name) |
| Field | Required | Description |
|---|---|---|
name |
Yes | Short identifier (e.g. cortex) |
forward |
Yes | Forward spec: -L local:remote:port or -R remote:local |
host |
Yes | SSH target: [user@]host |
label |
No | Display name in the tray (defaults to capitalized name) |
opts |
No | Extra SSH options (e.g. "-p 2222 -i ~/.ssh/key") |
The resulting SSH command is: ssh -N ${opts} ${forward} ${host}
| Field | Required | Description |
|---|---|---|
proxy_binary |
No | Path to cloud-sql-proxy (auto-detected from PATH if omitted) |
Note: the SQL proxy is launched bound to
--address 0.0.0.0so containers (e.g. an MCP server) can reach it viahost.docker.internal. On shared or untrusted networks, remember the local port is reachable from your LAN while a tunnel is up.
The tunnel script works everywhere, no tray required:
./tunnel status # show all tunnel statuses (default)
./tunnel up [name|all] # start tunnel(s)
./tunnel down [name|all] # stop tunnel(s)
./tunnel restart [name|all] # restart tunnel(s)
./tunnel logs <name> # tail a tunnel's logsLower-level helpers (used by the tray) are available too:
./lib/proxy-ctl.sh start|stop|toggle|status|copy <name|all> # SQL tunnels
./lib/ssh-ctl.sh start|stop|toggle|status|copy <name|all> # SSH tunnels
./lib/bulk-ctl.sh start|stop # everything- macOS:
./uninstall.sh(stops tunnels, removes the SwiftBar symlink, cleans up state). - Linux:
systemctl --user disable --now tunnel-tray.service && rm ~/.config/systemd/user/tunnel-tray.service, then./tunnel down all.
- Tunnel won't start — check logs at
~/.tunnel-toggle/sql/<name>.logor~/.tunnel-toggle/ssh/<name>.log, or./tunnel logs <name>. - Port already in use —
ss -tlnp | grep <port>(Linux) orlsof -i :<port>(macOS). - SQL auth errors — run
gcloud auth application-default login. - Tray icon missing (Linux) — confirm your desktop has an SNI tray (see the note above); check
systemctl --user status tunnel-tray.service. - Menu is stale after a config change — restart the tray:
systemctl --user restart tunnel-tray.service. - SSH host key changed — remove the old key from
~/.ssh/known_hosts. - SSH key passphrase — load the key into ssh-agent:
ssh-add ~/.ssh/your_key.