Most side-project cloud bills are not big because the app is popular. They are big because something is running that nobody remembers turning on, or because a "free" managed tier quietly graduated to a paid one. The fastest wins come from deleting idle resources, right-sizing what's left, and moving spiky background work off always-on machines — none of which touches your uptime. The changes that do risk uptime (dropping to a single instance, aggressive autoscaling-to-zero) come last, and only with eyes open.
This is the checklist I run when a hobby project's bill creeps past what the project earns — which, for a side project, is usually zero. It's ordered from "pure savings, no downside" to "real trade-offs, decide deliberately."
Where does the money actually leak?
Before optimizing anything, find out what you're paying for. Almost every cloud bill has a line item that makes you say "wait, what is that?" On AWS the answer is usually one of: an orphaned load balancer, a provisioned database you spun up for a test, an old EBS/persistent disk detached from any instance, NAT Gateway data processing charges, or egress bandwidth. On the smaller platforms (Render, Railway, Fly.io) it's usually a second service you forgot was deployed, or a plan that auto-upgraded when you crossed a resource threshold.
Pull the actual line items before touching anything:
# AWS: top spend by service for last month, largest first
aws ce get-cost-and-usage \
--time-period Start=2026-07-01,End=2026-08-01 \
--granularity MONTHLY \
--metrics UnblendedCost \
--group-by Type=DIMENSION,Key=SERVICE \
--query 'ResultsByTime[0].Groups[?Metrics.UnblendedCost.Amount>`1`]' \
--output table
The single most common surprise I hit is the NAT Gateway. It has an hourly charge plus a per-GB data processing charge, and a chatty background job pulling from S3 through it can cost more than the compute doing the work. If your workload doesn't strictly need private-subnet outbound, a VPC gateway endpoint for S3 (free) or just running in a public subnet removes that line entirely.
Takeaway: you cannot cut a bill you haven't itemized — the first hour is reading the invoice, not changing infrastructure.
What can I delete right now with zero risk?
This is the free money tier. None of it affects a running app.
- Detached disks and old snapshots. An EBS volume or persistent disk with no instance attached bills at full rate forever. Automated snapshot schedules from a project you abandoned six months ago accumulate silently.
- Idle load balancers. An ALB/NLB with no healthy targets still bills hourly. If a single small instance serves your traffic, you often don't need a load balancer at all — point DNS straight at it or use the platform's built-in routing.
- Duplicate environments. The staging copy you cloned to reproduce a bug and never tore down. For a solo project, "staging" can be a local Docker Compose file that costs nothing.
- Over-provisioned logging and metrics retention. Default log retention is often "never expire." A hobby project does not need 400 days of debug logs in paid storage; 7–14 days is plenty.
# AWS: find unattached EBS volumes still billing you
aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query 'Volumes[].{ID:VolumeId,GiB:Size,AZ:AvailabilityZone}' \
--output table
The tool that pays for itself here is your cloud provider's native cost dashboard combined with a resource tag you apply from day one — tag everything with the project name at creation, and orphans become obvious because they're the untagged ones.
Takeaway: the cheapest resource is the one that no longer exists — delete before you optimize.
Am I paying for capacity I never use?
Side projects are almost always over-provisioned, because you picked an instance size once, under load-test anxiety, and never revisited it. A hobby API serving a few requests a minute does not need 4 vCPUs.
Two moves, in order of safety:
- Right-size the instance. Look at a week of CPU and memory metrics. If p95 CPU sits under 20% and memory has comfortable headroom, drop one size. This is reversible in minutes and, on a single-instance app, causes a brief restart at most.
- Commit to what you know you'll keep. If you're certain a database or instance will run for a year, a committed-use discount (AWS Savings Plans / Reserved Instances, GCP committed use) knocks 30–60% off the exact same resource with no performance change. As of mid-2026 this is the highest-leverage no-risk discount on the big clouds — but only commit to baseline capacity you'd run anyway, never to peak.
The trap with right-sizing is databases. A managed Postgres instance under memory pressure will thrash its cache and get slower under load, which reads as an uptime problem even though the instance is "up." Right-size compute aggressively; right-size databases only after checking that your working set still fits in RAM.
Takeaway: measure a week of real utilization before resizing — the instance size you chose on day one was a guess, not a measurement.
Should I move background work off my always-on server?
This is where side projects overpay structurally. A single always-on instance sized for occasional heavy work — a nightly report, an image resize, a scheduled scrape — burns money 24 hours a day to cover a job that runs for 90 seconds.
Split the workload by shape:
| Workload shape | Cheapest fit for a side project | Why |
|---|---|---|
| Steady HTTP traffic, needs to be always warm | Small always-on instance or container | Predictable, avoids cold-start latency on your critical path |
| Spiky/scheduled background jobs | Serverless functions or scheduled containers | You pay per execution, not per idle hour |
| Rare, heavy batch (minutes, infrequent) | Spot/preemptible instance or a scale-to-zero container | Interruptible work tolerates cheap, interruptible compute |
| Static frontend | Object storage + CDN | Near-free at hobby scale, and faster than serving from your instance |
Moving a scheduled job from an always-on box to a serverless function is the change with the best savings-to-risk ratio I know of: the user-facing app keeps its warm instance, and the background cost drops to near-nothing because you stop paying for idle. If you want the managed version of scale-to-zero for a whole web service, Fly.io's auto-stop/auto-start and Render's ability to suspend services both handle the pause-when-idle case without you writing any orchestration.
Takeaway: pay for idle only on the path that must stay warm; everything else should bill per execution.
When does saving money start costing uptime?
Some cuts do trade availability, and you should make them consciously rather than discover the trade at 2am.
- Dropping to a single instance / single AZ. For a side project this is usually the right call — multi-AZ redundancy roughly doubles compute cost to protect against a rare event on something nobody is paged for. Just know that a zone outage or a bad deploy now means downtime, and set expectations (a status note, not a pager) accordingly.
- Scale-to-zero on the user-facing service. It eliminates idle cost but adds a cold start — anywhere from sub-second to many seconds depending on runtime and image size — to the first request after idle. Fine for a personal tool; noticeable for anything you'd demo. Keep a small always-warm floor if first-impression latency matters.
- Spot/preemptible for anything stateful. Cheap, but the provider can reclaim the machine with little warning. Great for a retryable batch job; a good way to corrupt data if you put a database on it.
The honest framing: for a project with no revenue and no SLA, single-instance and scale-to-zero are usually correct, and the "downtime risk" is a few seconds of cold start or a rare zone blip. The mistake is applying the same aggressiveness to the one component that holds state.
Takeaway: cut redundancy on stateless, retryable, non-critical paths freely — protect the one place that holds data you can't rebuild.
FAQ
How can I reduce my AWS bill for a hobby project without downtime?
Start with deletions that touch nothing running: unattached EBS volumes, idle load balancers, old snapshots, and duplicate environments. Then right-size over-provisioned instances using a week of utilization metrics, and move scheduled background jobs to serverless so you stop paying for idle compute. Only after that consider single-AZ or scale-to-zero, which trade a little availability for cost.
What is the most common hidden cost on a cloud bill?
For AWS side projects it's usually the NAT Gateway (hourly plus per-GB data processing) and data egress bandwidth; on Render, Railway, and Fly.io it's a forgotten second service or a plan that auto-upgraded when you crossed a resource threshold. Always itemize the invoice by service before optimizing.
Does scale-to-zero cause downtime?
It doesn't cause downtime, but it adds a cold start to the first request after an idle period, which can range from under a second to several seconds depending on runtime and image size. Keep a small always-warm floor for anything where first-request latency matters, and use scale-to-zero freely for personal tools.
Bottom line
Run the checklist in order and stop when the savings stop being worth your time. Delete orphaned resources and shrink log retention first — that's free money with no downside. Right-size compute against real metrics and commit to baseline capacity you'll keep anyway. Move spiky background work to per-execution billing so you stop renting idle hours. Only then, and only deliberately, trade redundancy for cost on the stateless parts of the system — and never on the one component holding data you can't afford to lose.
Top comments (0)