feat(connect): release the Cloudflare tunnel when the environment shuts down - #4531
Merged
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Cloudflare bills per provisioned tunnel regardless of traffic, so per their team's guidance ("delete the tunnel when the user stops the server, otherwise you will have a bunch of idle tunnels") the managed tunnel is now disposed when the environment goes offline, while reconnects keep the exact same URL.
Disabling Connect (
t3 connect unlink, web/mobile unlink, publish-only downgrade) already deleted the tunnel via the relaydeprovisionpath. The gap was stopping the server: only the localcloudflaredprocess was killed, and the provisioned tunnel idled in Cloudflare indefinitely. This PR covers that case.Relay
ManagedEndpointProvider.release: deletes only the Cloudflare tunnel (404-tolerant) and deliberately keeps the allocation row and DNS record.(stage, userId, environmentId)and the tunnel-name reservation persists, so the nextprovisionrecreates the tunnel under the same name, repoints the CNAME to the new tunnel id, and returns a fresh connector token — same endpoint URL.tunnelIdalso keepsconnect/statusauthorization intact, so an offline environment still reports "offline" from the health probe instead of regressing to "not authorized".DELETE /v1/client/environment-links/:environmentId/tunnel.Server
releaseManagedTunnelOnShutdown: stops the connector, calls the release endpoint with the stored CLI token, then drops the cached endpoint runtime config — the connector token dies with the tunnel, so the next boot waits for the link reconcile instead of respawningcloudflaredwith a dead token.reconcileDesiredCloudLinkon startup re-links and re-provisions automatically.A hard kill (crash/power loss) still leaves a tunnel behind since nothing can run; the next link reuses it, so nothing accumulates. A relay-side idle sweep would be a possible follow-up.
Tests
🤖 Generated with Claude Code
Note
Release the Cloudflare tunnel via relay DELETE endpoint when the environment shuts down
releaseManagedTunnelOnShutdownin http.ts that stops the local connector and callsDELETE /v1/client/environment-links/:environmentId/tunnelon the relay with a bearer token.CLOUD_ENDPOINT_RUNTIME_CONFIGonly if it still matches the released config, preserving a freshly written config from a concurrent fast restart.ManagedEndpointProvider.releasein the relay, which uses a newclaimReleaseoptimistic-lock check (matchingtunnelIdandupdatedAt) before deleting the Cloudflare tunnel, leaving the allocation row and DNS record intact.Macroscope summarized 59b16af.
Note
Medium Risk
Changes managed tunnel lifecycle, shutdown finalizers, and relay allocation locking; failures leave tunnels running (existing fallback) but incorrect release could affect connectivity or billing.
Overview
Managed tunnels are torn down when a CLI-linked environment stops, so idle Cloudflare tunnels are not left billing after the server goes offline. The environment link and hostname stay in place; startup reconcile can provision a replacement tunnel at the same URL.
On the relay, a new authenticated
DELETE …/environment-links/:environmentId/tunnelcallsManagedEndpointProvider.release, which deletes only the Cloudflare tunnel and keeps the allocation row and DNS.claimReleaseusesupdatedAtas a generation lock so a release that races a concurrent provision returns{ ok: false }and does not delete a freshly provisioned tunnel.On the server,
releaseManagedTunnelOnShutdownstops the local connector, issues that DELETE with the stored CLI token, and removesCLOUD_ENDPOINT_RUNTIME_CONFIGonly when the relay confirms release and the stored config still matches what was released (so a fast restart orok: falsekeeps the live token/config). This runs as a 10s best-effort shutdown finalizer for cloud-enabled servers; it applies only to CLI-desired managed links—not publish-only or web/mobile-installed links.Reviewed by Cursor Bugbot for commit 59b16af. Bugbot is set up for automated code reviews on this repo. Configure here.