What a Small Cloud Run Site Really Costs — Scale to Zero, Cold Starts, Memory Limits, Max Instances, and the Line Items That Are Not Free
How Cloud Run bills a low-traffic Node.js site, why min-instances 0 keeps it near zero, what a cold start costs you in latency, how memory and max-instances settings change the bill, and the small charges people forget: image storage, egress and the build.
The pitch for Cloud Run is that you pay only while requests are running. For a small content site that is true, and the monthly bill is usually coins, but a few settings and side services decide whether it stays that way. Here is how it actually adds up.
What you are billed for
| Item | When it is charged |
|---|---|
| CPU and memory time | While an instance is handling requests (and, if enabled, while idle with CPU always allocated) |
| Requests | Per million, with a free tier |
| Container image storage | Continuously, for every image you keep in Artifact Registry |
| Cloud Build minutes | Each --source deploy builds an image |
| Network egress | Bytes served to the internet beyond the free allowance |
The first two have generous free tiers that a small site rarely exceeds. The last three are small but not zero, and they are the ones that surprise people looking at a bill for a site with no visitors.
Scale to zero
--min-instances 0 means no instance runs when there is no traffic, so CPU and memory time is zero most of the day. The price is a cold start on the first request after idle: for a small Express app on a slim Node image this is typically under a second, sometimes two. For a content site that is acceptable; for an API behind a mobile app it may not be, and then --min-instances 1 buys away the cold start for a predictable monthly fee.
Keep the container small and fast to start
Cold-start time scales with image size and startup work. Practical rules: use node:22-slim, install with npm ci --omit=dev, keep .dockerignore tight, and do any content parsing once at startup rather than per request. Loading a few hundred Markdown files into memory at boot is fine; fetching from a database on boot is what makes cold starts hurt.
Memory and CPU
An Express site serving Markdown runs comfortably in 256 MiB. Larger limits cost more per second of request time, so do not default to 512 MiB or 1 GiB "to be safe"; raise it only if you see memory errors in the logs. CPU is allocated only during requests by default, which is what you want for a site.
Max instances
--max-instances 2 (or a similarly small number) is a safety cap. A crawler storm or a mistake in your own code cannot spin up fifty instances and turn a free site into an invoice. Raise it deliberately when real traffic needs it.
The charges people forget
- Old images. Every deploy leaves an image in Artifact Registry. Set a cleanup policy or delete old versions occasionally.
- Builds.
--sourcedeploys use Cloud Build minutes; frequent deploys of a big context cost more than the site itself. Keep the upload small with.dockerignore. - Egress. Large images and PDFs served often add up. Compress images (see sharp recipes) and set cache headers on static files.
- Logs. Verbose per-request logging fills Cloud Logging; keep it to errors and one startup line.
Check your own bill
In the billing console, filter by the Cloud Run service and by Artifact Registry. A healthy small site shows a few cents for Cloud Run, a few cents for storage, and nothing else.
Common mistakes
- Setting
min-instances 1on a hobby site "for speed." It becomes the whole bill. - Oversizing memory. Request time is billed per MiB.
- Never deleting images. Storage grows with every deploy.
Summary
Scale to zero, keep the image slim so cold starts stay short, cap memory at 256 MiB and instances at a small number, and clean up old images. Done that way, a small Cloud Run site costs about as much as a domain renewal per year, not per month. Setup details are in deploying Express to Cloud Run.