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

Attribute cost

· Ed Grosvenor

Laravel Cloud gives you one total, but that total is not the answer you usually need. You need to know which application is responsible, which resources are still useful, and whether something you deleted months ago is still billing you today.

You can get there. The important part is being clear about what Cloud links for you and what you have to infer.

How Cloud bills

  • Compute is billed per second, capped each month at 2,419,200 seconds, which is 28 days.
  • Billing periods follow the organization's billing cycle, not calendar months. Cloud exposes four of them. The CLI's --period accepts current, previous, 1, 2 or 3.
  • There is no per-day or per-hour cost. Environment metrics have CPU, memory and replica series over shorter windows, but those are utilization, not money.
  • Usage figures lag by 30 to 60 minutes. Check lastUpdatedAt.

For a sense of scale, from our own organizations in September 2026: the smallest current public instance size, flex-512mb, was about $6 a month. A single-binary application cost about $7 a month, while a Rails app with a worker, Postgres, Valkey and Reverb cost about $33. Serverless Postgres clusters ran from a cent to under a dollar per period.

Start with the billing-period usage

Export the previous billing period first:

cloud usage --json -n --period=previous > cloud-usage.json

Use previous when you are reconciling an invoice. current is still changing.

All money in the response is integer cents. Read totalCents or totalCostCents rather than trying to rebuild a total from every subtype. Database fields such as storageCents and computeCents can be null while totalCents is correct.

The usage response is also not an inventory. An application deleted halfway through the period can still appear in usage but be absent from application:list, while a current application with no spend can be absent from usage.

If you need instance-level compute for one suspicious environment, ask for it explicitly:

cloud usage --json -n --period=previous --environment=<environment-id> > environment-usage.json

Without --environment, environmentUsageItems is empty. Fetching it for every environment costs one extra API call per environment, so investigate the application you care about instead of hammering the whole organization.

Prove that the totals reconcile

Before attributing anything, check the arithmetic in cloud-usage.json:

  1. Add every application's totalCostCents. It must equal applicationsTotalCostCents.
  2. Add every database, cache, bucket and WebSocket totalCents. It must equal resourcesTotalCostCents.
  3. Add applications, resources, add-ons and bandwidth. It must equal currentSpendCents.
  4. For an individual application, add the compute totals of its environments. It must equal that application's compute total.

This is the trust check. Stop if the numbers do not reconcile. Do not build a tidy attribution report on top of an incomplete export or a changed billing period.

Bandwidth remains one organization-level figure against an allowance. Leave it unattributed rather than inventing a split.

Label linked cost differently from inferred cost

Application compute carries a real app- identifier. Join that identifier to the output of:

cloud application:list --json -n --fields=id,name,environments.id,environments.name > cloud-applications.json

That cost is linked. Note the --fields: without it, this listing prints every environment's build command, credentials included.

Attached resources are different. Databases, caches, buckets and WebSockets do not carry a dependable application or environment key in either direction. Relation arrays can be empty even when a resource is in use. On one 22-app organization, compute was about 37% of the bill and resources were about 63%, so pretending the whole bill is linked would make most of the report false.

Give every line one of four labels:

  • linked: Cloud supplies the relationship, as it does for compute.
  • inferred: a resource name matches an application by a repeatable rule.
  • override: you pinned a known exception by hand.
  • unattributed: no unique owner can be supported.

An explicit unattributed row is more useful than a confident guess.

Shared resources make this harder. One cache and one WebSocket application served both a production and a staging environment in our census. If you split a shared resource, say how, and label the split override.

Match resource names, then keep the exceptions

Cloud commonly names an auto-provisioned resource after its application, sometimes with the environment appended. Use that convention as evidence, not as a hidden assumption:

  1. Lowercase names and remove non-alphanumeric characters. example-shop.com and example_shop both become exampleshop.
  2. Index each application's slug and name, both intact and with a known top-level domain removed.
  3. Add variants with every environment name appended, such as exampleshopproduction.
  4. Try an exact match first, then the longest key that the resource name starts with.
  5. Drop keys shorter than four characters, because they collide too easily.
  6. If one key matches more than one application, mark the resource unattributed and report the candidates.

Keep the exceptions in a small JSON file that can be reviewed with the report:

{
  "legacy-primary": "example-shop"
}

Pass that same map into every run. Do not bury one-off decisions in code, or make somebody rediscover them next month.

This method moved one real organization's resource attribution from 83.6% to 97.9%. The remaining rows were not failures of the report. They were the useful part of it.

Read the residue

A paid resource that matches no application is a finding. Nobody can name its owner, so nobody is likely to be watching it.

A zero-cost unmatched resource is still worth checking. It may be harmless clutter today and a charge tomorrow. Confirm what it is, record the decision, and remove it only when you can prove it has no owner. Clean up safely covers how.

Do not paste raw resource listings into an issue or chat. cloud cache:list --json printed live passwords inside its connection block on CLIs before v0.6.0, and still prints connection details on current ones. Extract only the identifiers, names and state you need.

Also slow down enumeration loops. The CLI can rate-limit repeated per-cluster or per-bucket calls. A rate-limited check is inconclusive, not a pass.

Read cost shape for waste

Cost can point at waste even without metrics:

  • A serverless database whose cost barely changes between periods never scales down. If its spend varies by less than about 10% across billing periods, something keeps it awake all the time.
  • A queue billing every hour of every day has a minimum of one replica. See Provision and deploy.
  • An environment billing compute with no database, no cache and only APP_KEY set is hollow. We found one left behind by a product rename.

Cost data tells you that something never sleeps. It cannot tell you what keeps it awake. Hibernation and idle cost covers that, and how Hone answers it from your own telemetry.

If you want the attribution algorithm and report format ready-made, use the public laravel-cloud-cost skill. It reconciles the totals to the cent, labels every line as above, and deliberately leaves build commands and connection blocks out of its output.

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