Teardown
Destroying a deployment — what disappears, what's irreversible, and when this is actually the right move.
Teardown is the destructive operation. The Railway project is deleted, the Neon branch is dropped, the R2 bucket is emptied, the Cloudflare Worker Custom Domain is removed, KV mappings are wiped, and the deployment row is marked destroyed. Tenants lose every piece of data the tool produced. There is no undo.
This is rarely the right answer while debugging a problem. Suspend instead, fix the issue, and resume. Reach for teardown when the deployment is genuinely meant to end.
Teardown is irreversible. There is no soft-delete, no recovery window, no backups outside what your tool's storage providers retain by their own policy. Tell tenants exactly what disappears before they confirm.
What teardown destroys
| Resource | Destroyed by teardown |
|---|---|
| Railway project (compute, services, volumes) | Yes |
| Neon Postgres branch (schema + data) | Yes |
| R2 bucket and contents | Yes |
| Cloudflare Worker Custom Domain | Yes |
KV mapping deploy:{subdomain} | Yes |
vendo_api_key (proxy key) | Yes (revoked + KV key wiped) |
app_connection_bindings for this deployment | Yes |
deployment_env_vars | Yes |
deployment_credentials | Yes |
deploy_logs | Yes |
deployments row | No — flipped to status='destroyed', retained for audit |
apps row | No — retained, but disconnected_at is set to now() by the disconnect_authorization phase |
usage_events | No — retained for billing history |
What survives
- Audit data —
deployments,apps,usage_eventsrows are retained. This is for billing and support, not for recovery. - Other deployments of the same tool. Teardown is scoped to one deployment row. The same tenant deploying the same tool again is a fresh row with fresh state.
- The tool's catalog entry. Tearing down a tenant's instance has zero effect on the catalog.
Who can trigger it
- Tenant — dashboard "Destroy" button. Confirmation dialog with a typed-confirmation field.
- Suspension reaper — automatic after 90 days suspended (see Suspend & resume).
- Internal teardown via support — for stuck deployments where the workflow can't recover.
Tool authors don't tear down tenant deployments. Even with admin access on the catalog entry, teardown is a tenant action.
The workflow
TeardownWorkflow (cloudflare/deploy-worker/src/workflows/teardown.ts) runs idempotent phases:
fetch_deployment— load the row, short-circuit if alreadydestroyed.teardown_compute—projectDeleteon Railway (or equivalent on other providers).teardown_databases— drop the Neon branch (and any other database providers attached).remove_dns— remove the Cloudflare Worker Custom Domain / DNS record.revoke_proxy_keys— invalidatevendo_api_key(and any other proxy keys bound to the deployment).delete_r2_bucket— empty and delete the deployment's R2 bucket.wipe_credentials— null outdeployment_credentialsanddeployment_env_varsrows.disconnect_authorization— setapps.disconnected_at = now()on the linkedappsrow so the catalog UI knows this binding is soft-detached.finalize—status='destroyed',destroyed_at=now().
Each phase checks current state before acting — re-running the workflow on a partially-destroyed deployment is safe.
teardown_compute is the phase that matters most for cost — orphaned Railway projects keep accruing charges. If teardown fails at this step, verify the Railway project still exists, delete it manually, and retry the teardown (POST /api/deployments/[id]/destroy).
Edge cases
- Teardown on a
faileddeploy that never recorded aprojectId. The workflow short-circuits cleanly becausedeploy_computeguards againstprojectDelete(undefined). No manual cleanup needed. - Teardown on
suspended. Works the same way. The compute is already paused, but the workflow still callsprojectDeleteto fully release resources. - Teardown during an in-flight upgrade. The upgrade is preempted. If the image swap completed but the readiness check was still polling, the teardown drops the now-running new revision along with everything else.
What tenants see
After teardown:
- The deployment URL —
{tenant_slug}-{deployment_slug}.vendo.runfor new deployments (single-level format), or the legacy{deployment_slug}.{tenant_slug}.vendo.runfor older ones — returns NXDOMAIN within minutes (CF DNS propagation). - The deployment row hides from the dashboard's default Apps view (filter
status != 'destroyed'). - The dashboard's history view still shows the row with a "Destroyed" badge for audit.
When teardown is the right move
- The tenant is decommissioning the workload permanently.
- They want to re-deploy from scratch with a different
deployment_slug. - A
faileddeployment can't be retried (rare — almost always retry first). - A
resume_faileddeployment is permanently broken (rare — retry first, escalate to support before tearing down).
In every other case, prefer suspend (free) or restart (cheap, lossless).