| name | local-publish |
| description | Publish a local service (Node.js, Python, any HTTP server) to the public internet on macOS using Cloudflare Tunnel + launchd LaunchAgent. Use when setting up a new local web service that should be accessible via a public domain, adding a Cloudflare Tunnel to an existing local server, managing (status/restart/logs) a deployed local-publish service, or tearing down a published service. Triggers on phrases like 'publish local service', 'expose local port', 'set up CF tunnel', 'make local server public', 'add cloudflare tunnel', 'deploy local dashboard'. |
local-publish
Publishes a local HTTP service to the internet via Cloudflare Tunnel, managed by macOS launchd (auto-start on login, auto-restart on crash). No public IP or port forwarding needed.
Architecture
Browser → yourdomain.com (Cloudflare DNS)
→ Cloudflare edge
→ cloudflared tunnel client (LaunchAgent on Mac)
→ localhost:<PORT> (your server)
Two LaunchAgents per service:
com.<label>.server — runs the HTTP server process
com.<label>.cloudflared — runs the cloudflared tunnel
Prerequisites
brew install cloudflared
cloudflared tunnel login
Proxy note: cloudflared must reach Cloudflare's network. If behind a proxy (e.g. Clash), set HTTP_PROXY/HTTPS_PROXY in the LaunchAgent and use --protocol http2 to avoid QUIC/fake-ip issues.
Setup Workflow
1. Create the Cloudflare Tunnel
cloudflared tunnel create <tunnel-name>
2. Add DNS Record
cloudflared tunnel route dns <tunnel-name> <subdomain.yourdomain.com>
3. Create Tunnel Config
~/.cloudflared/<service>.yml:
tunnel: <tunnel-id>
credentials-file: /Users/<user>/.cloudflared/<tunnel-id>.json
ingress:
- hostname: <subdomain.yourdomain.com>
service: http://localhost:<PORT>
- service: http_status:404
4. Create LaunchAgents
Use scripts/create_launchagents.py to generate both plist files, or follow the templates in references/plist-templates.md.
Place plist files in ~/Library/LaunchAgents/.
5. Load and Start
launchctl load ~/Library/LaunchAgents/com.<label>.server.plist
launchctl load ~/Library/LaunchAgents/com.<label>.cloudflared.plist
Management Commands
launchctl list | grep <label>
launchctl kickstart -k gui/$(id -u)/com.<label>.server
launchctl kickstart -k gui/$(id -u)/com.<label>.cloudflared
launchctl unload ~/Library/LaunchAgents/com.<label>.server.plist
launchctl unload ~/Library/LaunchAgents/com.<label>.cloudflared.plist
tail -f <workdir>/logs/server.log
tail -f <workdir>/logs/cloudflared.log
Teardown
launchctl unload ~/Library/LaunchAgents/com.<label>.server.plist
launchctl unload ~/Library/LaunchAgents/com.<label>.cloudflared.plist
rm ~/Library/LaunchAgents/com.<label>.{server,cloudflared}.plist
cloudflared tunnel delete <tunnel-name>
Reference
- Plist templates and field explanations:
references/plist-templates.md
- Script usage:
scripts/create_launchagents.py --help