Skip to main content

🧩 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:

ServiceJobWhy not do it yourself?
ComputeRuns your application codeServers need patching, scaling, and monitoring
Managed databaseStores structured data reliablyBackups, failover, and upgrades are hard to get right
Object storageHolds files, images, uploadsCheaper, more durable, and infinitely scalable
CDNServes content fast, worldwideYou can't put a server in every city
DNSTurns your domain into an addressNeeds global reliability and low latency
Load balancerSpreads traffic across instancesEnables scaling and survives instance failures
Secrets managerStores API keys and passwords safelyCredentials 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.

Four compute options from most control to least effort More control ◀ ▶ Less effort Virtual Machine You own the OS EC2 / Compute Engine Container Packaged app Cloud Run / ECS / AKS PaaS Push code, done App Service / Render Serverless Just a function Lambda / Cloud Functions
Move right and the provider handles more for you; move left and you gain control at the cost of operational work.

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.

NeedTypeAWSAzureGCP
Structured, relational dataSQLRDS / AuroraSQL DatabaseCloud SQL
Flexible, schemaless docsNoSQLDynamoDBCosmos DBFirestore
Fast temporary data / cacheIn-memoryElastiCacheCache for RedisMemorystore

💡 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.

graph LR U["User types yourapp.com"] --> DNS["DNS resolves the domain"] DNS --> CDN["CDN edge serves cached static files"] CDN -->|cache miss or dynamic| LB["Load Balancer"] LB --> A1["App instance 1"] LB --> A2["App instance 2"] LB --> A3["App instance 3"]

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.

graph TD User["Browser"] --> DNS["DNS"] DNS --> CDN["CDN + edge cache"] CDN --> Static["Object storage: SPA build, images"] CDN --> LB["Load balancer"] LB --> API["Compute: API instances or containers"] API --> DB["Managed SQL database"] API --> Cache["Managed Redis cache"] API --> Files["Object storage: uploads"] API --> Sec["Secrets manager"] Monitor["Monitoring and logging"] --> API

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.

  1. Storing millions of user-uploaded photos.
  2. Serving those photos quickly to users in different continents.
  3. Keeping the API's third-party mail-service API key safe.
  4. 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 .env locally
  • 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

🚀 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.