Cost, Scaling & Not Getting Hacked
Every few months a student posts a screenshot of a five-lakh-rupee AWS bill from a “free tier” account they forgot about, or a startup makes the news because an engineer pushed an access key to a public repo and bots drained the account in hours. Both disasters are completely preventable, and the prevention takes minutes. This module is the three things that keep a cloud account from ruining your week: not getting billed by surprise, scaling the right way, and not getting hacked.
The Goal
By the end of this module you can:
- Set a budget alarm — the first thing you do on any new cloud account, before deploying anything
- Name the free-tier traps that silently bill you and explain why “free tier” is not “free account”
- Distinguish horizontal from vertical scaling in cloud terms and say which one the cloud is actually good at
- Apply the security basics juniors get wrong — least-privilege IAM, never committing keys, locked-down security groups
- Judge when a team should actually move off a PaaS onto raw cloud, and when that is premature
The Lesson
Do this first, on every account, before anything else: a budget alarm
The single most important action on any new cloud account is not deploying — it is setting a budget alarm. The cloud bills by the second for things you forgot are running. There is no “you’ve spent enough, we’ll stop now” by default; the meter just runs. A budget alarm emails you when spend crosses a threshold you set, so a forgotten resource costs you ₹200 and an email, not ₹50,000 and a panic.
On AWS: Billing → Budgets → Create budget, set a monthly amount (start at ₹500 if you are learning), and an alert at 50% / 80% / 100%. On GCP it is Billing → Budgets & alerts; on Azure, Cost Management → Budgets. Five minutes, once, and you have removed the worst-case outcome of the entire cloud.
flowchart LR
Spend[Daily cloud spend accrues] --> Check{Crossed your threshold}
Check -->|no| Spend
Check -->|yes| Alert[Email and notification fire]
Alert --> You[You log in and kill the runaway resource]
NoAlarm[No budget alarm set] -.silent.-> Bill[Surprise bill at month end]
Read the two paths: with an alarm, spend crosses a line and you get told while it is still small; without one, the first signal is the invoice. A budget alarm does not stop spending — it tells you. For a hard stop you would need automation that deletes resources on alert, which is advanced; for a learner, the email is enough because you can react in minutes.
The honest reason this matters more for you than for a funded company: you have zero budget. One forgotten GPU instance or a misconfigured data-transfer loop can wipe out a month of what you would have saved. The alarm is your seatbelt.
The free-tier traps — “free tier” is not “free account”
A cloud free tier is a set of specific, limited allowances — not a blanket “nothing costs money.” The trap is assuming the account is free when only certain usage is. The things that silently bill you:
| Trap | What silently bills you |
|---|---|
| Always-on resources | An EC2 instance or RDS database left running 24/7 burns its free hours, then bills per hour — even with zero traffic. Idle is not free. |
| The 12-month cliff | AWS’s headline free tier (e.g. 750 EC2 hours) lasts 12 months from signup. On month 13 the same resources start billing at full price. People forget the clock. |
| Data transfer / egress | Data out of the cloud to the internet is charged per GB after a small free allowance. A leaked URL serving a large file, or a chatty service, racks this up quietly. |
| Storage that outlives compute | You delete the server but leave the disk (EBS volume), the database snapshot, the S3 bucket. Storage bills monthly forever until you delete it. |
| Per-request and “managed” services | NAT gateways, load balancers, and many managed services bill hourly regardless of use — small numbers that add up. |
| Forgotten regions | You spun something up in a different region for a test and never see it on your default dashboard view. It bills on. |
The discipline: know exactly what your free tier covers and for how long, prefer services that scale to zero when idle (which is why PaaS free tiers and serverless are friendlier to learners), and after any experiment, delete the whole thing — server, disk, snapshots, the lot. The budget alarm is the backstop for when you forget.
Scaling — up versus out
When your app needs to handle more load, there are exactly two moves, and the cloud is dramatically better at one of them.
flowchart TB
subgraph Vertical[Vertical scaling - scale UP]
V1[1 small server] --> V2[1 bigger server - more CPU and RAM]
end
subgraph Horizontal[Horizontal scaling - scale OUT]
H1[1 server] --> H2[3 identical servers behind a load balancer]
end
| Vertical (scale up) | Horizontal (scale out) | |
|---|---|---|
| The move | Bigger machine — more CPU/RAM | More machines, same size |
| Limit | The biggest machine that exists | Effectively none |
| Downtime | Often a restart to resize | None — add/remove live |
| Resilience | One machine = one point of failure | One dies, others carry on |
| The catch | Simple, but a ceiling | Needs stateless app + a load balancer |
| Cloud fit | Fine for databases | What the cloud is built for |
The cloud’s superpower is horizontal scaling: spin up identical copies of your app behind a load balancer, automatically, as traffic rises, and kill them when it falls. That is auto-scaling and elasticity — paying only for what the moment needs. This only works if your app is stateless — no session or user data stored in the instance’s memory or disk, because the next request might hit a different copy. That is exactly why your JWT design matters: the token carries the user’s identity, so any instance can serve any request without shared session state.
The non-obvious rule: databases usually scale vertically first, apps scale horizontally. You cannot just add database copies and write to all of them — writes must stay consistent. So the database gets a bigger machine (vertical), and you add read replicas to spread read load (a careful form of horizontal that only works for reads). The full treatment — load balancers, replicas, the read-your-own-writes trap, when sharding becomes necessary — is in System Design’s scaling module. Here, just lock in: app out, database up-then-replicas, and statelessness is the precondition for either.
Not getting hacked — the three things juniors get wrong
Cloud security is enormous, but for a backend engineer three basics prevent the overwhelming majority of real incidents.
1. IAM and least privilege. IAM (Identity and Access Management) is who-can-do-what in your account. The junior mistake is giving every user and service AdministratorAccess “to make it work.” The rule is least privilege: grant exactly the permissions a thing needs, nothing more. If your app only reads from one S3 bucket, its role gets read on that one bucket — not write, not delete, not other buckets. Then when a key leaks or a service is compromised, the blast radius is tiny. Least privilege is the same instinct as not running your container as root, which you already do in your Dockerfile.
2. Never commit keys. Access keys (an AWS access key ID + secret) are credentials that let code call the API as you. The catastrophe is pushing them to a public repo — automated bots scan GitHub continuously and find a leaked key in seconds, then spin up expensive resources (usually crypto mining) on your account. Prevention:
- Never put keys in source. Use environment variables or a secrets manager — the same principle from the deploy module and from Spring Boot’s security module: secrets come from the environment, not the code.
- Add a
.gitignorethat excludes credential files; use a pre-commit secret scanner. - Prefer roles over long-lived keys — a role gives a service temporary, auto-rotating credentials, so there is no static secret to leak.
- If a key does leak: revoke it immediately, do not just delete the repo — the key is already copied by bots; deletion does nothing.
3. Security groups — the cloud firewall. A security group is a virtual firewall around a resource: a list of which ports accept traffic from which sources. The junior mistake is opening everything (0.0.0.0/0 on all ports) to make connectivity work, which exposes the database directly to the internet. The rule:
- The app’s HTTPS port (443) open to the world is fine — that is its job.
- The database port (5432) open only to the app’s security group, never to the internet. Your Postgres should be unreachable from anywhere except your own app.
- SSH (22) open only to your own IP, if at all.
flowchart LR
Net[Public internet] -->|443 allowed| App[App security group]
Net -.22 and 5432 blocked.-> App
App -->|5432 allowed from app only| DB[Database security group]
Net -.5432 blocked entirely.-> DB
The diagram is the whole policy: the internet reaches the app on 443 and nothing else; only the app reaches the database, and the internet cannot touch the database at all. A managed PaaS does this for you (your database is on a private network by default), which is one more reason a beginner starts there.
When to actually move off a PaaS
You stayed on Railway/Render through the last module on purpose. So when is it genuinely time to go to raw cloud? Not “when you feel like a real engineer” — there is a real list:
| Move when you need… | Because the PaaS can’t (cheaply) give it |
|---|---|
| A private network (VPC) | Compliance/isolation a PaaS abstracts away |
| Fine-grained IAM | PaaS access control is coarse |
| Specific regions / data residency | RBI rules require certain financial data to stay in India |
| Cost control at scale | Above a point, the PaaS markup exceeds the ops effort of DIY |
| A service the PaaS lacks | Queues, specific managed offerings, GPUs |
| Deep control of networking/tuning | PaaS deliberately hides these |
And the reasons not to move, which matter just as much: until you hit one of the above, raw cloud just adds VPCs, IAM, security groups, and patching to your plate for no product benefit. The senior move is staying on the boring managed thing as long as it serves you, because the goal is shipping, not operating infrastructure. “We moved to AWS” is not an achievement; “we moved to AWS because we needed data residency for an RBI audit and saved 40% at our scale” is. The trigger is a concrete need, never resume-driven architecture.
Check The Concept
How This Shows Up At Work
- The surprise-bill postmortem. A teammate’s experiment left an instance and its disk running in a region nobody watches; the bill spikes. The takeaway every team writes down afterward is the one you already have: budget alarms on day one, and tear down experiments completely. Being the person who already set the alarm is quiet seniority.
- The leaked key incident. A key lands in a public repo; within minutes there are mining instances and a five-figure bill. The correct first move — revoke the key, do not just delete the repo — separates people who understand the threat from people who make it worse. This is exactly why the repo for this site carries no real secrets.
- The Black-Friday scaling question. “Traffic will 10x for one day — what do you do?” The strong answer is horizontal auto-scaling on the stateless app tier plus read replicas for the database, not “buy a bigger server.” Knowing the database scales differently from the app is the part that signals depth.
- The premature-migration debate. Someone wants to move the whole stack to Kubernetes on AWS “to be production-grade.” You ask what concrete need the PaaS fails to meet, and the answer is none yet. Arguing against unnecessary complexity is a stronger signal than enthusiastically adopting it.
Build This
This module is mostly judgment, but two of these are real clicks you should do once on a real free account, and the rest are exercises you run on paper or in the console.
-
Set a real budget alarm (do this for real). Create a free-tier account on AWS (or GCP/Azure), and before deploying anything, go to Billing → Budgets, create a ₹500/month budget with alerts at 50/80/100%, and confirm the email arrives in your inbox. You now have the seatbelt. If you already deployed on a PaaS, check whether it has a spend cap or usage alert and turn it on.
-
Audit your own deploy for the free-tier traps. Open the platform from the last module and answer in writing: Is anything running 24/7 that does not need to be? Is there a database snapshot or volume you could delete? When does your free allowance expire? You are training the habit of looking before the bill does.
-
Map a scaling plan for SplitEase. On paper: “Traffic 10x for month-end settlement.” Write which tier scales horizontally (the API), which scales vertically and why (the database), what must be true of the app for horizontal to work (stateless — JWT, no in-memory sessions), and where read replicas help. Check yourself against the scaling module.
-
Write a least-privilege policy in English. For
splitease-api, list exactly what it needs to touch (its one database; maybe one storage bucket for receipts) and write the permission as a sentence: “read and write its own database; read and write the receipts bucket; nothing else.” That sentence is what a real IAM policy encodes — you have just done the hard thinking part. -
Break it on purpose — the security-group thought experiment. Imagine setting your database security group to allow 5432 from
0.0.0.0/0. Write down, in two lines, what an attacker who finds the host could now do, and contrast it with the database only allowing the app. Then write the correct rule. (Do not actually open a real DB to the internet — the point is to feel why the default-closed posture exists.)
Interview Practice
The cloud questions that actually get asked of backend/fintech candidates at Indian product companies — cost, scaling, and security come up far more than service trivia.
Q1 — How do you avoid a surprise cloud bill?
Set a budget alarm the moment you create the account, before deploying — it emails you when spend crosses a threshold, turning a worst case of a giant invoice into a small charge plus a heads-up. Know exactly what your free tier covers and when it expires (the AWS headline tier is 12 months from signup). Prefer services that scale to zero when idle. Tear down experiments completely — server, disk, snapshots, in every region — because idle resources and orphaned storage bill on. For hard limits, automate teardown on an alert; for most teams the alarm plus discipline is enough.
Q2 — Horizontal vs vertical scaling — explain and say which the cloud favours.
Vertical scaling is a bigger machine (more CPU/RAM) — simple, but it hits the ceiling of the largest machine available and usually needs a restart to resize. Horizontal scaling is more identical machines behind a load balancer — effectively unlimited, no downtime to add or remove, and resilient since one failure does not take the system down. The cloud is built for horizontal: auto-scaling and elasticity spin copies up and down with demand so you pay for the moment’s need. The catch is the app must be stateless. In practice the app tier scales horizontally and the database scales vertically first, then adds read replicas, because writes must stay consistent.
Q3 — What is IAM and what is least privilege?
IAM (Identity and Access Management) is the system that controls who — users, services, roles — can do what in a cloud account. Least privilege means granting exactly the permissions an identity needs and nothing more. If a service only reads one bucket, its role gets read on that one bucket, not admin. The payoff is blast-radius control: when a credential leaks or a service is compromised, the attacker can only do the little that identity was allowed. The opposite — handing out AdministratorAccess for convenience — turns any small breach into a full account takeover.
Q4 — What happens if an access key is committed to a public repo, and what do you do?
Bots continuously scan public repositories for credentials and typically exploit a leaked key within seconds — spinning up expensive compute, usually crypto mining, on your account. Deleting the repo does not help, because the key is already harvested. The correct response is to revoke/rotate the key immediately, then audit the account for resources created by the attacker. Prevention: never put keys in source, use environment variables or a secrets manager, run a pre-commit secret scanner, and prefer roles that issue temporary auto-rotating credentials so there is no static key to leak.
Q5 — How would you design for high availability?
Remove single points of failure. Run multiple stateless app instances across more than one availability zone behind a load balancer, so one instance or one AZ failing does not take you down. Use a managed database with multi-AZ failover and automated backups. Add health checks so unhealthy instances are pulled from rotation and replaced automatically. Keep the app stateless (identity in a JWT, no in-memory sessions) so any instance can serve any request. The principle is redundancy plus automatic failover at every tier, and the cloud gives you the building blocks — multiple AZs, load balancers, auto-scaling — to assemble it.
Q6 — What are regions and availability zones?
A region is a geographic location (Mumbai, Singapore) where the provider has data centres; an availability zone (AZ) is one or more isolated data centres within a region, with independent power and networking. You choose a region for latency (close to users) and for data residency/compliance (RBI rules can require Indian financial data to stay in India). You spread across multiple AZs within a region for high availability — an AZ can fail without taking your whole region down. Rule of thumb: multiple AZs for resilience inside a region; multiple regions only when you need geographic redundancy or to serve distant users.
Q7 — When should a team move off a managed platform onto raw cloud?
When a concrete need appears that the PaaS cannot meet cheaply: a private network (VPC) for isolation, fine-grained IAM, specific regions for data residency, cost relief at a scale where the PaaS markup exceeds DIY ops effort, or a service the PaaS lacks. Until then, staying on the managed platform is the senior choice — raw cloud just adds networking, IAM, and patching for no product gain. The trigger is always a specific requirement, never the wish to look production-grade; resume-driven architecture is a smell.
Q8 — Where should the database live network-wise, and what is a security group?
A security group is a virtual firewall around a resource — a list of which ports accept traffic from which sources. The database should sit on a private network, with its port (5432 for Postgres) open only to the application’s security group and not to the public internet at all. The app’s HTTPS port is open to the world because that is its job; the database is reachable only by the app. Opening the DB port to 0.0.0.0/0 is the classic mistake that exposes data directly to attackers. Managed PaaS databases default to this private posture, which is part of why they are safer for beginners.
Where to Practice
| Resource | What to do | How long |
|---|---|---|
| aws.amazon.com/free | Read the free-tier terms carefully — note the 12-month clock and which services are always-free vs trial; then set a real budget in Billing → Budgets | 30 min |
| docs.aws.amazon.com (IAM) | Read “IAM best practices” — least privilege, roles over long-lived keys, MFA on root | 30 min |
| Cloudflare Learning Center | Read “What is a firewall?” and the load-balancing explainer to cement security groups and horizontal scaling concepts | 25 min |
| learn.microsoft.com | Skim “Azure security groups” and “Scale an app” to see the same ideas under another provider — proves the concepts transfer | 20 min |
Check Yourself
- What is the first action on any new cloud account, and what does it actually do (and not do)?
- Name three free-tier traps that silently bill you.
- Horizontal vs vertical scaling: define each, and say which the cloud is built for and why.
- Why must an app be stateless to scale horizontally, and how does a JWT help?
- Why do databases usually scale vertically while apps scale horizontally?
- State the least-privilege principle and one concrete example for
splitease-api. - An access key just got pushed to a public repo. What is your first move and why isn’t deleting the repo enough?
- What port should your database security group allow, and from where?
Answers
- Set a budget alarm. It emails you when spend crosses a threshold so a forgotten resource costs a small amount plus a warning instead of a huge invoice. It does not stop spending — it only tells you, so you react in minutes.
- Any three: always-on idle resources still bill per hour; the free tier expires 12 months after signup; data egress charges per GB; orphaned storage (disks, snapshots, buckets) bills monthly forever; load balancers / NAT gateways bill hourly; resources left in forgotten regions.
- Vertical = a bigger single machine (ceiling: the largest machine, usually needs a restart). Horizontal = more identical machines behind a load balancer (effectively unlimited, no downtime, resilient). The cloud is built for horizontal — auto-scaling and elasticity match capacity to demand.
- Because the next request may hit a different copy, so no user/session state can live in one instance’s memory or disk. A JWT carries the user’s identity in the token itself, so any instance can serve any request without shared session state.
- Writes must stay consistent, so you cannot just add database copies and write to all of them. The database gets a bigger machine (vertical) and read replicas to spread read load; the stateless app tier simply adds identical copies (horizontal).
- Grant exactly the permissions an identity needs and nothing more. Example:
splitease-api’s role can read/write only its own database (and maybe its receipts bucket), not admin, not other resources — so a leak has a tiny blast radius. - Revoke/rotate the key immediately. Deleting the repo is useless because bots have already harvested the key within seconds; only revocation stops it. Then audit for resources the attacker created.
- The database port (5432 for Postgres) open only to the application’s security group — never to the public internet. The internet should not be able to reach the database at all.
Explain it out loud: Explain to an empty chair the three ways a cloud account ruins your month — surprise bill, a leaked key, an exposed database — and the one preventative move for each (budget alarm, keys out of code plus roles, security group locked to the app). If you can’t name the move for each disaster without pausing, re-read that section.
Why AI Can’t Do This For You
AI will cheerfully write you an IAM policy or a Terraform file — and it does not know that the bucket you just granted write to holds customer KYC data, or that the security group it opened to 0.0.0.0/0 fronts your production database. Cloud cost and security failures are failures of judgment about your specific account: which resource is actually needed, which permission is actually too broad, which port is actually exposed. A model cannot see your bill, your blast radius, or the key sitting in your last commit.
The instincts here — set the alarm before you deploy, treat a leaked key as an active fire, default every door closed and open only what is needed, stay on the boring managed thing until a real need forces the move — are built by owning an account, getting a scary email once, and reading a bill line by line. That earned caution is exactly what protects a real company’s money and data, and no prompt installs it for you.
Module done? Add it to today’s tracker