Skip to content
Scalpels home

Scalpels is officially launching on October 31, 2026. Special early adopter pricing is available now.

← All guides

Guide

A Comprehensive Agent's Guide to Laravel Cloud

Clean up safely

· Ed Grosvenor

Deleting an application does not delete what it provisioned. Teardown is its own operation, and every step of it is irreversible. This page is about finding what was left behind and removing it without being wrong.

What deletion takes, and what it leaves

application:delete removes the application's environments, their instances (managed queues included), background processes, deployments and domain bindings.

It does not remove:

  • database clusters or the databases inside them,
  • caches,
  • buckets and their keys,
  • WebSocket applications,
  • DNS records you created at your own provider, which keep resolving to nothing.

All of those keep billing. An audit of our own organization found four entire database clusters, three more abandoned databases inside clusters still in use, two caches, a WebSocket application and several buckets left behind by deleted applications. The $2.02 a period we could attribute directly was only a floor, because buckets, abandoned databases and hollow environments could not be priced individually.

Two more places cost hides:

  • Incomplete multipart uploads to a bucket are billed and do not appear in object listings. If your code starts a multipart upload, it must abort it on failure. Throwing away the upload ID leaves the parts billing indefinitely.
  • First-run tooling can leave spares. cloud ship created one or two extra empty databases in a cluster, one named after the environment and one after the application.

Deleting a parent takes its children:

  • Deleting a database cluster deletes every database and snapshot in it.
  • Deleting a bucket deletes its keys and every object in it. Cloud did not ask us to delete the keys first. There is no recovery.
  • Deleting a WebSocket cluster deletes every application in it.

Audit children against their parents

The dependable orphan audit runs from each environment to its children:

cloud application:list --json -n --fields=id,name,environments.id,environments.name
cloud environment:get <environment-id> --json -n --fields=id,name,databaseSchemaId,cacheId,websocketApplicationId
cloud database-cluster:list --json -n --fields=id,name,schemas
cloud cache:list --json -n --fields=id,name,status
cloud websocket-cluster:list --json -n --fields=id,name
cloud websocket-application:list <cluster-id> --json -n --fields=id,name
cloud bucket:list --json -n --fields=id,name
cloud bucket-key:list <bucket-id> --json -n --fields=id,name,permission
cloud instance:list <environment-id> --json -n --fields=id,name,type
cloud database-snapshot:list <cluster-id> --json -n --fields=id,createdAt

Run -h on each command first and adjust --fields to what your version offers. Never drop the --fields: several of these print credentials without it.

Fetch every environment individually and collect its databaseSchemaId, cacheId and websocketApplicationId. That set is your reference graph. There is no reliable bulk shortcut. The relationship fields on the listings are empty even when resources are in use. See Reads that lie.

Then enumerate every resource type. Anything outside the reference graph is a candidate, not yet an orphan. Fetch the candidate itself and inspect the environment that ought to own it before deciding. An empty relation array is never proof that a resource is unused.

Pay special attention to these cases:

  • Database clusters and databases are not the same thing. A cluster is orphaned only when every database inside it is unreferenced. A used database and an abandoned one often share a cluster. Delete the unused database, not the cluster.
  • Buckets cannot be proved orphaned from Cloud's relationships. They have no environment field, and Cloud injects the default disk configuration outside the environment variables you can inspect. Classify them as unproven. A bucket named after a deleted application is a probable orphan, but inspect its contents before deleting it.
  • Look for hollow environments. An environment can be running and billing compute while having no database, no cache and no meaningful deployment.
  • Inspect managed queues. Queue creation defaults the minimum to one replica, which keeps a worker alive permanently. Check whether each queue should scale to zero. Managed queues have no listing of their own: they appear in instance:list <environment-id> with the type managed_queue.
  • Shared resources have more than one parent. One cache can serve both production and staging. It is in use if any environment references it.

Make deletion boring

Build a manifest before deleting anything. Store the full identifier, resource type, proposed owner, evidence, and the number of resources you expect to remove.

Then follow these rules:

  • Compare full identifiers. A WebSocket cluster ws-00000000-1111-2222-3333-444444444444 and its application wsa-00000000-1111-2222-3333-444444444444 can share a UUID stem. Deleting the cluster deletes every application in it.
  • Prove deletion by absence from a fresh list. A get against a deleted WebSocket application can return an unrelated "multiple clusters found" error rather than not-found. Absence is the evidence; an error is not.
  • Never test a write with a made-up identifier. Before v0.6.2, the CLI could resolve an unknown environment ID to another environment and write there. Application IDs can still resolve to a different application.
  • Record the manifest count separately from its list. Stop if the count and the list length disagree. This caught a brief that said 15 objects over a manifest listing 16.
  • Re-enumerate immediately before deletion. In one run, a new application appeared between the audit and the approved deletion, and the run stopped rather than act on a stale picture. A new application or attachment can invalidate yesterday's decision.
  • Prove the organization again before the first delete. See Work in the right organization.
  • Delete children before parents: unused databases, then WebSocket applications, then caches, then empty clusters, then buckets.
  • After deletion, verify that every environment is still running and every expected resource reference is intact.

Holding an uncertain candidate costs nothing beyond its ordinary bill. Deleting the wrong parent can cost the data beneath it. Make uncertainty stop the run.

After a teardown

  • Update or remove any .cloud/config.json that names the deleted application. A stale binding can resolve to a different application. See Work in the right organization.
  • Remove the DNS records you created at your own provider.
  • Find anything still sending traffic to the deleted application. Telemetry clients, webhooks and integrations kept posting to one of ours until they were reconfigured.
  • Correct notes, briefs and memories that name the old identifiers. One of our briefs, written four days after a teardown, named the deleted environment. Executed as written, it would have revoked a live credential belonging to another of our sites.

Want some help?

After you buy, we will pay for an hour with one of these experts to help you get everything up and running.

  • Artisan Build

    USA

    We run Scalpels, as well as a suite of other tools for developers and development teams.

    Agency Ask for help
    Ask Artisan Build for help