Tunnel Tools (ngrok / cloudflared)¶
Expose a local port or service to the internet — useful for webhook testing, sharing a dev server, SSH reverse tunnels, and Cloudflare-native access policies.
Optional role (installTunnelTools). Enable at chezmoi init --force or set installTunnelTools = true in ~/.config/chezmoi/chezmoi.toml.
Tools¶
| Tool | Purpose | Account required? | Free tier |
|---|---|---|---|
| ngrok | HTTP/TCP/TLS tunnels, traffic inspection UI | Yes (free plan available) | 1 agent, 1 endpoint, random subdomain |
| cloudflared | Cloudflare Tunnel — production-grade, no port forwarding needed | Yes (Cloudflare account, free) | Unlimited named tunnels, custom domains via Cloudflare DNS |
Installation¶
Managed by the networking_tools Ansible role under the tunnel_tools tag.
| Platform | ngrok | cloudflared |
|---|---|---|
| macOS | brew install ngrok/ngrok/ngrok |
brew install cloudflared |
| Linux (Debian/Ubuntu) | apt repo (official) → tgz fallback | .deb from GitHub releases → binary fallback |
| Linux (CentOS/RHEL) | tgz from equinox.io → ~/.local/bin |
.rpm from GitHub releases |
ngrok¶
Initial setup¶
# Authenticate (one-time, token stored in ~/.config/ngrok/ngrok.yml)
ngrok config add-authtoken <YOUR_TOKEN>
Get your token from https://dashboard.ngrok.com/get-started/your-authtoken.
Expose a local HTTP server¶
# Forward localhost:3000 to a random ngrok HTTPS URL
ngrok http 3000
# Fixed subdomain (paid plan)
ngrok http --domain=myapp.ngrok.app 3000
# Specific host header (useful with vhosts)
ngrok http --host-header=rewrite 3000
Expose TCP / raw ports¶
# SSH (useful for remote access to a machine behind NAT)
ngrok tcp 22
# Any TCP service
ngrok tcp 5432
Traffic inspection¶
ngrok runs a local web inspector at http://127.0.0.1:4040 while a tunnel is active:
# Open inspector in browser
open http://127.0.0.1:4040
# List active tunnels via API
curl -s http://127.0.0.1:4041/api/tunnels | jq
Replay requests¶
In the web inspector (or via ngrok replay) you can replay any captured request — great for debugging webhook handlers without re-triggering the upstream service.
Named tunnels in config¶
~/.config/ngrok/ngrok.yml:
version: "3"
agent:
authtoken: <YOUR_TOKEN>
tunnels:
dev-server:
proto: http
addr: 3000
api:
proto: http
addr: 8080
host_header: rewrite
# Start a specific named tunnel
ngrok start dev-server
# Start all tunnels defined in config
ngrok start --all
cloudflared¶
Quick tunnel (no config needed)¶
# Instantly expose port 3000 — generates a random trycloudflare.com URL, no account needed
cloudflared tunnel --url http://localhost:3000
This is the fastest way to share a local server with someone.
Named tunnels (permanent, custom domain)¶
# Authenticate with Cloudflare (opens browser)
cloudflared tunnel login
# Create a named tunnel
cloudflared tunnel create my-tunnel
# Create DNS record pointing to the tunnel
cloudflared tunnel route dns my-tunnel dev.example.com
# Run the tunnel
cloudflared tunnel run my-tunnel
Config file ~/.cloudflared/config.yml:
tunnel: <TUNNEL_ID>
credentials-file: /Users/you/.cloudflared/<TUNNEL_ID>.json
ingress:
- hostname: dev.example.com
service: http://localhost:3000
- hostname: api.example.com
service: http://localhost:8080
- service: http_status:404
SSH access via Cloudflare Access¶
Cloudflare Access lets you expose SSH without opening any port — the client connects through Cloudflare's network.
Server side (machine to expose):
# Create tunnel and configure SSH ingress (hostname: ssh.example.com → localhost:22)
cloudflared tunnel run
Client side:
Run as a system service¶
# Install as launchd (macOS) or systemd (Linux) service
sudo cloudflared service install
# Check status
sudo launchctl list | grep cloudflared # macOS
sudo systemctl status cloudflared # Linux
Comparison¶
| Feature | ngrok | cloudflared |
|---|---|---|
| Quick share (no config) | ngrok http 3000 |
cloudflared tunnel --url localhost:3000 |
| Custom domain (free) | Paid | Yes (via Cloudflare DNS) |
| Traffic inspection UI | Yes (localhost:4040) |
No built-in |
| Replay requests | Yes | No |
| Production-grade | Limited | Yes (runs Cloudflare CDN) |
| TCP tunnels | Yes | Yes (via Spectrum, paid) |
| SSH proxy | Yes (TCP tunnel) | Yes (Cloudflare Access, free) |
| Needs open port | No | No |
| Self-hosted option | ngrok Agent SDK | Yes (Cloudflare WARP) |
Common Dev Patterns¶
Webhook testing¶
# Start your local handler
python -m http.server 8080
# Expose it and copy the HTTPS URL into your webhook provider's dashboard
ngrok http 8080
# or
cloudflared tunnel --url http://localhost:8080
Share a local dev server with a teammate¶
# One-liner with cloudflared (no account, no install on recipient's side)
cloudflared tunnel --url http://localhost:3000
Remote SSH to a machine behind NAT¶
# On the remote machine (server)
ngrok tcp 22
# Note the forwarding address, e.g. 0.tcp.ngrok.io:12345
# From your laptop
ssh -p 12345 user@0.tcp.ngrok.io
VS Code Remote via tunnel¶
# On the remote machine
code tunnel # uses Microsoft's tunnel (no ngrok/cloudflared needed)
# Alternative: expose SSH port and use Remote-SSH extension
ngrok tcp 22
# Add to ~/.ssh/config: Host ngrok-remote \n HostName 0.tcp.ngrok.io \n Port 12345