Module 13 · Cloud & System Design · Deep Dive

Cloud & AWS Essentials

You already shipped DocChat to Vercel and Neon with a few clicks. Now learn the path most UAE enterprises actually run on — AWS — and why the extra control is worth it when data residency, compliance, and scale are on the line.

BasicIntermediateBuild

Why this matters Vercel and Neon are wonderful — they hide the cloud so you can focus on product. But walk into a bank in DIFC, a government entity in Abu Dhabi, or a healthcare provider in Dubai and you'll hear the same three words: "It runs on AWS." They need data to physically stay in the UAE, fine-grained access control, and a bill they can forecast. This lesson rebuilds the DocChat you know on AWS primitives — S3, RDS, App Runner, Secrets Manager — so you can speak this language in an interview and on the job.
In this lesson
  1. The cloud mental model vs the Vercel path
  2. IAM — the #1 thing to get right
  3. Regions & AZs (me-central-1 UAE)
  4. Compute: EC2 vs containers vs serverless
  5. S3 & presigned URLs
  6. RDS, pgvector & running your container
  7. Secrets, logs, VPC & cost traps
  8. Infrastructure as Code
  9. Build: DocChat on AWS
  10. Check yourself

1 · The cloud mental model

On Vercel you pushed to git and a URL appeared. The platform decided where your code ran, how it scaled, and how it talked to Neon. That's a Platform-as-a-Service (PaaS) — opinionated, fast, and it hides the machinery.

AWS is the machinery. It's a menu of ~200 building blocks — compute, storage, networking, databases — that you assemble. More work, but total control: you pick the region, the network boundaries, who can touch what, and exactly how much you pay.

ConcernVercel/Neon (you did this)AWS (this module)
Run the APIAuto from git pushApp Runner / ECS Fargate / EC2
DatabaseNeon (managed Postgres)RDS / Aurora (managed Postgres)
File storageBlob / externalS3
SecretsProject env varsSecrets Manager / Parameter Store
Who decides scale & regionThe platformYou
Vercel bridge: everything you got "for free" on Vercel still exists on AWS — it just has a name, a price, and an access policy you control. That control is the whole point for regulated UAE clients.

2 · IAM — get this right first

Identity and Access Management is the front door to your entire account, and it's the single most important thing to understand. Get it wrong and you either lock yourself out or — far worse — leave the door wide open. Three concepts:

