You restart your app and the webhook URL is different again. The provider dashboard that pointed Stripe — or GitHub, or Slack — at https://abc123.ngrok.io/hook now needs https://xyz789.ngrok.io/hook, and someone has to go update it. A persistent, stable webhook endpoint URL that doesn't change every time you restart sounds like it should be a checkbox somewhere. It isn't. Here's why it keeps happening, and what actually fixes it.
Why Your Webhook URL Changes on Every Restart
A webhook receiver needs an inbound path: the sender has to be able to reach you. If your receiver runs on localhost, the internet can't see it, so you put something in front of it — a tunnel, a port forward, a public box. That's where the instability comes from.
The usual culprits:
- Ephemeral tunnel URLs. Free tunnel services hand you a fresh random subdomain each session. Great for a demo, painful for anything you configure once and expect to keep working.
- Dynamic IPs. Your ISP reassigns your public IP. Your port-forwarding rule points at an IP that no longer describes your router.
- CGNAT. Carriers pile customers behind shared public IPs, so there's no public address to forward to at all.
- The machine itself. A laptop moves between Wi-Fi networks. A container gets recreated. A dev box gets a new DHCP lease. Every change is a new URL or a dead one.
The root cause is the same in every case: the receiver has no stable identity on the internet. Its reachability is tied to whatever network it happens to be on at the moment.
What the Usual Fixes Give You
None of these are bad tools. They solve the problem up to a point, and the point matters:
- Reserved tunnel domains. Services like ngrok let you buy a fixed subdomain, so the URL stops rotating. It works, and the URL is stable — but you're paying to lease a name from a middleman, and every delivery now routes through their infrastructure.
- Cloudflare Tunnel. A named tunnel gives you a genuinely stable hostname, and it's well-engineered. You're fronting your local service with a CDN, and the config lives per-machine — but if you want "stable URL, minimal effort," this is a legitimately good answer.
- A VPS with a static IP. The boring, bulletproof option. Put a reverse proxy on a box with a fixed IP and the URL never changes. The catch: you're now running infrastructure, and your service isn't on your machine anymore.
- Overlay VPNs (Tailscale, ZeroTier). These give your machines stable addresses inside your own private network. Excellent for machine-to-machine traffic within your fleet. But a third-party webhook sender on the public internet still needs a public path to you.
For a human-operated server, one of these usually suffices. For an agent — or any process that lives on a laptop, an edge device, or a container that gets recreated — the fit gets awkward, because the receiver keeps moving and restarting, and every move means gluing a new URL onto it.
If the Receiver Is an Agent, the Requirements Change
An autonomous agent doesn't have an ops person to update dashboards after every restart. When the thing behind the webhook is an agent, the endpoint has to:
- stay the same across restarts, IP changes, and moves between networks or clouds;
- be reachable from behind NAT without you configuring port forwarding;
- not depend on a public IP, a dynamic-DNS update, or a per-machine tunnel config.
That's not a DNS problem. It's an identity-and-reachability problem: the receiver needs an address that belongs to it, not to its current network.
Give the Receiver a Permanent Address
This is where overlay networking earns its keep. Pilot Protocol is an open-source overlay network for AI agents: every agent gets a permanent virtual address, assigned by a rendezvous registry, that survives restarts, IP changes, and moves across clouds. Your laptop at a coffee shop and your VM in a datacenter are reachable at the same address, because the address is registry state, not a property of your IP.
The daemon handles the networking itself — STUN discovery, NAT hole-punching, and a relay fallback when a direct path isn't possible — so agents behind home routers and carrier-grade NAT are reachable without you touching a router. Over 243k+ agents and users run on the network today.
Getting your own stable endpoint takes about a minute:
curl -fsSL https://pilotprotocol.network/install.sh | sh
pilotctl info # Address: 0:0000.0000.xxxx — this is your endpoint
pilotctl set-hostname my-webhook-agent # optional: a name peers can resolve
Restart the daemon, switch networks, move the machine to another cloud — the address stays the same. It's assigned once, at registration, and it doesn't re-roll when your environment does. That's the property the tunnel URLs never had.
Receiving Real HTTP Webhooks at That Address
A permanent address is the foundation; here's the part that actually receives webhooks.
The receiver runs its normal HTTP server locally — FastAPI, Express, anything that listens on a TCP port. The Pilot gateway bridges IP traffic to the overlay, so a sender can reach that local service through the encrypted tunnel at your address. No port forwarding, no VPN, no firewall changes on your side.
The flow looks like this:
- Your webhook receiver listens on
localhost:8080. - The gateway bridges the pilot stream to that port on your side.
- A peer dials your address — the same address every time — and their HTTP request lands on your receiver.
- The trust model decides who's allowed in: the receiver approves peers via handshake, so the endpoint isn't an open port on the internet.
One more piece that fits the same puzzle: the daemon itself can emit webhooks for its own events. pilotctl set-webhook http://localhost:8080/events makes the daemon POST JSON notifications about connections, trust changes, and messages — handy for event-driven agent workflows that already speak webhooks.
Which One Should You Pick?
Honest summary:
- If your receiver lives inside your own fleet, an overlay VPN or a static-IP box is fine. Nothing wrong with either.
- If you want a stable URL with minimal effort and you're okay with a CDN in front, Cloudflare Tunnel is a good answer.
- If the receiver is an agent — or you want an address that's the same everywhere, with no port forwarding and nothing to reconfigure after a restart — an overlay network with a permanent address is the shape that matches the problem.
The test is simple: restart the receiver, then check whether the URL you configured still works. If it does, you're done. If it doesn't, the fix isn't a better URL — it's an address that doesn't change.
curl -fsSL https://pilotprotocol.network/install.sh | sh
Top comments (0)