🧩 Cloud Services for Web Apps
A modern web app is never a single thing running on a single server. It's a small orchestra of specialized services — one to serve static files fast, one to run your code, one to store data safely, one to keep secrets out of your source. This lesson names every instrument and shows how they play together.
Week 13 · Monday: Cloud Platforms Overview · Lecture 2
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Identify the seven core services a typical web app uses: compute, managed DB, object storage, CDN, DNS, load balancer, and secrets
- Choose the right compute model — VM, container, PaaS, or serverless — for a given workload
- Explain how a CDN, load balancer, and DNS route a user's request to your app
- Describe why object storage and managed databases beat storing files and running your own DB on a server
- Read and reason about a cloud reference architecture diagram end to end
- Keep credentials safe using a managed secrets service instead of hard-coding them
Estimated Time: 60 minutes
Practice: Sketch a reference architecture for a small SaaS app and label each service.
In This Lesson
The Anatomy of a Web App
When a user visits your site, a surprising number of services cooperate in the few hundred milliseconds before the page appears. Think of building a web app in the cloud like running a restaurant. You could own the building, buy every appliance, and hire every specialist — but you'd spend all your energy on plumbing instead of food. Instead, you rent a fully-equipped space and buy specialized services, so you can focus on your actual product.
Almost every web application, regardless of provider, is assembled from the same seven categories of service:
| Service | Job | Why not do it yourself? |
|---|---|---|
| Compute | Runs your application code | Servers need patching, scaling, and monitoring |
| Managed database | Stores structured data reliably | Backups, failover, and upgrades are hard to get right |
| Object storage | Holds files, images, uploads | Cheaper, more durable, and infinitely scalable |
| CDN | Serves content fast, worldwide | You can't put a server in every city |
| DNS | Turns your domain into an address | Needs global reliability and low latency |
| Load balancer | Spreads traffic across instances | Enables scaling and survives instance failures |
| Secrets manager | Stores API keys and passwords safely | Credentials must never live in your code |
Compute: Running Your Code
Compute is where your Node.js server, your API, your rendering logic actually runs. You have four broad choices, from most control to least effort. The right one depends on how much of the machine you want to manage.
Containers & serverless in practice
For most full-stack apps today, a managed container platform hits the sweet spot: you package your app once with Docker and the platform runs, scales, and heals it. Here's a Node app deployed to Google Cloud Run — the same idea works on AWS App Runner or Azure Container Apps.
# 1. Build and push your container image
gcloud builds submit --tag gcr.io/PROJECT_ID/web-app
# 2. Deploy it — scales to zero when idle, up on demand
gcloud run deploy web-app \
--image gcr.io/PROJECT_ID/web-app \
--platform managed \
--region us-central1 \
--memory 512Mi \
--min-instances 0 \
--max-instances 10 \
--allow-unauthenticated
The matching Dockerfile is small and standard:
# Dockerfile for a Node web app
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev # reproducible, production-only install
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]
For truly spiky or event-driven work — a webhook handler, an image resizer, a scheduled cleanup — serverless functions shine because they cost nothing when idle:
// AWS Lambda handler — invoked per request, scales automatically
exports.handler = async (event) => {
const body = JSON.parse(event.body || "{}");
const name = body.name || "World";
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: `Hello, ${name}!`,
timestamp: new Date().toISOString()
})
};
};
⚠️ The cold-start trade-off
Serverless functions and scale-to-zero containers save money by spinning down when idle — but the next request must wait for a fresh instance to boot (a "cold start," often 100ms–1s). For latency-sensitive endpoints, keep a minimum number of instances warm, or use a steadily-running container instead.
Managed Databases
You could install PostgreSQL on a VM and manage it yourself — but then you own backups, security patches, replication, failover, and the 2 a.m. page when the disk fills up. A managed database hands all of that to the provider. You get a connection string; they keep it alive.
| Need | Type | AWS | Azure | GCP |
|---|---|---|---|---|
| Structured, relational data | SQL | RDS / Aurora | SQL Database | Cloud SQL |
| Flexible, schemaless docs | NoSQL | DynamoDB | Cosmos DB | Firestore |
| Fast temporary data / cache | In-memory | ElastiCache | Cache for Redis | Memorystore |
💡 SQL or NoSQL?
Reach for SQL (Postgres, MySQL) by default — most apps have relationships (users, orders, products) that relational databases model beautifully, and you get transactions for free. Reach for NoSQL when you need massive horizontal scale with simple access patterns, or a genuinely flexible schema. Many apps use both: SQL for core data, Redis for caching and sessions.
Managed databases give you production-grade reliability out of the box: multi-AZ replication (a standby in another data center takes over if the primary fails), automated backups with point-in-time recovery, and read replicas to spread heavy read traffic.
Object Storage
Where do user-uploaded profile photos, PDFs, video, and your app's build artifacts go? Not on your app server's disk — that disk vanishes when the instance restarts, and it doesn't scale. The answer is object storage: effectively infinite, cheap, durable buckets of files, each addressable by a URL.
- Durability — providers replicate objects across facilities, quoting eleven nines (99.999999999%) of durability. Files effectively don't get lost.
- Scalability — store one file or one billion; the service doesn't care.
- Cheap — pennies per gigabyte per month, with even cheaper "cold" tiers for archives.
- Web-native — serve files directly, or better, put a CDN in front.
✅ The upload pattern to know
Don't route file uploads through your server (it wastes compute and memory). Instead, have your server hand the browser a short-lived presigned URL, and the browser uploads directly to the bucket. Your server only records the resulting file key. This is how photo-heavy apps process thousands of uploads with almost no server load.
CDN, DNS & Load Balancer: Getting the Request There
These three services form the front door of your application. Together they turn a typed URL into a fast, reliable response.
DNS — the address book
When someone types yourapp.com, DNS (Route 53, Azure DNS, Cloud DNS) translates that name into an IP address — the internet's phone book. It also lets you route traffic geographically or fail over to a backup region.
CDN — the local warehouse
A CDN (CloudFront, Front Door, Cloud CDN) caches copies of your static content — HTML, CSS, JS bundles, images — at edge locations around the world. A user in Tokyo is served from a Tokyo edge instead of waiting for a round-trip to Virginia. This is the single biggest, cheapest performance win most sites can make.
Load balancer — the host at the door
A load balancer sits in front of multiple app instances and distributes incoming requests among them. If one instance dies, the balancer stops sending it traffic; when you add instances, it starts using them. It's what makes horizontal scaling and zero-downtime deploys possible.
Secrets Management
Your app needs database passwords, third-party API keys, signing tokens. Where do they go? Never in your source code, and never committed to Git. The professional answer is a managed secrets service (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager) that stores credentials encrypted and hands them to your app at runtime.
// Fetch a secret at runtime instead of hard-coding it.
// The app authenticates via its cloud role — no key needed in code.
import { SecretsManagerClient, GetSecretValueCommand }
from "@aws-sdk/client-secrets-manager";
const client = new SecretsManagerClient({ region: "us-east-1" });
async function getDbPassword() {
const res = await client.send(
new GetSecretValueCommand({ SecretId: "prod/db/password" })
);
return JSON.parse(res.SecretString).password;
}
⚠️ For local development, use environment variables
In development, a .env file (loaded with a library like dotenv, and listed in .gitignore) is fine. In production, prefer a managed secrets service so keys are encrypted, access is audited, and rotation is possible. The one unbreakable rule at every stage: secrets never enter version control.
A Reference Architecture
Let's assemble everything into one coherent picture — a typical production web application. Trace a request from the top and you'll see all seven services doing their jobs.
Notice the shape: static content (the React build, images) is served straight from object storage through the CDN, never touching your compute. Only dynamic requests (API calls) pass the load balancer to your compute layer, which reads and writes the database, caches hot data in Redis, stores uploads in object storage, and pulls credentials from the secrets manager. This separation is what lets a small app scale to millions of users without re-architecting.
📖 Three common shapes
Serverless (CDN → API Gateway → Lambda → DynamoDB): lowest ops, scales to zero, ideal for MVPs and spiky traffic. Containerized (Load balancer → Cloud Run/ECS → managed DB): the balanced default for most full-stack apps. Three-tier (Load balancer → VMs → managed DB): the classic, most control, most operational work. Pick by workload, not fashion.
Practice & Quiz
🏋️ Exercise 1: Place the service
Goal: For each requirement of a photo-sharing app, name the cloud service category that fits best.
- Storing millions of user-uploaded photos.
- Serving those photos quickly to users in different continents.
- Keeping the API's third-party mail-service API key safe.
- Storing users, follows, and comments with relationships.
💡 Hint
Files → not a database. "Quickly, everywhere" → an edge service. "Safe key" → not an env var in prod. "Relationships" → relational.
✅ Solution
- 1 → Object storage (S3 / Cloud Storage / Blob).
- 2 → CDN (CloudFront / Cloud CDN / Front Door).
- 3 → Secrets manager (Secrets Manager / Key Vault / Secret Manager).
- 4 → Managed SQL database (RDS / Cloud SQL / SQL Database).
🏋️ Exercise 2: Fix the anti-pattern
Goal: A junior dev's Express app saves uploaded avatars to ./uploads on the server's local disk and reads const KEY = "sk_live_abc123" from a constant in the code. Name the two problems and the correct fix for each.
✅ Solution
- Local-disk uploads: the disk is ephemeral (lost on restart/redeploy) and doesn't scale across instances. Fix: upload to object storage, ideally via a presigned URL.
- Hard-coded API key: secrets in code leak into Git and can't be rotated. Fix: load it from a secrets manager in production (env var locally), and never commit it.
🎯 Quick Quiz
Question 1: Which service caches your static content at edge locations to serve users faster worldwide?
Question 2: Where should user-uploaded files be stored in a scalable web app?
Question 3: Which compute model costs nothing while it is idle and scales up automatically per request?
Best Practices & Pitfalls
✅ Do
- Serve static assets from object storage through a CDN, not from your app server
- Use a managed database so backups, patching, and failover are handled for you
- Store uploads in object storage, ideally via presigned URLs
- Pull secrets from a secrets manager in production; use
.envlocally - Put a load balancer in front of compute so you can scale and deploy without downtime
❌ Don't
- Write uploaded files to a server's local disk — it's ephemeral and unshared
- Commit API keys or passwords to Git, ever
- Run your own database on a VM unless you truly need that control
- Forget cold starts when choosing serverless for latency-critical paths
⚠️ The "it works on one server" trap
Storing sessions in memory, files on local disk, or state in a single process all break the moment you run a second instance behind a load balancer. Design as if there are always many instances: sessions in Redis, files in object storage, state in the database. This is the essence of the twelve-factor mindset.
Summary
🎉 Key Takeaways
- A web app is assembled from seven core services: compute, managed DB, object storage, CDN, DNS, load balancer, and secrets
- Compute ranges from VMs → containers → PaaS → serverless; containers are the common sweet spot
- Managed databases and object storage hand reliability and scale to the provider — use them by default
- DNS → CDN → load balancer form the front door that routes each request quickly and reliably
- Secrets belong in a secrets manager, never in code or Git
📚 Additional Resources
- AWS — Web application reference architectures
- Google Cloud — Web serving architecture overview
- Azure — Basic web application architecture
- The Twelve-Factor App methodology
🚀 What's Next?
You now know what services a web app uses. The next lesson tackles the question that follows every architecture decision: how do you keep the bill under control? Next up is Cost Optimization Strategies.
🎉 You can read an architecture now
Every one of those intimidating cloud diagrams is just these seven building blocks arranged for a specific job. Next, we make them cheap.