A policy is allow/deny rules. Here's one that lets DocChat's container read and write objects in only its own bucket — nothing else:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::docchat-uploads/*"
    }
  ]
}
Least privilege — the principle interviewers test Grant the minimum permissions needed, then add more only when something breaks. Never attach AdministratorAccess to an app role, and never use the root account for daily work. Notice the policy above does not say s3:* and does not say Resource: "*" — it names the exact actions and the exact bucket. That specificity is the difference between a controlled blast radius and a breach.
Interview hook: "How would you let a service read one S3 bucket securely?" The senior answer is "an IAM role with a least-privilege policy scoped to that bucket's ARN — not access keys baked into the image."

3 · Regions & availability zones

AWS is physically organised into Regions — geographic areas like us-east-1 (Virginia) or eu-west-1 (Ireland). Each Region contains multiple Availability Zones (AZs): separate data centres with independent power and networking, a few kilometres apart. Spreading across AZs is how you survive one data centre failing.

UAE data residency — say this in the interview AWS has a Region in the UAE: me-central-1 (UAE, based in the Emirates). There's also me-south-1 (Bahrain) nearby in the Gulf. For UAE banks, government, and healthcare, regulations often require customer data to physically stay inside the country. Deploying DocChat to me-central-1 means your uploaded PDFs and database never leave the UAE — exactly what a compliance officer needs to hear. This is a genuine differentiator: "I'd pick me-central-1 for data residency" shows you understand the local market.

Pick your Region before you build — every resource lives in one. The tradeoffs: latency to your users, data-residency law, and which services are available (newer Regions sometimes lag on niche services). For a UAE product serving UAE users, me-central-1 wins on both latency and law.

4 · Compute: three ways to run code

On Vercel you never chose this — the platform did. On AWS you pick from a spectrum, trading control for convenience:

EC2 virtual machines

# A raw Linux server in the cloud. You manage the OS, patches, scaling.
aws ec2 run-instances --image-id ami-0abc --instance-type t3.micro

Maximum control, maximum responsibility. Choose EC2 when you need a specific OS, GPUs, or long-running stateful processes. For a stateless web API it's usually too much work.

Containers ECS Fargate & App Runner

You already containerised DocChat with Docker in Module 7. Hand AWS that same image and it runs it — no servers to patch. Fargate runs containers with no VMs to manage; App Runner is even simpler: point it at your image, it builds, deploys, scales, and gives you HTTPS. App Runner is the closest thing to the Vercel feeling, and it's the right default for DocChat's FastAPI container.

Serverless Lambda + API Gateway

def handler(event, context):
    return {"statusCode": 200, "body": "hi"}

Lambda runs a single function on demand — you pay per invocation, scale to zero when idle. Put API Gateway in front to turn HTTP requests into Lambda calls. Brilliant for spiky, event-driven work. The catch for DocChat: a 15-minute max runtime and "cold starts" make it awkward for a heavy RAG request that loads a model. Use Lambda for the thumbnail-generation or webhook around DocChat, not the core API.

Rule of thumb: spiky/event-driven → Lambda · a normal web service → App Runner/Fargate · special OS or hardware → EC2. Knowing when to use each is the interview skill, not memorising flags.

5 · S3 — object storage for your PDFs

S3 (Simple Storage Service) stores objects (files) in buckets. It's effectively infinite, cheap, and durable — the natural home for DocChat's uploaded PDFs. Buckets are private by default, and you should keep them that way; access is granted through IAM policies and signed URLs, never by making the bucket public.

The slick pattern is the presigned URL. Instead of routing a 40 MB PDF through your FastAPI server (slow, doubles your bandwidth), your backend hands the browser a temporary, signed URL and the browser uploads directly to S3:

# Backend asks S3 for a one-time upload URL
import boto3

s3 = boto3.client("s3", region_name="me-central-1")
url = s3.generate_presigned_url(
    "put_object",
    Params={"Bucket": "docchat-uploads", "Key": "u123/report.pdf"},
    ExpiresIn=300,   # URL valid for 5 minutes
)
# Send `url` to the browser; it does a PUT straight to S3.
Why presigned URLs are the right answer Your server never touches the file bytes, so it stays small and fast. The URL is time-limited and scoped to one object key, so it leaks no broader access. And because the bucket stays private, there's no public listing of everyone's documents. This exact pattern shows up constantly in UAE fintech interviews.

6 · RDS, pgvector & your container

Neon gave you managed Postgres on Vercel's side. On AWS the equivalent is RDS (Relational Database Service) — managed Postgres with automated backups, patching, and failover. Aurora is AWS's higher-performance Postgres-compatible engine in the same family.

Crucially for DocChat: pgvector is available on RDS and Aurora PostgreSQL. You enable it just like anywhere else, so your entire RAG schema from Module 8 ports over unchanged:

CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE chunks (
    id        bigserial PRIMARY KEY,
    doc_id    text,
    content   text,
    embedding vector(1536)
);

Run RDS in private subnets so it's never reachable from the public internet — only your App Runner / Fargate service, inside the same VPC, can connect. Then deploy DocChat's FastAPI container to App Runner:

# Push your existing Docker image to ECR (AWS's registry)...
aws ecr create-repository --repository-name docchat-api
# ...then point App Runner at it. It builds, deploys, and gives you HTTPS.
Same code, new home: the FastAPI app, the pgvector queries, the embeddings — all identical to your Vercel/Neon build. Only the surrounding plumbing changed.

7 · Secrets, logs, VPC & the bill

Secrets Manager & Parameter Store. Never hardcode your database password or API keys. Store them in Secrets Manager (encrypted, rotatable) or Parameter Store (free for plain strings), and let your container's IAM role fetch them at runtime. No secrets in the image, no secrets in git.

CloudWatch. Every print/log from your container flows to CloudWatch Logs, and metrics (CPU, request count, errors) land in CloudWatch Metrics where you set alarms. This is your console.log and your dashboard. When DocChat 500s in production, CloudWatch is where you look.

VPC — just enough. A Virtual Private Cloud is your private network. The key idea: public subnets (reachable from the internet — your App Runner endpoint) and private subnets (database, internal services). Security groups are per-resource firewalls — e.g. "RDS only accepts port 5432 from the app's security group." That one rule is what keeps your database off the open internet.

The bill traps — every cost story is one of these AWS bills are forecastable if you know the traps. The classics: NAT Gateways (~$30+/month each, plus per-GB) that beginners over-provision; data egress (moving data out of AWS or across Regions costs money — keep traffic in one Region); and idle resources (an EC2 box or RDS instance left running over a weekend). Set a Budgets alarm on day one. "How do you control AWS cost?" — name these three.

8 · Infrastructure as Code

Clicking around the AWS console to create resources is fine for learning and fatal for production — nobody can reproduce it, and nobody remembers what you did. Infrastructure as Code (IaC) describes your whole stack in version-controlled files you can apply, review in a PR, and tear down cleanly.

Two leading choices in June 2026: Terraform (HashiCorp's declarative HCL, cloud-agnostic) and AWS CDK (define infra in real Python/TypeScript). A taste of Terraform for DocChat's bucket:

resource "aws_s3_bucket" "uploads" {
  bucket = "docchat-uploads"
}

resource "aws_s3_bucket_public_access_block" "uploads" {
  bucket                  = aws_s3_bucket.uploads.id
  block_public_acls       = true
  block_public_policy     = true
}

Now the bucket — private by default, enforced — lives in git. A teammate runs terraform apply and gets an identical environment in me-central-1. That reproducibility is what "production-ready" actually means.

Interview hook: "How do you make your infrastructure reproducible?" — "IaC with Terraform or CDK, reviewed in PRs, applied to UAE's me-central-1 for data residency." That single sentence signals seniority.

9 · Build: DocChat on AWS

Your tangible win Sketch — and ideally provision — DocChat's full AWS shape: PDFs in S3 via presigned URLs, Postgres + pgvector on RDS, the FastAPI container on App Runner, secrets in Secrets Manager, logs in CloudWatch — all in me-central-1 for UAE data residency.

The request flow, end to end:

DocChat on AWS — the shape
# 1. Browser wants to upload a PDF
Browser  ─▶  App Runner (FastAPI)  ─▶  generate_presigned_url()
# 2. Browser uploads the PDF DIRECTLY to S3 with that URL
Browser  ─────────────────────────▶  S3 (docchat-uploads, private)
# 3. App Runner reads the PDF, chunks + embeds it, writes vectors
App Runner  ─▶  RDS Postgres + pgvector (private subnet)
# 4. Secrets & observability throughout
App Runner  ─▶  Secrets Manager (DB password, API keys)
App Runner  ─▶  CloudWatch (logs + metrics + alarms)

And the boto3 snippet that powers step 1 — the heart of the whole flow, with the IAM role (not access keys) supplying credentials automatically:

app/storage.py
import boto3

s3 = boto3.client("s3", region_name="me-central-1")

def presigned_upload(user_id: str, filename: str) -> str:
    """Return a short-lived URL the browser can PUT to."""
    key = f"{user_id}/{filename}"
    return s3.generate_presigned_url(
        "put_object",
        Params={
            "Bucket": "docchat-uploads",
            "Key": key,
            "ContentType": "application/pdf",
        },
        ExpiresIn=300,
    )

No credentials in that code — App Runner runs under an IAM role scoped to exactly s3:PutObject on docchat-uploads/*. Least privilege, presigned, private, and resident in the UAE. That's the production shape.

10 · Check yourself

Answer from memory — retrieval is what moves this from "I read it" to "I know it".

Recall quiz

Which AWS Region keeps DocChat's data inside the UAE?

How should a container get access to one S3 bucket?

What lets the browser upload a PDF straight to S3?

Best compute for DocChat's normal FastAPI web service?

Which is a classic AWS bill trap to watch?

Primary source ⭐ AWS — Overview of Amazon Web Services, the authoritative map of the services above. For the security mindset, read IAM security best practices, and for presigned uploads see the boto3 presigned URL guide.