Cloudflare Tunnel¶
Quickly expose your local development server to the internet with a Cloudflare tunnel. This is essential for testing LayerCode webhooks during local development.
Overview¶
The tunnel command provides:
- Quick public URL: Expose any local port via Cloudflare's quick tunnel service
- Automatic webhook update: Optionally update your LayerCode agent's webhook URL
- Automatic restore: Previous webhook URL is restored when the tunnel stops
- Debug logging: Full tunnel logs saved to timestamped files
Prerequisites¶
Required: cloudflared must be installed.
Install cloudflared¶
macOS:
Linux:
# Debian/Ubuntu
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb
# Or via package manager
sudo apt-get update && sudo apt-get install cloudflared
Windows:
Verify installation:
Quick Start¶
# Basic tunnel - displays webhook URL to copy manually
layercode-gym tunnel --port 8000
# Or specify a full URL directly
layercode-gym tunnel --url http://localhost:8000
# Auto-update agent webhook (recommended)
layercode-gym tunnel --port 8000 --unsafe-update-webhook
When the tunnel starts, you'll see a banner like:
======================================================================
======================================================================
CLOUDFLARE TUNNEL ESTABLISHED
======================================================================
Tunnel URL: https://random-words-here.trycloudflare.com
======================================================================
IMPORTANT: Add this URL to your LayerCode agent webhook:
https://dash.layercode.com/
TIP: Use --unsafe-update-webhook to automatically update
the webhook for your agent (requires --agent-id)
======================================================================
Press Ctrl+C to stop
======================================================================
======================================================================
Command Reference¶
Target Arguments (one required)¶
| Argument | Description |
|---|---|
--port PORT |
Local port to expose via tunnel (uses --host, default: localhost) |
--url URL |
Full URL to expose (e.g., http://localhost:8000) |
Webhook Update Options¶
| Option | Description |
|---|---|
--unsafe-update-webhook |
Automatically update agent webhook to tunnel URL |
--agent-id ID |
LayerCode agent ID to update (default: LAYERCODE_AGENT_ID env var) |
--api-key KEY |
LayerCode API key (default: LAYERCODE_API_KEY env var) |
--agent-path PATH |
Path to append to tunnel URL (see Webhook URL Composition) |
Tunnel Configuration¶
| Option | Default | Description |
|---|---|---|
--host HOST |
localhost |
Local host to tunnel to |
--timeout SECONDS |
30 |
Timeout waiting for tunnel to establish |
--cloudflared-args ARGS |
— | Flags injected before tunnel --url (default: --no-config); replaces the default entirely when provided |
Usage Examples¶
Basic Tunnel¶
Expose port 8000 and manually copy the webhook URL:
layercode-gym tunnel --port 8000
# Or with a full URL
layercode-gym tunnel --url http://localhost:8000
Auto-Update Webhook (Recommended)¶
Automatically update your agent's webhook and restore it on exit:
# Using environment variables
export LAYERCODE_AGENT_ID="ag-123456"
export LAYERCODE_API_KEY="your-api-key"
layercode-gym tunnel --port 8000 --unsafe-update-webhook
Explicit Agent ID¶
Override the environment variable:
Custom Host¶
Tunnel to a different host:
Cloudflared Config File¶
By default, layercode-gym tunnel passes --no-config to cloudflared so that your local ~/.cloudflared/config.yml is ignored. This prevents named-tunnel ingress rules for other projects from causing silent 404s on quick tunnels.
Use --cloudflared-args to replace this default with any flags you need:
# Keep --no-config and add extra flags
layercode-gym tunnel --port 8000 --cloudflared-args "--no-config --logfile /tmp/cf.log"
# Load your ~/.cloudflared/config.yml instead (drop --no-config)
layercode-gym tunnel --port 8000 --cloudflared-args ""
# Any other cloudflared global flags
layercode-gym tunnel --port 8000 --cloudflared-args "--retries 5"
Note: Whatever you pass to
--cloudflared-argsreplaces the default--no-configentirely. If you want--no-configalongside extra flags, include it explicitly.
How It Works¶
Tunnel Lifecycle¶
- Start:
cloudflared --no-config tunnel --url http://localhost:PORTsubprocess launches - URL Detection: Parses tunnel URL from cloudflared output
- Webhook Update (if enabled):
- Fetches current webhook URL from LayerCode API
- Saves it for later restoration
- Updates webhook to tunnel URL
- Running: Tunnel stays open until Ctrl+C
- Shutdown:
- Checks if webhook is still our tunnel URL (not changed externally)
- Restores previous webhook URL
- Terminates cloudflared process
Webhook URL Composition¶
When --unsafe-update-webhook is enabled, the final webhook URL is composed from two parts:
Example:
Tunnel Base URL: https://random-words.trycloudflare.com
Agent Path: /api/agent
Final URL: https://random-words.trycloudflare.com/api/agent
Agent Path Resolution Priority:
The agent path is resolved in this order (first match wins):
- Explicit
--agent-path- If you provide--agent-path /custom/endpoint, it's used directly - Existing webhook path - If no explicit path, the path is extracted from your agent's current webhook URL
- Default
/api/agent- If no other source available
This means if your agent's webhook is currently https://example.com/api/voice-handler, the tunnel will automatically use /api/voice-handler as the path—no configuration needed.
Examples:
# Use explicit path
layercode-gym tunnel --port 8000 --unsafe-update-webhook --agent-path /webhooks/voice
# Result: https://random.trycloudflare.com/webhooks/voice
# Use path from existing webhook (automatic)
# If current webhook is https://example.com/api/voice-agent
layercode-gym tunnel --port 8000 --unsafe-update-webhook
# Result: https://random.trycloudflare.com/api/voice-agent
# Use default when no existing webhook
layercode-gym tunnel --port 8000 --unsafe-update-webhook
# Result: https://random.trycloudflare.com/api/agent
Webhook Restore Logic¶
The restore is intelligent - it only restores if:
- The current webhook URL matches our tunnel URL
- If yes: restores the saved previous webhook (or clears if there was none)
- If no: leaves it alone (someone else changed it)
This prevents accidentally overwriting webhook changes made during your session.
Environment Variables¶
| Variable | Description | Used For |
|---|---|---|
LAYERCODE_AGENT_ID |
Default agent ID | --unsafe-update-webhook |
LAYERCODE_API_KEY |
API key for LayerCode API | --unsafe-update-webhook |
LAYERCODE_AGENT_PATH |
Path to append to tunnel URL (e.g., /api/agent) |
--unsafe-update-webhook |
Logging¶
Tunnel debug output is saved to timestamped log files:
The log file path is displayed when the tunnel starts:
This keeps your terminal clean while preserving full debug information.
Error Handling¶
cloudflared Not Installed¶
Error: cloudflared binary not found.
Install from: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/
Solution: Install cloudflared (see Prerequisites)
Tunnel Timeout¶
Error: Timed out waiting for tunnel URL after 30s. Check logs at: cloudflare_tunnel_20251211_143022.log
Solution:
1. Check the log file for cloudflared errors
2. Verify internet connectivity
3. Try increasing timeout: --timeout 60
Missing Agent ID¶
Solution: Set the environment variable or provide --agent-id
Missing API Key¶
Solution: Set LAYERCODE_API_KEY environment variable or provide --api-key
Webhook Update Failed¶
The tunnel continues running even if webhook update fails. You can manually copy the webhook URL.
Best Practices¶
1. Use Environment Variables¶
# Add to your .env or shell profile
export LAYERCODE_AGENT_ID="ag-your-dev-agent"
export LAYERCODE_API_KEY="your-api-key"
# Then just run
layercode-gym tunnel --port 8000 --unsafe-update-webhook
2. Use a Dedicated Development Agent¶
Create a separate agent in LayerCode for local development:
# Production agent - never auto-update
layercode-gym api-agents get --agent-id ag-production
# Development agent - safe to auto-update
export LAYERCODE_AGENT_ID="ag-development"
layercode-gym tunnel --port 8000 --unsafe-update-webhook
3. Start Server Before Tunnel¶
The tunnel just exposes a port - make sure something is listening:
# Terminal 1: Start your server
python -m uvicorn myapp:app --port 8000
# Terminal 2: Start the tunnel
layercode-gym tunnel --port 8000 --unsafe-update-webhook
4. Check Logs on Issues¶
If the tunnel misbehaves, check the log file:
Comparison with layercode-create-app¶
| Feature | layercode-gym tunnel |
layercode-create-app --tunnel |
|---|---|---|
| Purpose | Expose any server | Run built-in server + tunnel |
| Server | Bring your own | Included (FastAPI) |
| Webhook Update | Yes | Yes |
| Webhook Restore | Yes | Yes |
| Use Case | Custom backends | Quick prototyping |
When to use each:
- Use
layercode-gym tunnelwhen you have your own backend server - Use
layercode-create-app --tunnelfor quick prototyping with built-in agents
Troubleshooting¶
Tunnel Connects but Webhooks Don't Work¶
- Verify your server is running on the correct port
- Check that your webhook endpoint path is correct
- Test the local endpoint:
curl http://localhost:8000/your-webhook-path
Webhook Not Restored on Exit¶
This can happen if: - The tunnel was killed (SIGKILL) instead of interrupted (Ctrl+C) - The webhook was changed during the session - Network issues during shutdown
Manual restore:
layercode-gym api-agents update \
--agent-id ag-123456 \
--webhook-url https://your-original-webhook.com
Multiple Tunnels Conflict¶
Only run one tunnel per agent at a time. If you need multiple tunnels, use different agents:
# Terminal 1
LAYERCODE_AGENT_ID=ag-dev-1 layercode-gym tunnel --port 8000 --unsafe-update-webhook
# Terminal 2
LAYERCODE_AGENT_ID=ag-dev-2 layercode-gym tunnel --port 8001 --unsafe-update-webhook
Related Documentation¶
- Getting Started - Full setup guide
- API Agents CLI - Manual webhook management
- GitHub Actions - CI/CD integration
Support¶
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- cloudflared docs: Cloudflare Tunnel Documentation