You log in · EC2 + DynamoDB

12 steps from your browser to the database and back.

No background job: logging in doesn't change any todos. Try Create, Update or Delete.

InternetAWS Region · us-east-1VPCPublic subnet · to IGWYour browseron the internetIAMglobal · who can do whatAPI GatewayWebSocket API · live lineCloudWatch Logsapp log linesInternet gatewaythe VPC's doorEC2 instanceruns the web appDynamoDBTodo.io's tablesYou log in1Into the VPC2Through the firewall3Read the form4Get AWS credentials5Call DynamoDB6Is this allowed?7DynamoDB answers8Check the password9Back to you10Open the live line11Leave a note12

Text version of this diagram.

What sits where:

  • AWS Region · us-east-1: A Region is a part of the world where AWS has data centers. Most things you build live in one Region. us-east-1 is in northern Virginia. contains:
    • VPC: Virtual private cloud: your own private network inside AWS. Nothing gets in or out except the ways you allow. contains:
      • Public subnet · to IGW: A slice of the VPC that can reach the internet directly, through the internet gateway (IGW). contains:
        • EC2 instance (runs the web app): A virtual server you rent from AWS. You pick its size and operating system, and you look after it.
      • Internet gateway (the VPC's door): The door between a VPC and the internet. Without one, nothing in the VPC can talk to the internet directly.
    • API Gateway (WebSocket API · live line): The front door for the app's web requests. A WebSocket API keeps a connection open, so the server can push updates to your browser instantly.
    • CloudWatch Logs (app log lines): Collects the lines your app writes about what it's doing (its logs), so you can search them later.
    • DynamoDB (Todo.io's tables): AWS's serverless NoSQL database: you store and fetch items by key, and it scales by itself with no server to manage.
  • Your browser (on the internet)
  • IAM (global · who can do what): Identity and Access Management: decides who (and which app) may do what in AWS. It's global, the same in every Region.

Steps, in order:

  1. You log in. Path: Your browser. Your browser sends POST /login over HTTPS.
  2. Into the VPC. Path: Your browser → Internet gateway. todo.io points at the instance's public IP, so the request comes in through the VPC's internet gateway. The public IP changes if the instance is stopped and started (an Elastic IP stays put), so the DNS record has to follow it. In Level 2 todo.io points at a load balancer's DNS name instead, which never changes.
  3. Through the firewall. Path: Internet gateway → EC2 instance. The security group around the instance allows HTTPS on port 443, so the request reaches the web server. Checks: security-group: todo-app-sg: TCP 443 from 0.0.0.0/0 → allow.
  4. Read the form. Path: EC2 instance. The app reads your email and password. You're a customer of Todo.io, not an IAM user, so AWS has no idea who you are.
  5. Get AWS credentials. Path: EC2 instance. The app gets temporary credentials for its IAM role (the instance profile) from the instance metadata service, which lives on the host itself. No keys are stored in the code.
  6. Call DynamoDB. Path: EC2 instance → Internet gateway → DynamoDB. The app calls GetItem on the DynamoDB API over HTTPS, signed with those credentials. It leaves through the internet gateway because DynamoDB is a public AWS service, not something inside your VPC.
  7. Is this allowed?. Path: DynamoDB → IAM. DynamoDB checks with IAM: may todo-app-ec2-role do dynamodb:GetItem here? The policy on the role allows it on the table's ARN. Checks: iam: todo-app-ec2-role: dynamodb:GetItem on table/Users → allow.
  8. DynamoDB answers. Path: DynamoDB → Internet gateway → EC2 instance. DynamoDB returns your user item, including the stored password hash.
  9. Check the password. Path: EC2 instance. The app hashes the password you typed and compares it with the stored hash. They match, so it starts a session for you. Checks: app: Password hash matches → allow.
  10. Back to you. Path: EC2 instance → Internet gateway → Your browser. You get 200 OK plus a session cookie, so your next requests prove who you are.
  11. Open the live line. Path: Your browser → API Gateway. Now that you are logged in, your browser opens a WebSocket to API Gateway. It stays open, and the app saves its connection id against your user.
  12. Leave a note. Path: EC2 instance → Internet gateway → CloudWatch Logs. The app's log line goes to CloudWatch Logs so Todo.io's engineers can see what happened. Like SQS, it's a public AWS service, so it leaves through the internet gateway.
Step 1 of 12
Step 1 of 12
  1. Your browser sends POST /login over HTTPS.

    Show me the real thing

    The HTTP request

    POST /login HTTP/1.1
    Host: todo.io
    Content-Type: application/json
    
    {"email":"sam@example.com","password":"••••••••"}
  2. todo.io points at the instance's public IP, so the request comes in through the VPC's internet gateway. The public IP changes if the instance is stopped and started (an Elastic IP stays put), so the DNS record has to follow it. In Level 2 todo.io points at a load balancer's DNS name instead, which never changes.

    Exam tipA subnet is "public" only because its route table sends 0.0.0.0/0 to an internet gateway. Nothing else makes it public.

  3. The security group around the instance allows HTTPS on port 443, so the request reaches the web server.

    • 🛡Security group todo-app-sg: TCP 443 from 0.0.0.0/0Allowed
    Show me the real thing

    Inbound rules on todo-app-sg

    Type    Protocol  Port  Source
    HTTPS   TCP       443   0.0.0.0/0

    Exam tipThe classic way to do HTTPS is a free AWS Certificate Manager (ACM) certificate on a load balancer or CloudFront (Level 2). Since 2025 ACM can also sell you an exportable certificate to install on the instance or container itself.

  4. The app reads your email and password. You're a customer of Todo.io, not an IAM user, so AWS has no idea who you are.

    Exam tipYour app's customers are never IAM principals. IAM is for the people and software that manage or call AWS itself.

  5. The app gets temporary credentials for its IAM role (the instance profile) from the instance metadata service, which lives on the host itself. No keys are stored in the code.

    Show me the real thing

    Trust policy on todo-app-ec2-role: who may use this role

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": "ec2.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }

    Exam tipNever store access keys on an instance. Attach a role through an instance profile; the SDK fetches its credentials from 169.254.169.254.

  6. The app calls GetItem on the DynamoDB API over HTTPS, signed with those credentials. It leaves through the internet gateway because DynamoDB is a public AWS service, not something inside your VPC.

    Show me the real thing

    The GetItem request body

    {
      "TableName": "Users",
      "Key": {
        "email": {
          "S": "sam@example.com"
        }
      }
    }

    Exam tipA VPC gateway endpoint (Level 1) keeps this traffic off the internet, and it costs nothing.

  7. DynamoDB checks with IAM: may todo-app-ec2-role do dynamodb:GetItem here? The policy on the role allows it on the table's ARN.

    • 🔑IAM todo-app-ec2-role: dynamodb:GetItem on table/UsersAllowed
    Show me the real thing

    Permissions policy on todo-app-ec2-role

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "dynamodb:GetItem",
            "dynamodb:Query",
            "dynamodb:PutItem",
            "dynamodb:UpdateItem",
            "dynamodb:DeleteItem"
          ],
          "Resource": [
            "arn:aws:dynamodb:us-east-1:111122223333:table/Users",
            "arn:aws:dynamodb:us-east-1:111122223333:table/Todos"
          ]
        }
      ]
    }

    Exam tipIAM starts from deny. A request is only allowed if some policy explicitly allows it and nothing explicitly denies it.

  8. DynamoDB returns your user item, including the stored password hash.

  9. The app hashes the password you typed and compares it with the stored hash. They match, so it starts a session for you.

    • ✓App Password hash matchesAllowed
  10. You get 200 OK plus a session cookie, so your next requests prove who you are.

    Show me the real thing

    The HTTP response

    HTTP/1.1 200 OK
    Set-Cookie: session=eyJ1c2VySWQiOiJ1LTEyMyJ9.Zm9v; HttpOnly; Secure

    Exam tipSecurity groups are stateful: a reply to an allowed request goes back out automatically. No outbound rule needed for it.

  11. Now that you are logged in, your browser opens a WebSocket to API Gateway. It stays open, and the app saves its connection id against your user.

    Show me the real thing

    The WebSocket handshake

    GET /prod HTTP/1.1
    Host: ws123.execute-api.us-east-1.amazonaws.com
    Upgrade: websocket
    Connection: Upgrade
    
    → API Gateway assigns connection id Ab3xYzEXAMPLE=

    Exam tipAn API Gateway WebSocket API has $connect, $disconnect and $default routes. Each one can call Lambda or an HTTP backend.

  12. The app's log line goes to CloudWatch Logs so Todo.io's engineers can see what happened. Like SQS, it's a public AWS service, so it leaves through the internet gateway.

    Show me the real thing

    The log line

    2026-10-11T18:42:07Z INFO POST /login user=u-123 status=200 14ms

    Exam tipEC2 doesn't send logs by itself. You install the CloudWatch agent and give the instance role permission to write logs.

What to notice

  • You run the server. Patching, scaling and the HTTPS certificate are your job. One instance in one Availability Zone is a single point of failure (Level 2 adds a load balancer and a second AZ).
  • IAM approved every database call, using a role. There's no database password anywhere.
  • DynamoDB isn't in your VPC. It's a public AWS service called over HTTPS.
  • A WebSocket stays open, so the server side can send you updates without you asking.
  • You, the customer, were never an IAM user. IAM controls the app, not its customers.