<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Micheal Angelo</title>
    <description>The latest articles on DEV Community by Micheal Angelo (@micheal_angelo_41cea4e81a).</description>
    <link>https://dev.to/micheal_angelo_41cea4e81a</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3692427%2F335051a9-3e2a-438a-8022-aff118532b01.jpg</url>
      <title>DEV Community: Micheal Angelo</title>
      <link>https://dev.to/micheal_angelo_41cea4e81a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/micheal_angelo_41cea4e81a"/>
    <language>en</language>
    <item>
      <title>From Local FastAPI to AWS: Deploying a Dockerized Backend with ECR, ECS Fargate, and RDS PostgreSQL</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Sun, 09 Aug 2026 17:17:53 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/from-local-fastapi-to-aws-deploying-a-dockerized-backend-with-ecr-ecs-fargate-and-rds-postgresql-15jd</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/from-local-fastapi-to-aws-deploying-a-dockerized-backend-with-ecr-ecs-fargate-and-rds-postgresql-15jd</guid>
      <description>&lt;p&gt;Deploying an application to the cloud for the first time can feel overwhelming.&lt;/p&gt;

&lt;p&gt;There are containers, container registries, networking, IAM, databases, security groups, task definitions, services, and many other concepts that need to work together.&lt;/p&gt;

&lt;p&gt;This article documents my first end-to-end AWS deployment.&lt;/p&gt;

&lt;p&gt;I took a &lt;strong&gt;FastAPI backend running locally&lt;/strong&gt;, containerized it with &lt;strong&gt;Docker&lt;/strong&gt;, pushed the image to &lt;strong&gt;Amazon ECR&lt;/strong&gt;, deployed it using &lt;strong&gt;Amazon ECS with AWS Fargate&lt;/strong&gt;, and connected it to &lt;strong&gt;PostgreSQL running on Amazon RDS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal wasn't simply to get an application running on AWS.&lt;/p&gt;

&lt;p&gt;I wanted to understand what actually happens when a locally developed backend becomes a cloud-hosted application.&lt;/p&gt;




&lt;h1&gt;
  
  
  Starting Point: A FastAPI Application
&lt;/h1&gt;

&lt;p&gt;The application started as a normal FastAPI backend running on my local machine.&lt;/p&gt;

&lt;p&gt;The basic architecture was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   │
   ▼
FastAPI
   │
   ▼
PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Locally, I could start the application with Uvicorn and connect it to a PostgreSQL database running on my machine.&lt;/p&gt;

&lt;p&gt;This is convenient for development, but eventually the application needs to run somewhere other than my laptop.&lt;/p&gt;

&lt;p&gt;That raised the first question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I move this application to AWS?&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 1 — Containerizing the Application
&lt;/h1&gt;

&lt;p&gt;The first step was to package the FastAPI application into a Docker container.&lt;/p&gt;

&lt;p&gt;Instead of depending on my local Python installation, virtual environment, and manually installed packages, Docker allows the application and its dependencies to be packaged together.&lt;/p&gt;

&lt;p&gt;The conceptual architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FastAPI Application
        │
        ▼
    Dockerfile
        │
        ▼
   Docker Image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Docker image contains the application code, Python environment, dependencies, and the instructions required to start the application.&lt;/p&gt;

&lt;p&gt;This gives us a consistent artifact that can be moved between environments.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Does this server have the correct Python version and dependencies?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;we can ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Can this server run the Docker image?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a much simpler deployment model.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 2 — Amazon ECR
&lt;/h1&gt;

&lt;p&gt;Once the Docker image was created locally, the next problem was:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where should the image be stored?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Amazon Elastic Container Registry (ECR)&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;ECR is a managed container registry provided by AWS.&lt;/p&gt;

&lt;p&gt;The workflow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer Laptop
       │
       │ docker build
       ▼
   Docker Image
       │
       │ docker push
       ▼
     Amazon ECR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ECR acts as the place where AWS can retrieve the container image when deploying the application.&lt;/p&gt;

&lt;p&gt;The important distinction is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ECR stores the image.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It does not run the application.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 3 — Amazon ECS
&lt;/h1&gt;

&lt;p&gt;After storing the image in ECR, we need something that can actually run it.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Amazon Elastic Container Service (ECS)&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;ECS is a container orchestration service.&lt;/p&gt;

&lt;p&gt;It manages the deployment and operation of containers.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Amazon ECR
     │
     │ Pull Image
     ▼
Amazon ECS
     │
     ▼
Container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, ECS itself does not necessarily provide the underlying compute capacity.&lt;/p&gt;

&lt;p&gt;That brings us to Fargate.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 4 — AWS Fargate
&lt;/h1&gt;

&lt;p&gt;AWS Fargate allows ECS containers to run without us having to manage EC2 servers ourselves.&lt;/p&gt;

&lt;p&gt;With a traditional EC2-based deployment, we would need to manage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EC2
 ├── Operating System
 ├── Docker
 ├── Security Updates
 ├── Container Runtime
 └── Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Fargate, AWS manages the underlying compute infrastructure.&lt;/p&gt;

&lt;p&gt;We primarily define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which container image should run&lt;/li&gt;
&lt;li&gt;How much CPU it needs&lt;/li&gt;
&lt;li&gt;How much memory it needs&lt;/li&gt;
&lt;li&gt;Which ports it uses&lt;/li&gt;
&lt;li&gt;Which IAM permissions it requires&lt;/li&gt;
&lt;li&gt;Which networking configuration it should use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Amazon ECR
     │
     ▼
ECS Task
     │
     ▼
AWS Fargate
     │
     ▼
FastAPI Container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was one of the important distinctions I learned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ECS manages the containers, while Fargate provides the serverless compute capacity for those containers.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 5 — Connecting PostgreSQL with Amazon RDS
&lt;/h1&gt;

&lt;p&gt;The FastAPI application also needed a database.&lt;/p&gt;

&lt;p&gt;Instead of continuing to use PostgreSQL running on my laptop, I moved the database to &lt;strong&gt;Amazon RDS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Amazon RDS is a managed relational database service.&lt;/p&gt;

&lt;p&gt;The architecture now looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             AWS
              │
       ┌──────┴──────┐
       │             │
       ▼             ▼
 ECS Fargate       RDS
       │          PostgreSQL
       │
   FastAPI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The FastAPI container communicates with PostgreSQL over the AWS network.&lt;/p&gt;

&lt;p&gt;This introduced an important networking concept.&lt;/p&gt;

&lt;p&gt;The application and database don't simply communicate because they are both "inside AWS."&lt;/p&gt;

&lt;p&gt;They need appropriate networking and security configuration.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding the Network
&lt;/h1&gt;

&lt;p&gt;This was one of the most important parts of the deployment.&lt;/p&gt;

&lt;p&gt;A simplified architecture looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
    │
    ▼
AWS Networking
    │
    ▼
ECS Fargate
    │
    │ PostgreSQL connection
    ▼
RDS PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ECS task and RDS instance need to be able to communicate with each other through the configured network.&lt;/p&gt;

&lt;p&gt;This means understanding concepts such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC&lt;/li&gt;
&lt;li&gt;Subnets&lt;/li&gt;
&lt;li&gt;Security Groups&lt;/li&gt;
&lt;li&gt;IP addresses&lt;/li&gt;
&lt;li&gt;Ports&lt;/li&gt;
&lt;li&gt;Inbound rules&lt;/li&gt;
&lt;li&gt;Outbound rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For PostgreSQL, the default port is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5432
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database security configuration therefore needs to allow the appropriate application traffic to reach that port.&lt;/p&gt;

&lt;p&gt;This was a good reminder that deploying an application isn't simply about putting a Docker container somewhere.&lt;/p&gt;

&lt;p&gt;The components need to be able to communicate securely.&lt;/p&gt;




&lt;h1&gt;
  
  
  Security Groups
&lt;/h1&gt;

&lt;p&gt;Security Groups act as virtual firewalls for AWS resources.&lt;/p&gt;

&lt;p&gt;For example, the RDS database should not simply allow PostgreSQL traffic from the entire Internet.&lt;/p&gt;

&lt;p&gt;Instead, the goal is to allow only the traffic that actually needs access to the database.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
   X
   │
   │ PostgreSQL traffic
   │
   ▼
RDS PostgreSQL

Only trusted application traffic
should be allowed to reach port 5432.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This follows an important security principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Allow only the traffic that is actually required.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  IAM and Permissions
&lt;/h1&gt;

&lt;p&gt;Another major part of the deployment was &lt;strong&gt;IAM — Identity and Access Management&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;AWS resources should not simply have unrestricted access to everything else.&lt;/p&gt;

&lt;p&gt;Instead, AWS uses identities and policies to determine what actions are permitted.&lt;/p&gt;

&lt;p&gt;For example, the ECS task may need permission to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull an image from ECR&lt;/li&gt;
&lt;li&gt;Write logs to CloudWatch&lt;/li&gt;
&lt;li&gt;Access other AWS services when required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where IAM roles become important.&lt;/p&gt;

&lt;p&gt;Rather than embedding AWS credentials inside the application container, AWS allows services to assume IAM roles.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ECS Task
   │
   ▼
IAM Role
   │
   ▼
AWS Permissions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much safer than putting long-lived access keys directly inside the application.&lt;/p&gt;




&lt;h1&gt;
  
  
  Task Definitions
&lt;/h1&gt;

&lt;p&gt;One of the concepts I had to understand while working with ECS was the &lt;strong&gt;Task Definition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A task definition describes how an ECS task should run.&lt;/p&gt;

&lt;p&gt;It specifies information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Container image&lt;/li&gt;
&lt;li&gt;CPU&lt;/li&gt;
&lt;li&gt;Memory&lt;/li&gt;
&lt;li&gt;Port mappings&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;IAM roles&lt;/li&gt;
&lt;li&gt;Logging configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Task Definition
       │
       ├── Container Image
       ├── CPU
       ├── Memory
       ├── Ports
       ├── Environment
       └── IAM Configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ECS uses this definition to know how to launch the container.&lt;/p&gt;




&lt;h1&gt;
  
  
  ECS Service
&lt;/h1&gt;

&lt;p&gt;A task is a running instance of the container configuration.&lt;/p&gt;

&lt;p&gt;An ECS Service manages the desired number of running tasks.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ECS Service

Desired Tasks = 1

        │
        ▼

   FastAPI Task
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we later configure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Desired Tasks = 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ECS can maintain multiple running tasks.&lt;/p&gt;

&lt;p&gt;This is where ECS begins to become useful for building scalable containerized applications.&lt;/p&gt;




&lt;h1&gt;
  
  
  Environment Variables
&lt;/h1&gt;

&lt;p&gt;The application also needs configuration such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_HOST
DATABASE_PORT
DATABASE_NAME
DATABASE_USER
DATABASE_PASSWORD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These values should not be hardcoded into the application.&lt;/p&gt;

&lt;p&gt;Instead, the container receives configuration through its runtime environment.&lt;/p&gt;

&lt;p&gt;This creates another useful separation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application Code
       +
Environment Configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same Docker image can therefore be deployed to different environments with different configuration.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Complete Deployment Flow
&lt;/h1&gt;

&lt;p&gt;After putting all the pieces together, the deployment flow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer
    │
    ▼
FastAPI Application
    │
    ▼
Docker Build
    │
    ▼
Docker Image
    │
    ▼
Amazon ECR
    │
    ▼
Amazon ECS
    │
    ▼
AWS Fargate
    │
    ▼
FastAPI Container
    │
    ▼
Amazon RDS
    │
    ▼
PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each component has a specific responsibility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Docker
→ Packages the application

ECR
→ Stores the Docker image

ECS
→ Manages the container workload

Fargate
→ Provides serverless compute for the container

RDS
→ Runs managed PostgreSQL

IAM
→ Controls permissions

VPC / Security Groups
→ Control network communication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  What Made This Deployment Difficult?
&lt;/h1&gt;

&lt;p&gt;The difficult part wasn't writing the FastAPI application.&lt;/p&gt;

&lt;p&gt;The challenging part was understanding how all the infrastructure pieces fit together.&lt;/p&gt;

&lt;p&gt;A locally running application can feel deceptively simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FastAPI
   │
   ▼
PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In AWS, the architecture becomes much more explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    AWS
                     │
              ┌──────┴──────┐
              │             │
              ▼             ▼
          ECS/Fargate      RDS
              │          PostgreSQL
              │
          FastAPI
              │
              ▼
        Network + IAM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every connection has to be intentionally configured.&lt;/p&gt;

&lt;p&gt;The deployment therefore became an exercise in understanding infrastructure rather than simply moving code from one machine to another.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Biggest Lessons
&lt;/h1&gt;

&lt;p&gt;This was my first end-to-end deployment of a FastAPI backend to AWS, and several concepts became much clearer after actually building it.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Docker separates the application from the machine
&lt;/h3&gt;

&lt;p&gt;The application becomes a portable artifact rather than a collection of files that must be manually configured on every server.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. ECR and ECS have different responsibilities
&lt;/h3&gt;

&lt;p&gt;ECR stores container images.&lt;/p&gt;

&lt;p&gt;ECS runs and manages containers.&lt;/p&gt;

&lt;p&gt;They solve different problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Fargate removes server management
&lt;/h3&gt;

&lt;p&gt;I don't need to manually manage an EC2 instance just to run my Docker container.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. RDS separates database infrastructure from the application
&lt;/h3&gt;

&lt;p&gt;The FastAPI application doesn't need to run PostgreSQL itself.&lt;/p&gt;

&lt;p&gt;AWS manages the database infrastructure through RDS.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Networking is part of application deployment
&lt;/h3&gt;

&lt;p&gt;A backend and database don't communicate automatically just because they are both hosted on AWS.&lt;/p&gt;

&lt;p&gt;VPC configuration, subnets, routes, security groups, ports, and connectivity all matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. IAM is fundamental
&lt;/h3&gt;

&lt;p&gt;Cloud applications need controlled access between services.&lt;/p&gt;

&lt;p&gt;IAM roles and policies provide that control without requiring applications to store long-lived credentials.&lt;/p&gt;




&lt;h1&gt;
  
  
  From Localhost to AWS
&lt;/h1&gt;

&lt;p&gt;The most interesting part of this project was seeing how familiar local development concepts map to AWS infrastructure.&lt;/p&gt;

&lt;p&gt;Locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop
 │
 ├── FastAPI
 │
 └── PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In AWS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS
 │
 ├── ECS + Fargate
 │      └── FastAPI Container
 │
 └── RDS
        └── PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the container image moves through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local Docker
     │
     ▼
Amazon ECR
     │
     ▼
ECS + Fargate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application itself didn't fundamentally change.&lt;/p&gt;

&lt;p&gt;The infrastructure around it did.&lt;/p&gt;

&lt;p&gt;That was probably the biggest lesson from this deployment.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Before working on this project, services such as ECR, ECS, Fargate, RDS, IAM, and VPC felt like separate AWS concepts.&lt;/p&gt;

&lt;p&gt;After deploying the application, they started to make sense as pieces of one larger system.&lt;/p&gt;

&lt;p&gt;A cloud deployment isn't simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Put the application on AWS."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is a collection of decisions about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where the application runs&lt;/li&gt;
&lt;li&gt;How it is packaged&lt;/li&gt;
&lt;li&gt;Where the image is stored&lt;/li&gt;
&lt;li&gt;How containers are managed&lt;/li&gt;
&lt;li&gt;Where the database lives&lt;/li&gt;
&lt;li&gt;How services communicate&lt;/li&gt;
&lt;li&gt;Which permissions they have&lt;/li&gt;
&lt;li&gt;How network traffic is controlled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This deployment gave me my first practical understanding of how those pieces fit together.&lt;/p&gt;

&lt;p&gt;And more importantly, it showed me that moving from local development to the cloud is not just about learning AWS services.&lt;/p&gt;

&lt;p&gt;It is about learning how applications, infrastructure, networking, security, and deployment processes work together.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was my first end-to-end AWS deployment of a Dockerized FastAPI backend.&lt;/p&gt;

&lt;p&gt;The final stack consisted of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FastAPI
   │
   ▼
Docker
   │
   ▼
Amazon ECR
   │
   ▼
Amazon ECS
   │
   ▼
AWS Fargate
   │
   ▼
PostgreSQL
   │
   ▼
Amazon RDS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Along the way, I learned about Docker images, container registries, ECS task definitions, Fargate, RDS, IAM, VPC networking, security groups, environment configuration, and the overall deployment lifecycle.&lt;/p&gt;

&lt;p&gt;For me, this was the point where AWS stopped feeling like a list of services and started feeling like an actual infrastructure platform.&lt;/p&gt;




</description>
      <category>aws</category>
      <category>fastapi</category>
      <category>docker</category>
      <category>ecs</category>
    </item>
    <item>
      <title>I Built an Application—Should I Deploy It on EC2, ECS + Fargate, or AWS Lambda?</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Tue, 28 Jul 2026 08:46:24 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/i-built-an-application-should-i-deploy-it-on-ec2-ecs-fargate-or-aws-lambda-4jcb</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/i-built-an-application-should-i-deploy-it-on-ec2-ecs-fargate-or-aws-lambda-4jcb</guid>
      <description>&lt;p&gt;One question kept coming up while I was learning AWS.&lt;/p&gt;

&lt;p&gt;After building an application, &lt;strong&gt;where should it actually run?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS offers several compute services, but three appear repeatedly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon EC2&lt;/li&gt;
&lt;li&gt;Amazon ECS with AWS Fargate&lt;/li&gt;
&lt;li&gt;AWS Lambda&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Initially they all seemed to do the same thing—they execute code.&lt;/p&gt;

&lt;p&gt;The more I learned, the more I realised they represent three different deployment models rather than three competing services.&lt;/p&gt;

&lt;p&gt;This article explains the mental model that helped me understand when each one makes sense.&lt;/p&gt;




&lt;h1&gt;
  
  
  Every Application Needs Compute
&lt;/h1&gt;

&lt;p&gt;Regardless of whether you're building a REST API, processing uploaded images, or running background jobs, somewhere there must be a computer executing your code.&lt;/p&gt;

&lt;p&gt;AWS offers different ways to provide that compute.&lt;/p&gt;

&lt;p&gt;The question isn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which service is the best?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, the better question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which execution model matches my application?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Option 1: Amazon EC2
&lt;/h1&gt;

&lt;p&gt;Amazon EC2 gives you a virtual machine.&lt;/p&gt;

&lt;p&gt;Inside that virtual machine you install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux (or Windows)&lt;/li&gt;
&lt;li&gt;Docker (optional)&lt;/li&gt;
&lt;li&gt;Your application&lt;/li&gt;
&lt;li&gt;Any additional software you need&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Amazon EC2
      │
      ▼
EC2 Instance
      │
      ▼
Ubuntu Linux
      │
      ▼
FastAPI / Node.js / Spring Boot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With EC2 you are responsible for managing the server.&lt;/p&gt;

&lt;p&gt;That includes operating system updates, security patches, monitoring, backups, scaling, and maintenance.&lt;/p&gt;

&lt;h3&gt;
  
  
  When EC2 Makes Sense
&lt;/h3&gt;

&lt;p&gt;EC2 is often a good choice when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your application runs continuously.&lt;/li&gt;
&lt;li&gt;You need complete control over the operating system.&lt;/li&gt;
&lt;li&gt;You install custom software.&lt;/li&gt;
&lt;li&gt;You require GPU instances.&lt;/li&gt;
&lt;li&gt;You need SSH access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Typical examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web applications&lt;/li&gt;
&lt;li&gt;Jenkins&lt;/li&gt;
&lt;li&gt;GitLab&lt;/li&gt;
&lt;li&gt;Minecraft servers&lt;/li&gt;
&lt;li&gt;Machine learning servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest advantage is flexibility.&lt;/p&gt;

&lt;p&gt;The trade-off is operational responsibility.&lt;/p&gt;




&lt;h1&gt;
  
  
  Option 2: AWS Lambda
&lt;/h1&gt;

&lt;p&gt;Lambda follows a completely different execution model.&lt;/p&gt;

&lt;p&gt;Instead of creating a server, you upload only your code.&lt;/p&gt;

&lt;p&gt;Whenever an event occurs, AWS creates the execution environment, runs your function, returns the result, and then releases the compute resources.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event
   │
Start Function
   │
Execute Code
   │
Return Result
   │
Stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike EC2, no server sits waiting for requests.&lt;/p&gt;

&lt;p&gt;Compute exists only while the function is running.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Lambda Makes Sense
&lt;/h3&gt;

&lt;p&gt;Lambda works particularly well for workloads that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event-driven&lt;/li&gt;
&lt;li&gt;Short-lived&lt;/li&gt;
&lt;li&gt;Stateless&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Processing uploaded files&lt;/li&gt;
&lt;li&gt;Sending emails&lt;/li&gt;
&lt;li&gt;Scheduled tasks&lt;/li&gt;
&lt;li&gt;Payment processing&lt;/li&gt;
&lt;li&gt;API Gateway integrations&lt;/li&gt;
&lt;li&gt;Image resizing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One major advantage is that there are no servers to manage.&lt;/p&gt;

&lt;p&gt;AWS automatically provisions infrastructure whenever the function is invoked.&lt;/p&gt;




&lt;h1&gt;
  
  
  Option 3: Amazon ECS with AWS Fargate
&lt;/h1&gt;

&lt;p&gt;Suppose you've already containerised your application using Docker.&lt;/p&gt;

&lt;p&gt;You don't necessarily want to manage Linux servers, but you also don't want to rewrite the application as Lambda functions.&lt;/p&gt;

&lt;p&gt;This is where ECS and Fargate become useful.&lt;/p&gt;

&lt;p&gt;Amazon ECS manages containers.&lt;/p&gt;

&lt;p&gt;AWS Fargate provides the compute needed to run those containers.&lt;/p&gt;

&lt;p&gt;You simply provide a Docker image.&lt;/p&gt;

&lt;p&gt;AWS handles the underlying infrastructure.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Docker Image
      │
      ▼
Amazon ECS
      │
      ▼
AWS Fargate
      │
      ▼
Running Container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides many of the benefits of containers without requiring direct server administration.&lt;/p&gt;

&lt;h3&gt;
  
  
  When ECS + Fargate Makes Sense
&lt;/h3&gt;

&lt;p&gt;Fargate is often a good choice when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your application already runs in Docker.&lt;/li&gt;
&lt;li&gt;You want containers without managing Linux servers.&lt;/li&gt;
&lt;li&gt;Your application runs continuously.&lt;/li&gt;
&lt;li&gt;You're building microservices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It provides a balance between the flexibility of containers and the convenience of managed infrastructure.&lt;/p&gt;




&lt;h1&gt;
  
  
  Comparing the Three
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Recommended Service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Need a virtual machine&lt;/td&gt;
&lt;td&gt;Amazon EC2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running Docker containers without managing servers&lt;/td&gt;
&lt;td&gt;Amazon ECS + AWS Fargate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event-driven function execution&lt;/td&gt;
&lt;td&gt;AWS Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Rather than replacing one another, these services address different architectural needs.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Helpful Mental Model
&lt;/h1&gt;

&lt;p&gt;One way I started thinking about these services was by asking what remains active while the application waits.&lt;/p&gt;

&lt;p&gt;With a traditional web application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Start Server
      │
Waiting...
      │
Receive Request
      │
Process
      │
Waiting...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server is always running.&lt;/p&gt;

&lt;p&gt;With Lambda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event
   │
Execute
   │
Finish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no continuously running server.&lt;/p&gt;

&lt;p&gt;Execution begins only when an event occurs.&lt;/p&gt;




&lt;h1&gt;
  
  
  Background Processing
&lt;/h1&gt;

&lt;p&gt;Another interesting distinction appears when using Amazon SQS.&lt;/p&gt;

&lt;p&gt;With a traditional application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Server
   │
Waiting
   │
Request
   │
Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application waits for work.&lt;/p&gt;

&lt;p&gt;With SQS and Lambda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message
   │
SQS Queue
   │
Lambda Starts
   │
Process Message
   │
Lambda Stops
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of the server waiting, the queue stores work until Lambda is triggered.&lt;/p&gt;

&lt;p&gt;This architecture is particularly useful for asynchronous tasks that don't need an immediate response.&lt;/p&gt;




&lt;h1&gt;
  
  
  So Which One Should You Choose?
&lt;/h1&gt;

&lt;p&gt;Over time, I realised that the decision is usually simpler than it first appears.&lt;/p&gt;

&lt;p&gt;If you need a virtual machine and complete control over the operating system, EC2 is often the right choice.&lt;/p&gt;

&lt;p&gt;If you've already packaged your application as a Docker container and don't want to manage Linux servers, ECS with Fargate provides a managed container platform.&lt;/p&gt;

&lt;p&gt;If your workload is short-lived and event-driven, Lambda allows you to focus entirely on your code without thinking about servers.&lt;/p&gt;

&lt;p&gt;Each service solves a different problem.&lt;/p&gt;

&lt;p&gt;The goal isn't to memorise features but to understand which execution model best matches the application you're building.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;One lesson that stood out while learning AWS was that compute isn't a single concept.&lt;/p&gt;

&lt;p&gt;Amazon EC2, ECS with Fargate, and Lambda all execute code, but they do so using different architectural approaches.&lt;/p&gt;

&lt;p&gt;Understanding those approaches makes it much easier to choose the right service instead of trying to force every application into the same deployment model.&lt;/p&gt;

&lt;p&gt;Once I started thinking in terms of execution models rather than AWS products, the differences between these services became much clearer.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>ec2</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Understanding AWS Compute Services: EC2, EC2 Instances, and When Lambda Makes More Sense</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Tue, 28 Jul 2026 05:51:51 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/understanding-aws-compute-services-ec2-ec2-instances-and-when-lambda-makes-more-sense-244g</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/understanding-aws-compute-services-ec2-ec2-instances-and-when-lambda-makes-more-sense-244g</guid>
      <description>&lt;p&gt;One of the first AWS services most developers learn is Amazon EC2.&lt;/p&gt;

&lt;p&gt;Soon after, they encounter AWS Lambda.&lt;/p&gt;

&lt;p&gt;Since both are compute services, one obvious question comes to mind:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why does AWS offer two different ways to run code?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Initially, I assumed the biggest difference was pricing.&lt;/p&gt;

&lt;p&gt;As I learned more, I realized the distinction goes much deeper.&lt;/p&gt;

&lt;p&gt;EC2 and Lambda represent two different approaches to running applications, each solving different kinds of problems.&lt;/p&gt;

&lt;p&gt;This article summarizes the mental model that helped me understand both services.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Is a Compute Service?
&lt;/h1&gt;

&lt;p&gt;A compute service provides the processing power required to run your application.&lt;/p&gt;

&lt;p&gt;Whether you're hosting a web application, processing uploaded files, or responding to API requests, somewhere there must be a machine executing your code.&lt;/p&gt;

&lt;p&gt;AWS provides several compute services, but two of the most common are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon EC2&lt;/li&gt;
&lt;li&gt;AWS Lambda&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although both execute code, they do so in very different ways.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding Amazon EC2
&lt;/h1&gt;

&lt;p&gt;Amazon EC2 (Elastic Compute Cloud) is an AWS service that allows you to create virtual machines on demand.&lt;/p&gt;

&lt;p&gt;One misconception I had early on was using the terms &lt;strong&gt;Amazon EC2&lt;/strong&gt; and &lt;strong&gt;EC2 Instance&lt;/strong&gt; interchangeably.&lt;/p&gt;

&lt;p&gt;They're related, but they are not the same thing.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Amazon EC2 (AWS Service)
        │
        ▼
EC2 Instance (Virtual Machine)
        │
        ▼
Operating System
        │
        ▼
Your Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Amazon EC2 is the service.&lt;/p&gt;

&lt;p&gt;An EC2 Instance is the virtual machine created by that service.&lt;/p&gt;

&lt;p&gt;Inside the instance, you install an operating system such as Ubuntu, Amazon Linux, or Windows Server.&lt;/p&gt;

&lt;p&gt;Your application then runs inside that operating system.&lt;/p&gt;




&lt;h1&gt;
  
  
  Where Does the Virtual Machine Come From?
&lt;/h1&gt;

&lt;p&gt;AWS owns physical servers inside its data centres.&lt;/p&gt;

&lt;p&gt;A hypervisor running on those servers creates multiple isolated virtual machines.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Physical Server
        │
   Hypervisor
        │
 ├── EC2 Instance
 ├── EC2 Instance
 ├── EC2 Instance
 └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Customers never interact with the hypervisor directly.&lt;/p&gt;

&lt;p&gt;AWS manages it, allowing developers to focus only on the virtual machines they launch.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Do People Say "SSH into the EC2"?
&lt;/h1&gt;

&lt;p&gt;This is simply shorthand.&lt;/p&gt;

&lt;p&gt;When someone says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"SSH into the EC2."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;they actually mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"SSH into the operating system running inside the EC2 Instance."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Likewise, when someone says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Deploy it on EC2."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;they usually mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Deploy it on an EC2 Instance."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Understanding this terminology made AWS documentation much easier for me to follow.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding AWS Lambda
&lt;/h1&gt;

&lt;p&gt;AWS Lambda follows a completely different model.&lt;/p&gt;

&lt;p&gt;Instead of creating and managing a virtual machine, you upload only your code.&lt;/p&gt;

&lt;p&gt;Whenever an event occurs—such as an HTTP request, an S3 upload, or a message arriving in an SQS queue—AWS automatically provisions the infrastructure required to execute that function.&lt;/p&gt;

&lt;p&gt;Once execution finishes, the compute environment is released.&lt;/p&gt;

&lt;p&gt;As developers, we don't manage operating systems or servers.&lt;/p&gt;

&lt;p&gt;We simply write the function.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding the Pricing Model
&lt;/h1&gt;

&lt;p&gt;One of the biggest differences between EC2 and Lambda is how they are billed.&lt;/p&gt;

&lt;p&gt;With Amazon EC2, compute resources are reserved for your instance while it is running.&lt;/p&gt;

&lt;p&gt;Whether the application receives one request or one million requests, the virtual machine continues to exist until you stop or terminate it.&lt;/p&gt;

&lt;p&gt;You are primarily paying for keeping those compute resources available.&lt;/p&gt;

&lt;p&gt;An analogy that helped me understand this was renting a flat.&lt;/p&gt;

&lt;p&gt;You pay rent for as long as you occupy it, whether you're inside or away.&lt;/p&gt;

&lt;p&gt;Lambda works differently.&lt;/p&gt;

&lt;p&gt;You are charged only when your function executes.&lt;/p&gt;

&lt;p&gt;The cost depends mainly on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The number of invocations&lt;/li&gt;
&lt;li&gt;The execution duration&lt;/li&gt;
&lt;li&gt;The memory allocated to the function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If nobody invokes the function, there is effectively no compute charge.&lt;/p&gt;

&lt;p&gt;A useful analogy is taking a taxi.&lt;/p&gt;

&lt;p&gt;You pay only while you're travelling.&lt;/p&gt;

&lt;p&gt;No journey means no fare.&lt;/p&gt;




&lt;h1&gt;
  
  
  EC2 vs Lambda
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Amazon EC2&lt;/th&gt;
&lt;th&gt;AWS Lambda&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Creates a virtual machine&lt;/td&gt;
&lt;td&gt;Executes individual functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You manage the operating system&lt;/td&gt;
&lt;td&gt;AWS manages the infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute resources remain available while the instance is running&lt;/td&gt;
&lt;td&gt;Compute resources are allocated only during execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Well suited for long-running applications&lt;/td&gt;
&lt;td&gt;Well suited for event-driven workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full control over the server&lt;/td&gt;
&lt;td&gt;Focus primarily on application code&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neither approach is universally better.&lt;/p&gt;

&lt;p&gt;They simply solve different problems.&lt;/p&gt;




&lt;h1&gt;
  
  
  When Might You Choose EC2?
&lt;/h1&gt;

&lt;p&gt;EC2 is often a good fit when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need full control over the operating system.&lt;/li&gt;
&lt;li&gt;Your application runs continuously.&lt;/li&gt;
&lt;li&gt;You host databases or web servers.&lt;/li&gt;
&lt;li&gt;You require custom software installations.&lt;/li&gt;
&lt;li&gt;Long-running background services are involved.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  When Might You Choose Lambda?
&lt;/h1&gt;

&lt;p&gt;Lambda is often a good fit when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Applications respond to events.&lt;/li&gt;
&lt;li&gt;Workloads are short-lived.&lt;/li&gt;
&lt;li&gt;Infrastructure management should be minimized.&lt;/li&gt;
&lt;li&gt;Traffic is unpredictable.&lt;/li&gt;
&lt;li&gt;You want to focus primarily on business logic.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;One lesson that stood out while learning AWS was that understanding services individually isn't enough.&lt;/p&gt;

&lt;p&gt;Building a mental model of how they fit into larger systems is much more valuable.&lt;/p&gt;

&lt;p&gt;Amazon EC2 isn't simply "a server."&lt;/p&gt;

&lt;p&gt;It is a service that creates virtual machines.&lt;/p&gt;

&lt;p&gt;AWS Lambda isn't "cheaper EC2."&lt;/p&gt;

&lt;p&gt;It is a different execution model designed around event-driven computing.&lt;/p&gt;

&lt;p&gt;Once I understood those distinctions, choosing between them became much less about comparing features and much more about selecting the right tool for the problem I was trying to solve.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>ec2</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Understanding AWS Cognito: How User Authentication Works Without Storing Passwords Yourself</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Mon, 27 Jul 2026 10:24:43 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/understanding-aws-cognito-how-user-authentication-works-without-storing-passwords-yourself-2p22</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/understanding-aws-cognito-how-user-authentication-works-without-storing-passwords-yourself-2p22</guid>
      <description>&lt;p&gt;Authentication is one of the first challenges developers encounter when building an application.&lt;/p&gt;

&lt;p&gt;Initially, it seems straightforward: ask users for an email and password, verify the credentials, and allow access.&lt;/p&gt;

&lt;p&gt;However, implementing authentication securely is much more involved than it first appears.&lt;/p&gt;

&lt;p&gt;Questions quickly begin to arise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where should passwords be stored?&lt;/li&gt;
&lt;li&gt;How should passwords be hashed?&lt;/li&gt;
&lt;li&gt;How are password resets implemented?&lt;/li&gt;
&lt;li&gt;How should Multi-Factor Authentication (MFA) be handled?&lt;/li&gt;
&lt;li&gt;How are secure authentication tokens generated?&lt;/li&gt;
&lt;li&gt;How can those tokens be trusted by the backend?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These responsibilities are exactly what &lt;strong&gt;AWS Cognito&lt;/strong&gt; is designed to solve.&lt;/p&gt;

&lt;p&gt;Rather than being just a JWT generator, Cognito is a fully managed identity and authentication service that handles the entire authentication lifecycle.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Use AWS Cognito?
&lt;/h1&gt;

&lt;p&gt;Imagine building authentication entirely from scratch.&lt;/p&gt;

&lt;p&gt;The authentication flow might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
   │
Email + Password
   │
   ▼
Backend
   │
Query Database
   │
Retrieve Password Hash
   │
Compare Password
   │
Generate JWT
   │
Return Access Token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this design, the backend becomes responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storing passwords&lt;/li&gt;
&lt;li&gt;Hashing passwords&lt;/li&gt;
&lt;li&gt;Verifying credentials&lt;/li&gt;
&lt;li&gt;Password resets&lt;/li&gt;
&lt;li&gt;Email verification&lt;/li&gt;
&lt;li&gt;Multi-factor authentication&lt;/li&gt;
&lt;li&gt;Refresh tokens&lt;/li&gt;
&lt;li&gt;JWT generation&lt;/li&gt;
&lt;li&gt;JWT validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Handling all of these securely is a significant engineering responsibility.&lt;/p&gt;

&lt;p&gt;AWS Cognito removes much of this complexity by managing authentication on your behalf.&lt;/p&gt;




&lt;h1&gt;
  
  
  The User Pool
&lt;/h1&gt;

&lt;p&gt;One of the central concepts in Cognito is the &lt;strong&gt;User Pool&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A User Pool is a managed identity database maintained by AWS.&lt;/p&gt;

&lt;p&gt;It stores authentication-related information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email address&lt;/li&gt;
&lt;li&gt;Username&lt;/li&gt;
&lt;li&gt;Password hash&lt;/li&gt;
&lt;li&gt;Phone number&lt;/li&gt;
&lt;li&gt;Email verification status&lt;/li&gt;
&lt;li&gt;MFA settings&lt;/li&gt;
&lt;li&gt;User status&lt;/li&gt;
&lt;li&gt;A unique identifier (&lt;code&gt;sub&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Pool

---------------------------------------
Email                 Password Hash
---------------------------------------
alice@example.com     *************
bob@example.com       *************
john@example.com      *************
---------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An important detail is that developers never have access to users' passwords or password hashes.&lt;/p&gt;

&lt;p&gt;Those are managed entirely by Cognito.&lt;/p&gt;




&lt;h1&gt;
  
  
  Where Does the User Pool Live?
&lt;/h1&gt;

&lt;p&gt;One question that initially confused me was where the User Pool actually exists.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;not&lt;/strong&gt; stored in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon RDS&lt;/li&gt;
&lt;li&gt;DynamoDB&lt;/li&gt;
&lt;li&gt;An EC2 instance&lt;/li&gt;
&lt;li&gt;Your application's VPC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, it exists inside AWS's managed Cognito service.&lt;/p&gt;

&lt;p&gt;This separation is intentional because authentication is infrastructure managed by AWS rather than application-specific data.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Isn't Cognito Inside the VPC?
&lt;/h1&gt;

&lt;p&gt;Many AWS resources live inside a Virtual Private Cloud (VPC), including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2&lt;/li&gt;
&lt;li&gt;RDS&lt;/li&gt;
&lt;li&gt;ECS&lt;/li&gt;
&lt;li&gt;Lambda (when configured for VPC access)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cognito works differently.&lt;/p&gt;

&lt;p&gt;Like IAM or Route 53, Cognito is an AWS-managed service that is accessed securely over HTTPS through AWS service endpoints.&lt;/p&gt;

&lt;p&gt;Your application communicates with Cognito—it does not host Cognito.&lt;/p&gt;




&lt;h1&gt;
  
  
  How Authentication Works
&lt;/h1&gt;

&lt;p&gt;When a user signs in, the flow is surprisingly simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 │
 │ Email + Password
 ▼
AWS Cognito
 │
 ▼
User Pool
 │
 ▼
Verify Credentials
 │
 ▼
Generate Tokens
 │
 ▼
Browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what does &lt;strong&gt;not&lt;/strong&gt; happen.&lt;/p&gt;

&lt;p&gt;The backend never:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Receives the user's password&lt;/li&gt;
&lt;li&gt;Compares password hashes&lt;/li&gt;
&lt;li&gt;Generates authentication tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cognito performs all of those responsibilities.&lt;/p&gt;




&lt;h1&gt;
  
  
  Signing Up
&lt;/h1&gt;

&lt;p&gt;During registration, the process is similar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 │
 │ Email
 │ Password
 ▼
AWS Cognito
 │
 ▼
User Pool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cognito automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validates password policies&lt;/li&gt;
&lt;li&gt;Stores passwords securely&lt;/li&gt;
&lt;li&gt;Sends verification emails (if configured)&lt;/li&gt;
&lt;li&gt;Creates the user identity&lt;/li&gt;
&lt;li&gt;Assigns a unique identifier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Applications no longer need to implement these features manually.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding the Tokens
&lt;/h1&gt;

&lt;p&gt;After successful authentication, Cognito returns three JWTs.&lt;/p&gt;

&lt;h2&gt;
  
  
  ID Token
&lt;/h2&gt;

&lt;p&gt;The ID Token identifies the authenticated user.&lt;/p&gt;

&lt;p&gt;It commonly contains information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User ID (&lt;code&gt;sub&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;Username&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Its purpose is identity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Access Token
&lt;/h2&gt;

&lt;p&gt;The Access Token is presented when calling protected APIs.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Authorization: Bearer &amp;lt;Access Token&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend validates this token before allowing access.&lt;/p&gt;




&lt;h2&gt;
  
  
  Refresh Token
&lt;/h2&gt;

&lt;p&gt;Access Tokens eventually expire.&lt;/p&gt;

&lt;p&gt;Instead of asking users to log in again, the Refresh Token can be exchanged for a new Access Token until it too expires.&lt;/p&gt;

&lt;p&gt;This allows users to remain signed in without repeatedly entering credentials.&lt;/p&gt;




&lt;h1&gt;
  
  
  How Does the Backend Trust Cognito?
&lt;/h1&gt;

&lt;p&gt;Every protected request includes the Access Token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser

Authorization: Bearer eyJhbGci...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather than checking passwords, the backend validates the JWT.&lt;/p&gt;

&lt;p&gt;Typical checks include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signature verification&lt;/li&gt;
&lt;li&gt;Issuer validation&lt;/li&gt;
&lt;li&gt;Audience validation&lt;/li&gt;
&lt;li&gt;Expiration time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the token is valid, access is granted.&lt;/p&gt;

&lt;p&gt;Otherwise, the request is rejected with a &lt;strong&gt;401 Unauthorized&lt;/strong&gt; response.&lt;/p&gt;

&lt;p&gt;The backend trusts the identity because the token was signed by Cognito.&lt;/p&gt;




&lt;h1&gt;
  
  
  Authentication Data vs Application Data
&lt;/h1&gt;

&lt;p&gt;One important architectural lesson is that authentication data and business data should remain separate.&lt;/p&gt;

&lt;p&gt;Cognito manages identity information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;Password&lt;/li&gt;
&lt;li&gt;MFA&lt;/li&gt;
&lt;li&gt;Verification status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Meanwhile, application databases store business information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Orders&lt;/li&gt;
&lt;li&gt;Addresses&lt;/li&gt;
&lt;li&gt;User preferences&lt;/li&gt;
&lt;li&gt;Shopping carts&lt;/li&gt;
&lt;li&gt;Subscription details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Applications typically store the Cognito user identifier (&lt;code&gt;sub&lt;/code&gt;) to associate business records with authenticated users.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authentication
      │
      ▼
 AWS Cognito

Business Data
      │
      ▼
RDS / DynamoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping these responsibilities separate improves both security and maintainability.&lt;/p&gt;




&lt;h1&gt;
  
  
  What If the Application Server Is Compromised?
&lt;/h1&gt;

&lt;p&gt;A question that naturally came to mind was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What happens if someone gains access to the EC2 instance?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An attacker might obtain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application source code&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Business database access (depending on IAM permissions)&lt;/li&gt;
&lt;li&gt;Access to other AWS services (depending on IAM policies)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, they still cannot retrieve users' passwords from Cognito.&lt;/p&gt;

&lt;p&gt;That's because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passwords are never stored on the EC2 instance.&lt;/li&gt;
&lt;li&gt;Password hashes are never stored in the application database.&lt;/li&gt;
&lt;li&gt;Cognito does not expose password hashes through its APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Separating authentication from application infrastructure significantly reduces the impact of a server compromise.&lt;/p&gt;




&lt;h1&gt;
  
  
  Can AWS Administrators See Passwords?
&lt;/h1&gt;

&lt;p&gt;Another common misconception is that AWS administrators can retrieve user passwords.&lt;/p&gt;

&lt;p&gt;They cannot.&lt;/p&gt;

&lt;p&gt;Administrators can perform management operations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating users&lt;/li&gt;
&lt;li&gt;Disabling users&lt;/li&gt;
&lt;li&gt;Resetting passwords&lt;/li&gt;
&lt;li&gt;Viewing permitted user attributes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But neither plaintext passwords nor password hashes are exposed.&lt;/p&gt;

&lt;p&gt;Those remain protected inside Cognito.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Use Cognito?
&lt;/h1&gt;

&lt;p&gt;Beyond simplifying development, Cognito also provides several security advantages.&lt;/p&gt;

&lt;p&gt;It offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure password storage&lt;/li&gt;
&lt;li&gt;Built-in password policies&lt;/li&gt;
&lt;li&gt;Multi-Factor Authentication (MFA)&lt;/li&gt;
&lt;li&gt;Email verification&lt;/li&gt;
&lt;li&gt;Secure JWT generation&lt;/li&gt;
&lt;li&gt;Refresh token management&lt;/li&gt;
&lt;li&gt;Reduced attack surface&lt;/li&gt;
&lt;li&gt;Standards-based authentication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than building these capabilities from scratch, applications can rely on a managed service designed specifically for identity management.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Before learning AWS Cognito, I viewed authentication mainly as checking usernames and passwords.&lt;/p&gt;

&lt;p&gt;Working through Cognito changed that perspective.&lt;/p&gt;

&lt;p&gt;Authentication is much more than credential verification—it involves securely managing identities, issuing trusted tokens, protecting user credentials, and separating authentication concerns from application logic.&lt;/p&gt;

&lt;p&gt;Perhaps the biggest takeaway was realizing that good authentication systems don't simply authenticate users.&lt;/p&gt;

&lt;p&gt;They minimize how much sensitive information an application is responsible for handling in the first place.&lt;/p&gt;

&lt;p&gt;That separation is one of the strongest security advantages of using a managed identity service like AWS Cognito.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>authentication</category>
      <category>cognito</category>
    </item>
    <item>
      <title>From Local Development to a Production-Style Deployment: Understanding Docker, Nginx, Linux, and AWS</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Fri, 03 Jul 2026 09:38:25 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/from-local-development-to-a-production-style-deployment-understanding-docker-nginx-linux-and-aws-1e27</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/from-local-development-to-a-production-style-deployment-understanding-docker-nginx-linux-and-aws-1e27</guid>
      <description>&lt;p&gt;When I first started learning cloud computing, I assumed AWS was simply a collection of services like Amazon S3, EC2, Lambda, and DynamoDB.&lt;/p&gt;

&lt;p&gt;Over time, I realized that cloud computing is much less about memorizing services and much more about understanding how distributed applications are deployed, communicate, and operate in production.&lt;/p&gt;

&lt;p&gt;To better understand these concepts without incurring cloud costs, I built a document processing pipeline locally using &lt;strong&gt;FastAPI, Docker, Docker Compose, Floci (LocalStack), Amazon S3, Amazon SQS, DynamoDB, and Nginx&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The objective wasn't to reproduce AWS perfectly—it was to understand the architectural principles that remain the same whether an application runs on a laptop or inside an EC2 instance.&lt;/p&gt;

&lt;p&gt;The complete project is available here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/micheal000010000-hub/aws-document-processing-pipeline/tree/release/v7.0" rel="noopener noreferrer"&gt;https://github.com/micheal000010000-hub/aws-document-processing-pipeline/tree/release/v7.0&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Looking Beyond Individual AWS Services
&lt;/h1&gt;

&lt;p&gt;One realization stood out while working on this project.&lt;/p&gt;

&lt;p&gt;Learning individual services is useful, but understanding &lt;strong&gt;how they collaborate&lt;/strong&gt; is far more valuable.&lt;/p&gt;

&lt;p&gt;In production, applications are rarely a single process.&lt;/p&gt;

&lt;p&gt;Instead, they consist of multiple independent components, each responsible for a specific task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web servers&lt;/li&gt;
&lt;li&gt;Application servers&lt;/li&gt;
&lt;li&gt;Object storage&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;Message queues&lt;/li&gt;
&lt;li&gt;Background workers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding how these components communicate is what transforms cloud services from isolated tools into a complete system.&lt;/p&gt;




&lt;h1&gt;
  
  
  Thinking About the Internet
&lt;/h1&gt;

&lt;p&gt;The Internet allows computers located anywhere in the world to communicate using the TCP/IP protocol suite.&lt;/p&gt;

&lt;p&gt;Now imagine that instead of communicating with another personal computer, your browser is communicating with a Linux server running continuously inside a cloud provider's data center.&lt;/p&gt;

&lt;p&gt;That machine has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU&lt;/li&gt;
&lt;li&gt;Memory&lt;/li&gt;
&lt;li&gt;Storage&lt;/li&gt;
&lt;li&gt;Network connectivity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and behaves just like any other Linux computer.&lt;/p&gt;

&lt;p&gt;An Amazon EC2 instance is essentially one such virtual Linux machine.&lt;/p&gt;

&lt;p&gt;Deploying an application simply means copying your application onto that server and running it.&lt;/p&gt;




&lt;h1&gt;
  
  
  From Domain Name to Server
&lt;/h1&gt;

&lt;p&gt;Suppose a user visits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before any application receives the request, the browser performs a DNS lookup.&lt;/p&gt;

&lt;p&gt;DNS translates a human-readable domain name into a public IP address.&lt;/p&gt;

&lt;p&gt;Once the IP address is known, the browser establishes a connection with the destination server.&lt;/p&gt;

&lt;p&gt;This entire process happens before the application itself becomes involved.&lt;/p&gt;




&lt;h1&gt;
  
  
  IP Addresses, MAC Addresses, and NAT
&lt;/h1&gt;

&lt;p&gt;Every network packet contains important addressing information.&lt;/p&gt;

&lt;p&gt;At the network layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source IP Address&lt;/li&gt;
&lt;li&gt;Destination IP Address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the data-link layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source MAC Address&lt;/li&gt;
&lt;li&gt;Destination MAC Address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One concept that became much clearer while studying networking was that MAC addresses only exist within a local network.&lt;/p&gt;

&lt;p&gt;Packets travelling across the Internet never keep the same MAC address.&lt;/p&gt;

&lt;p&gt;Each router forwards the packet by replacing the Layer 2 addressing information while preserving the end-to-end IP addresses.&lt;/p&gt;

&lt;p&gt;Similarly, home routers perform Network Address Translation (NAT), replacing private addresses with a public address before forwarding traffic onto the Internet.&lt;/p&gt;

&lt;p&gt;Understanding these concepts made cloud networking feel much less mysterious.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Ports Exist
&lt;/h1&gt;

&lt;p&gt;Knowing the destination IP address isn't enough.&lt;/p&gt;

&lt;p&gt;A Linux server may be running many different applications simultaneously.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="m"&gt;22&lt;/span&gt;   → &lt;span class="n"&gt;SSH&lt;/span&gt;
&lt;span class="m"&gt;80&lt;/span&gt;   → &lt;span class="n"&gt;HTTP&lt;/span&gt;
&lt;span class="m"&gt;443&lt;/span&gt;  → &lt;span class="n"&gt;HTTPS&lt;/span&gt;
&lt;span class="m"&gt;5432&lt;/span&gt; → &lt;span class="n"&gt;PostgreSQL&lt;/span&gt;
&lt;span class="m"&gt;6379&lt;/span&gt; → &lt;span class="n"&gt;Redis&lt;/span&gt;
&lt;span class="m"&gt;8000&lt;/span&gt; → &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every process listens on a specific port.&lt;/p&gt;

&lt;p&gt;When packets arrive, the Linux kernel examines the destination port and forwards the request to the appropriate application.&lt;/p&gt;

&lt;p&gt;This explains why URLs such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;explicitly communicate with a FastAPI application running on port 8000.&lt;/p&gt;




&lt;h1&gt;
  
  
  Local Development
&lt;/h1&gt;

&lt;p&gt;During development, frontend and backend applications often run independently.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend
↓

localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Backend
↓

localhost:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup is convenient because developers can work on each application separately.&lt;/p&gt;

&lt;p&gt;However, exposing multiple ports directly to users becomes impractical in production.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Production Looks Different
&lt;/h1&gt;

&lt;p&gt;Imagine asking users to remember:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;:&lt;span class="m"&gt;3000&lt;/span&gt;
&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;:&lt;span class="m"&gt;8000&lt;/span&gt;
&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;:&lt;span class="m"&gt;9090&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for different services.&lt;/p&gt;

&lt;p&gt;That quickly becomes difficult to manage.&lt;/p&gt;

&lt;p&gt;Instead, production systems expose a single public entry point.&lt;/p&gt;

&lt;p&gt;This is where reverse proxies become important.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding Nginx
&lt;/h1&gt;

&lt;p&gt;One of the concepts that initially seemed confusing was the idea of a reverse proxy.&lt;/p&gt;

&lt;p&gt;Eventually, it became much simpler after realizing that Nginx is simply another Linux process listening on ports 80 and 443.&lt;/p&gt;

&lt;p&gt;Rather than exposing every application individually, only Nginx is exposed.&lt;/p&gt;

&lt;p&gt;It receives incoming requests and forwards them internally to the correct application.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
     │
     ▼
Nginx
     │
     ├────────► Frontend
     │
     └────────► FastAPI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser communicates only with Nginx.&lt;/p&gt;

&lt;p&gt;The backend applications remain hidden from direct Internet access.&lt;/p&gt;

&lt;p&gt;Besides routing requests, Nginx also centralizes configuration, improves security, and simplifies deployment.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding HTTPS
&lt;/h1&gt;

&lt;p&gt;HTTPS initially sounded like a completely different protocol.&lt;/p&gt;

&lt;p&gt;In reality, it is simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP + TLS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Certificate Authority (CA) issues a certificate for a domain after verifying ownership.&lt;/p&gt;

&lt;p&gt;That certificate is configured inside Nginx.&lt;/p&gt;

&lt;p&gt;When a browser connects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A TLS handshake occurs.&lt;/li&gt;
&lt;li&gt;The certificate is validated.&lt;/li&gt;
&lt;li&gt;Encryption keys are negotiated.&lt;/li&gt;
&lt;li&gt;HTTP communication becomes encrypted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An interesting observation is that the FastAPI application itself remains unchanged.&lt;/p&gt;

&lt;p&gt;Nginx performs TLS termination and forwards ordinary HTTP requests to the backend.&lt;/p&gt;

&lt;p&gt;This separation allows backend services to focus entirely on application logic.&lt;/p&gt;




&lt;h1&gt;
  
  
  Building the Architecture Locally
&lt;/h1&gt;

&lt;p&gt;Although the application currently runs on a local Linux machine, its architecture closely resembles a production deployment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
     │
     ▼
Nginx
     │
     ▼
FastAPI
     │
     ├────────► Amazon S3
     ├────────► Amazon SQS
     │                 │
     │                 ▼
     │            Background Worker
     │                 │
     ▼                 ▼
           Amazon DynamoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Floci made it possible to experiment with AWS-compatible services without requiring an active AWS account.&lt;/p&gt;

&lt;p&gt;The same architectural principles remain applicable when deploying to EC2.&lt;/p&gt;




&lt;h1&gt;
  
  
  Lessons Learned
&lt;/h1&gt;

&lt;p&gt;Working through this project connected many concepts that previously felt unrelated.&lt;/p&gt;

&lt;p&gt;Some of the most valuable lessons included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding Docker and Docker Compose.&lt;/li&gt;
&lt;li&gt;Building a multi-container application.&lt;/li&gt;
&lt;li&gt;Using AWS-compatible services locally through Floci.&lt;/li&gt;
&lt;li&gt;Learning event-driven architecture with Amazon SQS.&lt;/li&gt;
&lt;li&gt;Separating file storage from metadata storage.&lt;/li&gt;
&lt;li&gt;Running background workers independently from the API.&lt;/li&gt;
&lt;li&gt;Understanding Linux processes and ports.&lt;/li&gt;
&lt;li&gt;Connecting DNS, TCP/IP, NAT, and reverse proxies into one mental model.&lt;/li&gt;
&lt;li&gt;Understanding HTTPS and TLS termination.&lt;/li&gt;
&lt;li&gt;Organizing applications using production-style architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perhaps the biggest realization was this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cloud computing is not just about learning cloud services. It is about understanding the systems that connect them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once those architectural principles become clear, moving the same application from a local Linux machine to an EC2 instance becomes largely a deployment exercise rather than a redesign.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;This project fundamentally changed the way I think about cloud computing.&lt;/p&gt;

&lt;p&gt;Instead of seeing AWS as a catalog of independent services, I now see it as an ecosystem where networking, Linux, containers, storage, messaging, databases, and application servers collaborate to build scalable systems.&lt;/p&gt;

&lt;p&gt;Learning these architectural principles has been far more valuable than memorizing individual services, because those principles remain applicable regardless of the cloud provider or deployment environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  GitHub Repository
&lt;/h2&gt;

&lt;p&gt;The complete project is available here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/micheal000010000-hub/aws-document-processing-pipeline/tree/release/v7.0" rel="noopener noreferrer"&gt;https://github.com/micheal000010000-hub/aws-document-processing-pipeline/tree/release/v7.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback and suggestions are always welcome.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>nginx</category>
      <category>cloud</category>
      <category>networking</category>
    </item>
    <item>
      <title>From a Simple File Upload API to an Event-Driven AWS Document Processing Pipeline</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Tue, 30 Jun 2026 15:02:17 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/from-a-simple-file-upload-api-to-an-event-driven-aws-document-processing-pipeline-4i5m</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/from-a-simple-file-upload-api-to-an-event-driven-aws-document-processing-pipeline-4i5m</guid>
      <description>&lt;p&gt;When I first started learning AWS, I assumed cloud computing was mostly about understanding individual services like Amazon S3, DynamoDB, Lambda, or EC2.&lt;/p&gt;

&lt;p&gt;After spending time building a document processing pipeline locally, I realized something different.&lt;/p&gt;

&lt;p&gt;Cloud computing isn't simply a collection of services—it's about how those services collaborate to solve real engineering problems.&lt;/p&gt;

&lt;p&gt;Rather than learning each service in isolation, I gradually evolved a small FastAPI application into an event-driven, containerized backend. Along the way, I explored concepts like object storage, asynchronous processing, infrastructure automation, containerization, and continuous integration.&lt;/p&gt;

&lt;p&gt;This article summarizes that journey and, more importantly, the architectural lessons learned along the way.&lt;/p&gt;




&lt;h1&gt;
  
  
  Starting with a Simple REST API
&lt;/h1&gt;

&lt;p&gt;The project began with a straightforward goal: accept document uploads through a REST API.&lt;/p&gt;

&lt;p&gt;The initial architecture looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   │
   ▼
FastAPI
   │
   ▼
Local Storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uploaded files were simply written to a folder on the local machine.&lt;/p&gt;

&lt;p&gt;While functional, this design tightly coupled the application to the local filesystem. The application worked only because it was running on my laptop.&lt;/p&gt;




&lt;h1&gt;
  
  
  Moving from Local Storage to Object Storage
&lt;/h1&gt;

&lt;p&gt;The first architectural improvement was replacing local storage with Amazon S3.&lt;/p&gt;

&lt;p&gt;Instead of keeping uploaded files inside the application directory, they were stored in an object storage service.&lt;/p&gt;

&lt;p&gt;The architecture became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   │
   ▼
FastAPI
   │
   ▼
Amazon S3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This introduced one of the first important cloud concepts:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Application servers shouldn't permanently own user files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Object storage provides durability, scalability, and independence from the application itself.&lt;/p&gt;




&lt;h1&gt;
  
  
  Learning AWS Without an AWS Account
&lt;/h1&gt;

&lt;p&gt;Rather than using a paid AWS account, I used &lt;strong&gt;Floci&lt;/strong&gt;, an open-source AWS emulator built on top of LocalStack.&lt;/p&gt;

&lt;p&gt;The architecture looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FastAPI
    │
    ▼
localhost:4566
    │
    ▼
Floci
    │
    ├── S3
    ├── DynamoDB
    └── SQS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application interacted with Floci using the official AWS SDK (&lt;code&gt;boto3&lt;/code&gt;), making the experience very similar to working with real AWS services.&lt;/p&gt;

&lt;p&gt;This made it possible to experiment with cloud concepts locally without worrying about cloud costs.&lt;/p&gt;




&lt;h1&gt;
  
  
  Using boto3 Instead of Raw HTTP Requests
&lt;/h1&gt;

&lt;p&gt;Applications rarely communicate with AWS services by constructing HTTP requests manually.&lt;/p&gt;

&lt;p&gt;Instead, AWS provides Software Development Kits (SDKs).&lt;/p&gt;

&lt;p&gt;In Python, this is &lt;code&gt;boto3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A simple call like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;hides a significant amount of complexity.&lt;/p&gt;

&lt;p&gt;The SDK handles authentication, request formatting, retries, and communication with AWS-compatible APIs, allowing developers to focus on application logic rather than protocol details.&lt;/p&gt;




&lt;h1&gt;
  
  
  Making Infrastructure Self-Initializing
&lt;/h1&gt;

&lt;p&gt;Initially, the application assumed that the required S3 bucket already existed.&lt;/p&gt;

&lt;p&gt;That meant manually creating resources before starting the application.&lt;/p&gt;

&lt;p&gt;Instead, startup logic was introduced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application Starts
        │
        ▼
Check Bucket
        │
 ┌──────┴──────┐
 │             │
 ▼             ▼
Exists     Create Bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This small improvement made the application much easier to run in a fresh environment and reduced manual setup.&lt;/p&gt;




&lt;h1&gt;
  
  
  Separating Files from Metadata
&lt;/h1&gt;

&lt;p&gt;Uploading files solved only part of the problem.&lt;/p&gt;

&lt;p&gt;Information about each uploaded document—such as filename, upload time, size, and a unique identifier—also needed to be stored.&lt;/p&gt;

&lt;p&gt;Instead of embedding this information within the files themselves, metadata was stored separately in Amazon DynamoDB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S3
 │
 ▼
Document Files

DynamoDB
 │
 ▼
Document Metadata
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separating binary data from structured metadata is a common design pattern in cloud-native applications.&lt;/p&gt;




&lt;h1&gt;
  
  
  Introducing Event-Driven Architecture
&lt;/h1&gt;

&lt;p&gt;Initially, the upload request handled every operation synchronously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upload File
      │
      ▼
Store Metadata
      │
      ▼
Return Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This meant users had to wait until every task finished.&lt;/p&gt;

&lt;p&gt;To improve the design, asynchronous processing was introduced using Amazon SQS.&lt;/p&gt;

&lt;p&gt;The workflow became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
      │
      ▼
FastAPI
      │
      ▼
Upload to S3
      │
      ▼
Send Message to SQS
      │
      ▼
Return Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of doing everything immediately, the application now creates a message describing the work that still needs to be done.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Queues Matter
&lt;/h1&gt;

&lt;p&gt;Queues become especially valuable when traffic increases.&lt;/p&gt;

&lt;p&gt;Imagine thousands of users uploading documents simultaneously.&lt;/p&gt;

&lt;p&gt;Without a queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requests
    │
    ▼
Application
    │
    ▼
Overloaded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requests
    │
    ▼
SQS Queue
    │
    ▼
Background Workers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The queue acts as a buffer, smoothing sudden spikes in traffic and allowing work to be processed at a sustainable pace.&lt;/p&gt;




&lt;h1&gt;
  
  
  Background Workers
&lt;/h1&gt;

&lt;p&gt;A dedicated worker continuously monitors the queue.&lt;/p&gt;

&lt;p&gt;Its responsibility is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Receive Message
      │
      ▼
Process
      │
      ▼
Store Metadata
      │
      ▼
Delete Message
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separating background processing from the API keeps responsibilities clear and allows each component to scale independently.&lt;/p&gt;




&lt;h1&gt;
  
  
  Exploring Serverless Computing
&lt;/h1&gt;

&lt;p&gt;I also experimented with AWS Lambda.&lt;/p&gt;

&lt;p&gt;Instead of running workers continuously, Lambda executes code only when an event occurs.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event
   │
   ▼
Lambda
   │
   ▼
Execute
   │
   ▼
Terminate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This introduced the idea of serverless computing, where compute resources exist only while work is being performed.&lt;/p&gt;




&lt;h1&gt;
  
  
  Containerizing the Application
&lt;/h1&gt;

&lt;p&gt;As the project grew, another challenge appeared.&lt;/p&gt;

&lt;p&gt;How could another machine run the application without manually installing Python, dependencies, or configuring the environment?&lt;/p&gt;

&lt;p&gt;Docker solved this problem.&lt;/p&gt;

&lt;p&gt;A Docker image packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Dependencies&lt;/li&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;into a single portable artifact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source Code
      │
      ▼
Docker Build
      │
      ▼
Docker Image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A container is simply a running instance of that image.&lt;/p&gt;




&lt;h1&gt;
  
  
  Managing Multiple Services with Docker Compose
&lt;/h1&gt;

&lt;p&gt;Eventually, the project consisted of several independent services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FastAPI&lt;/li&gt;
&lt;li&gt;Background Worker&lt;/li&gt;
&lt;li&gt;Floci&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of starting each one manually, Docker Compose orchestrated the entire environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Docker Compose
      │
 ┌────┼────┐
 ▼    ▼    ▼
API Worker Floci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This made local development significantly more reproducible.&lt;/p&gt;




&lt;h1&gt;
  
  
  Automating Builds with GitHub Actions
&lt;/h1&gt;

&lt;p&gt;Running the application locally wasn't enough.&lt;/p&gt;

&lt;p&gt;Every code change should also be verified automatically.&lt;/p&gt;

&lt;p&gt;GitHub Actions introduced a simple CI pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Push Code
     │
     ▼
GitHub Actions
     │
     ▼
Install Dependencies
     │
     ▼
Build Docker Image
     │
     ▼
Report Status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Automation helps catch problems earlier and creates confidence that the project remains buildable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Sharing Images Through Docker Hub
&lt;/h1&gt;

&lt;p&gt;Docker images initially existed only on one machine.&lt;/p&gt;

&lt;p&gt;Publishing them to Docker Hub changed that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local Build
     │
     ▼
Docker Hub
     │
     ▼
Any Machine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once uploaded, the same image can be pulled and executed anywhere without rebuilding.&lt;/p&gt;

&lt;p&gt;This is the essence of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build once, run anywhere.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Looking Ahead to Deployment
&lt;/h1&gt;

&lt;p&gt;The next natural step is deployment.&lt;/p&gt;

&lt;p&gt;Instead of running the application on a personal laptop, the same Docker image can be deployed to an Amazon EC2 instance.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub
     │
     ▼
GitHub Actions
     │
     ▼
Docker Hub
     │
     ▼
EC2 Instance
     │
     ▼
Docker Compose
     │
 ┌───┼────┐
 ▼   ▼    ▼
API Worker Floci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloud deployment becomes much simpler because the application is already packaged as a container.&lt;/p&gt;




&lt;h1&gt;
  
  
  Key Lessons Learned
&lt;/h1&gt;

&lt;p&gt;This project reinforced several important ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud computing is about systems, not isolated services.&lt;/li&gt;
&lt;li&gt;Object storage and metadata storage solve different problems.&lt;/li&gt;
&lt;li&gt;Event-driven architectures improve scalability and responsiveness.&lt;/li&gt;
&lt;li&gt;Queues decouple producers from consumers.&lt;/li&gt;
&lt;li&gt;Background workers allow long-running tasks to happen asynchronously.&lt;/li&gt;
&lt;li&gt;Containers provide consistent execution environments.&lt;/li&gt;
&lt;li&gt;Continuous Integration improves software quality.&lt;/li&gt;
&lt;li&gt;Good software engineering principles matter just as much as cloud knowledge.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Looking back, the biggest takeaway wasn't learning Amazon S3, DynamoDB, SQS, Docker, or GitHub Actions individually.&lt;/p&gt;

&lt;p&gt;It was understanding how each component contributes a single responsibility within a larger system.&lt;/p&gt;

&lt;p&gt;Cloud applications become easier to extend, maintain, and scale when responsibilities are clearly separated and services communicate through well-defined interfaces.&lt;/p&gt;

&lt;p&gt;Building this project transformed cloud computing from a list of services into a connected ecosystem of architectural patterns—and that has been one of the most valuable lessons in my learning journey.&lt;/p&gt;




&lt;h2&gt;
  
  
  GitHub Repository
&lt;/h2&gt;

&lt;p&gt;The complete project is available here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/micheal000010000-hub/aws-document-processing-pipeline/tree/release/v6.0" rel="noopener noreferrer"&gt;https://github.com/micheal000010000-hub/aws-document-processing-pipeline/tree/release/v6.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback and suggestions are always welcome.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>docker</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Building a Cloud Application Locally: Lessons in Backend Architecture and AWS</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Thu, 25 Jun 2026 13:07:32 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/building-a-cloud-application-locally-lessons-in-backend-architecture-and-aws-501</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/building-a-cloud-application-locally-lessons-in-backend-architecture-and-aws-501</guid>
      <description>&lt;p&gt;When learning cloud computing, it's tempting to jump straight into individual services like Amazon S3, DynamoDB, or Lambda. While understanding each service is important, I found that the bigger lesson wasn't about the services themselves—it was about &lt;strong&gt;how applications are designed to evolve over time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To explore this, I started building a document processing backend locally using &lt;strong&gt;FastAPI&lt;/strong&gt;, &lt;strong&gt;Floci&lt;/strong&gt; (an open-source AWS emulator), &lt;strong&gt;Docker&lt;/strong&gt;, and &lt;strong&gt;boto3&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What began as a simple file upload endpoint gradually evolved into a small document management backend capable of uploading, listing, downloading, and deleting documents while keeping the application architecture modular.&lt;/p&gt;

&lt;p&gt;The goal wasn't just to interact with AWS services. It was to understand how good backend design allows applications to grow without requiring major rewrites.&lt;/p&gt;




&lt;h1&gt;
  
  
  Starting Simple
&lt;/h1&gt;

&lt;p&gt;The application originally had a single responsibility:&lt;/p&gt;

&lt;p&gt;Accept a document through an API.&lt;/p&gt;

&lt;p&gt;The initial architecture was straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   │
   ▼
FastAPI
   │
   ▼
uploads/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uploaded files were simply written to a local directory.&lt;/p&gt;

&lt;p&gt;Although this worked, the API endpoint became tightly coupled to the storage implementation.&lt;/p&gt;

&lt;p&gt;Changing the storage mechanism later would require modifying the route itself.&lt;/p&gt;




&lt;h1&gt;
  
  
  Introducing a Service Layer
&lt;/h1&gt;

&lt;p&gt;To reduce that coupling, the storage logic was extracted into a dedicated service.&lt;/p&gt;

&lt;p&gt;The architecture became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   │
   ▼
FastAPI
   │
   ▼
Storage Service
   │
   ▼
Local Storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although the application's behavior remained the same, this introduced an important software engineering principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Separation of Concerns.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The API became responsible for handling HTTP requests.&lt;/p&gt;

&lt;p&gt;The storage service became responsible for managing files.&lt;/p&gt;




&lt;h1&gt;
  
  
  Swapping Local Storage for Amazon S3
&lt;/h1&gt;

&lt;p&gt;Once the storage logic was isolated, replacing the implementation became surprisingly simple.&lt;/p&gt;

&lt;p&gt;Instead of saving files locally, the storage service was updated to use Amazon S3 through the AWS SDK (&lt;code&gt;boto3&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The architecture changed to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   │
   ▼
FastAPI
   │
   ▼
S3 Storage Service
   │
   ▼
Amazon S3 (Floci)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API endpoints themselves didn't need to change.&lt;/p&gt;

&lt;p&gt;Only the storage implementation changed.&lt;/p&gt;

&lt;p&gt;That was one of the biggest takeaways from this project.&lt;/p&gt;




&lt;h1&gt;
  
  
  Making Infrastructure Self-Initializing
&lt;/h1&gt;

&lt;p&gt;Another improvement was avoiding manual infrastructure setup.&lt;/p&gt;

&lt;p&gt;Rather than assuming the S3 bucket already existed, the application now checks for it during startup and creates it if necessary.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application Starts
        │
        ▼
Check Bucket
        │
        ▼
Create If Missing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the application easier to run on a fresh machine and reduces manual setup.&lt;/p&gt;




&lt;h1&gt;
  
  
  Evolving Beyond File Uploads
&lt;/h1&gt;

&lt;p&gt;Initially, the project focused only on uploading files.&lt;/p&gt;

&lt;p&gt;As development progressed, it evolved into a small document management backend.&lt;/p&gt;

&lt;p&gt;The application now supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uploading documents&lt;/li&gt;
&lt;li&gt;Listing stored documents&lt;/li&gt;
&lt;li&gt;Downloading documents&lt;/li&gt;
&lt;li&gt;Deleting documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current API exposes endpoints such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST /upload&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /documents&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /download/{filename}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE /documents/{filename}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One realization stood out during this stage:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Good architecture doesn't eliminate future changes—it makes future changes easier to implement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because storage logic was already isolated behind a service layer, adding new endpoints required very little modification to the existing code.&lt;/p&gt;




&lt;h1&gt;
  
  
  Adding DynamoDB
&lt;/h1&gt;

&lt;p&gt;Managing files solved only part of the problem.&lt;/p&gt;

&lt;p&gt;Applications also need to manage information &lt;em&gt;about&lt;/em&gt; those files.&lt;/p&gt;

&lt;p&gt;To prepare for that, a dedicated DynamoDB service was introduced.&lt;/p&gt;

&lt;p&gt;Its responsibilities include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generating unique document identifiers&lt;/li&gt;
&lt;li&gt;Recording upload timestamps&lt;/li&gt;
&lt;li&gt;Managing document metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Client
                    │
        ┌───────────┴───────────┐
        ▼                       ▼
 Upload / Download        List / Delete
        │                       │
        └───────────┬───────────┘
                    ▼
                FastAPI
            ┌───────┴────────┐
            ▼                ▼
      S3 Storage        DynamoDB Service
            ▼                ▼
      Amazon S3       Amazon DynamoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the moment, document storage is fully functional, while the DynamoDB service provides the foundation for future metadata management.&lt;/p&gt;




&lt;h1&gt;
  
  
  More Than Learning AWS
&lt;/h1&gt;

&lt;p&gt;Although the project uses services like Amazon S3 and DynamoDB, the most valuable lessons weren't AWS-specific.&lt;/p&gt;

&lt;p&gt;It reinforced several software engineering concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separation of Concerns&lt;/li&gt;
&lt;li&gt;Service Layer Pattern&lt;/li&gt;
&lt;li&gt;Dependency Isolation&lt;/li&gt;
&lt;li&gt;Infrastructure Initialization&lt;/li&gt;
&lt;li&gt;Modular Backend Design&lt;/li&gt;
&lt;li&gt;Storage Abstraction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These ideas apply regardless of whether the backend eventually uses Amazon S3, Azure Blob Storage, Google Cloud Storage, or even a local filesystem.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Bigger Lesson
&lt;/h1&gt;

&lt;p&gt;One realization stood out throughout this project:&lt;/p&gt;

&lt;p&gt;Cloud engineering isn't just about learning cloud services.&lt;/p&gt;

&lt;p&gt;It's about designing applications that can evolve as requirements change.&lt;/p&gt;

&lt;p&gt;The project started as a simple upload endpoint.&lt;/p&gt;

&lt;p&gt;Over time it gained support for listing, downloading, and deleting documents without requiring a redesign of the application.&lt;/p&gt;

&lt;p&gt;That flexibility came from separating responsibilities early rather than tightly coupling implementation details together.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local Storage
        │
        ▼
Amazon S3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API doesn't need to know which implementation is being used.&lt;/p&gt;

&lt;p&gt;As long as the interface remains consistent, the underlying storage mechanism can evolve independently.&lt;/p&gt;

&lt;p&gt;That flexibility is what makes production systems easier to extend, test, and maintain.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Building applications has been one of the most effective ways for me to learn cloud engineering.&lt;/p&gt;

&lt;p&gt;Working through real architectural decisions made concepts like Amazon S3, DynamoDB, and the AWS SDK feel much more intuitive than simply reading documentation.&lt;/p&gt;

&lt;p&gt;More importantly, this project reinforced that good cloud applications are built not just on cloud services, but on sound software engineering principles.&lt;/p&gt;

&lt;p&gt;The cloud services may change over time.&lt;/p&gt;

&lt;p&gt;A well-designed architecture makes those changes far less painful.&lt;/p&gt;




&lt;h2&gt;
  
  
  GitHub Repository
&lt;/h2&gt;

&lt;p&gt;The complete project is available here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/micheal000010000-hub/aws-document-processing-pipeline/tree/Document_Processing_Pipeline" rel="noopener noreferrer"&gt;https://github.com/micheal000010000-hub/aws-document-processing-pipeline/tree/Document_Processing_Pipeline&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback, suggestions, and contributions are always welcome.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>backend</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Learning AWS by Building a Local Document Processing Pipeline (Without an AWS Account)</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Thu, 25 Jun 2026 08:23:48 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/learning-aws-by-building-a-local-document-processing-pipeline-without-an-aws-account-4k25</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/learning-aws-by-building-a-local-document-processing-pipeline-without-an-aws-account-4k25</guid>
      <description>&lt;p&gt;Cloud computing often feels difficult to learn because many tutorials focus on individual services in isolation.&lt;/p&gt;

&lt;p&gt;You create an S3 bucket in one tutorial, invoke a Lambda function in another, and experiment with DynamoDB somewhere else. While each service makes sense individually, it can still be hard to understand how they work together in a real application.&lt;/p&gt;

&lt;p&gt;Instead of learning services one by one, I wanted to build something that connected them together.&lt;/p&gt;

&lt;p&gt;Even better, I wanted to do it &lt;strong&gt;without creating an AWS account or worrying about cloud costs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;Floci&lt;/strong&gt;, an open-source AWS emulator, came in.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Goal
&lt;/h1&gt;

&lt;p&gt;The objective wasn't to recreate AWS perfectly.&lt;/p&gt;

&lt;p&gt;It was to understand the interaction between services by building a simple document processing pipeline.&lt;/p&gt;

&lt;p&gt;The architecture looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
   │
   ▼
Upload Document
   │
   ▼
Amazon S3
   │
   ▼
AWS Lambda
   │
Extract Metadata
   │
   ▼
Amazon DynamoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although the final automatic Lambda → DynamoDB write couldn't be completed due to a networking limitation inside Floci, the overall architecture mirrors how the same workflow would be built on AWS.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Learn AWS Locally?
&lt;/h1&gt;

&lt;p&gt;Running AWS services locally offers several advantages while learning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No cloud costs&lt;/li&gt;
&lt;li&gt;Safe experimentation&lt;/li&gt;
&lt;li&gt;Fast iteration&lt;/li&gt;
&lt;li&gt;Ability to inspect every component&lt;/li&gt;
&lt;li&gt;Easy debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the AWS CLI against a local endpoint also helped reinforce an important idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The AWS CLI is simply a client that sends API requests. Whether those requests go to Amazon's cloud or a local emulator depends on the configured endpoint.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  What Each Service Taught Me
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Amazon S3
&lt;/h2&gt;

&lt;p&gt;The first service I explored was Amazon S3.&lt;/p&gt;

&lt;p&gt;Rather than thinking of S3 as "cloud storage," it became much easier to understand it as &lt;strong&gt;object storage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A bucket acts as a container, while every uploaded file is stored as an object.&lt;/p&gt;

&lt;p&gt;Practical exercises included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating buckets&lt;/li&gt;
&lt;li&gt;Uploading files&lt;/li&gt;
&lt;li&gt;Listing bucket contents&lt;/li&gt;
&lt;li&gt;Downloading objects&lt;/li&gt;
&lt;li&gt;Deleting objects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These simple operations clarified how applications persist documents before any further processing occurs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Amazon DynamoDB
&lt;/h2&gt;

&lt;p&gt;Once files could be stored, the next step was understanding structured data.&lt;/p&gt;

&lt;p&gt;Unlike S3, DynamoDB doesn't store files—it stores records.&lt;/p&gt;

&lt;p&gt;Creating tables, inserting items, retrieving data, and scanning tables helped reinforce the difference between object storage and NoSQL databases.&lt;/p&gt;

&lt;p&gt;Instead of storing the document itself, DynamoDB became the place to store information &lt;em&gt;about&lt;/em&gt; the document.&lt;/p&gt;




&lt;h2&gt;
  
  
  AWS Lambda
&lt;/h2&gt;

&lt;p&gt;Lambda introduced a completely different mindset.&lt;/p&gt;

&lt;p&gt;Instead of managing servers, code is packaged and uploaded as a deployment artifact.&lt;/p&gt;

&lt;p&gt;The Lambda function processed uploaded documents and generated metadata such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Document ID&lt;/li&gt;
&lt;li&gt;Filename&lt;/li&gt;
&lt;li&gt;File size&lt;/li&gt;
&lt;li&gt;Upload timestamp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was also where I encountered some of the most interesting debugging challenges.&lt;/p&gt;




&lt;h1&gt;
  
  
  Debugging Was the Real Teacher
&lt;/h1&gt;

&lt;p&gt;Building the project wasn't just about writing code.&lt;/p&gt;

&lt;p&gt;It involved understanding how different environments interact.&lt;/p&gt;

&lt;p&gt;Some issues I encountered included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing AWS CLI inside the Lambda runtime&lt;/li&gt;
&lt;li&gt;Updating deployment packages correctly&lt;/li&gt;
&lt;li&gt;Lambda timeout while communicating with DynamoDB&lt;/li&gt;
&lt;li&gt;Docker networking behaviour inside Floci&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each issue forced me to understand the difference between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My Linux machine&lt;/li&gt;
&lt;li&gt;Docker containers&lt;/li&gt;
&lt;li&gt;Lambda runtime environments&lt;/li&gt;
&lt;li&gt;AWS SDK (&lt;code&gt;boto3&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;AWS CLI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those distinctions aren't always obvious from documentation alone, but debugging made them much clearer.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding IAM
&lt;/h1&gt;

&lt;p&gt;IAM was another concept that became easier through practice.&lt;/p&gt;

&lt;p&gt;Rather than viewing it as just another AWS service, I started thinking of IAM as the system that answers three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Who is making the request?&lt;/li&gt;
&lt;li&gt;What action is being performed?&lt;/li&gt;
&lt;li&gt;Is that action allowed?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Learning about users, groups, policies, and roles also clarified why Lambda functions execute with an IAM role instead of inheriting permissions automatically.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Bigger Picture
&lt;/h1&gt;

&lt;p&gt;One realization stood out throughout the project:&lt;/p&gt;

&lt;p&gt;AWS services don't communicate because they're "inside AWS."&lt;/p&gt;

&lt;p&gt;They communicate through well-defined APIs.&lt;/p&gt;

&lt;p&gt;Whether the services are running in Amazon's cloud or emulated locally, the interaction model remains largely the same.&lt;/p&gt;

&lt;p&gt;Understanding those interactions felt much more valuable than memorizing individual commands.&lt;/p&gt;




&lt;h1&gt;
  
  
  What This Project Reinforced
&lt;/h1&gt;

&lt;p&gt;Working through this pipeline reinforced several ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building teaches more than reading documentation.&lt;/li&gt;
&lt;li&gt;Debugging is part of learning cloud computing.&lt;/li&gt;
&lt;li&gt;IAM is fundamentally about identities and permissions.&lt;/li&gt;
&lt;li&gt;Lambda runs inside an isolated execution environment.&lt;/li&gt;
&lt;li&gt;Cloud services are loosely coupled and communicate through APIs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Cloud computing can seem overwhelming because of the sheer number of services available.&lt;/p&gt;

&lt;p&gt;Building even a small end-to-end workflow makes those services feel much less abstract.&lt;/p&gt;

&lt;p&gt;By connecting object storage, serverless compute, databases, and identity management into a single project, I gained a much clearer understanding of how these pieces fit together.&lt;/p&gt;

&lt;p&gt;For anyone beginning their cloud journey, building a small pipeline—even locally—can often teach far more than reading documentation alone.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to explore the project or contribute, here's the repository:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/micheal000010000-hub/aws-document-processing-pipeline" rel="noopener noreferrer"&gt;https://github.com/micheal000010000-hub/aws-document-processing-pipeline&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Feedback, suggestions, and contributions are always welcome.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>Learning AWS Without an AWS Account: Running S3, DynamoDB, and Lambda Locally</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Sun, 21 Jun 2026 01:38:07 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/learning-aws-without-an-aws-account-running-s3-dynamodb-and-lambda-locally-27bb</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/learning-aws-without-an-aws-account-running-s3-dynamodb-and-lambda-locally-27bb</guid>
      <description>&lt;h1&gt;
  
  
  Learning AWS Without an AWS Account: Running S3, DynamoDB, and Lambda Locally
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Image: Local AWS architecture using Floci, Docker, and AWS CLI.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a long time, I approached AWS the same way many beginners do.&lt;/p&gt;

&lt;p&gt;I would read documentation, watch tutorials, and try to memorize services.&lt;/p&gt;

&lt;p&gt;The problem was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reading about cloud services is very different from actually using them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At the same time, I didn't want to create resources in a cloud account while learning basic concepts.&lt;/p&gt;

&lt;p&gt;So I started looking for a way to experiment locally.&lt;/p&gt;

&lt;p&gt;That's how I came across &lt;strong&gt;Floci&lt;/strong&gt;, an open-source AWS emulator that exposes AWS-compatible APIs on a local machine.&lt;/p&gt;

&lt;p&gt;The idea is surprisingly simple:&lt;/p&gt;

&lt;p&gt;Instead of sending requests to AWS, send them to a local container that behaves like AWS.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Learn AWS Locally?
&lt;/h2&gt;

&lt;p&gt;When learning cloud concepts, most beginners want to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is S3 really used for?&lt;/li&gt;
&lt;li&gt;How does DynamoDB store data?&lt;/li&gt;
&lt;li&gt;What does Lambda actually execute?&lt;/li&gt;
&lt;li&gt;What role does AWS CLI play?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions are easier to answer by creating resources than by reading definitions.&lt;/p&gt;

&lt;p&gt;Local emulation makes that possible without worrying about cloud costs.&lt;/p&gt;




&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;

&lt;p&gt;The setup looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS CLI
    ↓
localhost:4566
    ↓
Floci Container
    ├── S3 Emulator
    ├── DynamoDB Emulator
    └── Lambda Emulator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of communicating with AWS infrastructure, AWS CLI communicates with Floci running on the local machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpjxnlu1nv0e5iuzqffhj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpjxnlu1nv0e5iuzqffhj.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Running Floci with Docker
&lt;/h2&gt;

&lt;p&gt;The first step was starting the emulator.&lt;/p&gt;

&lt;p&gt;A simple Docker Compose configuration was enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;floci&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;floci/floci:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4566:4566"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Port &lt;code&gt;4566&lt;/code&gt; became the endpoint through which AWS CLI communicated with Floci.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding AWS CLI
&lt;/h2&gt;

&lt;p&gt;AWS CLI stands for:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Web Services Command Line Interface&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It acts as a client that converts commands into AWS API requests.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be viewed conceptually as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS CLI
     ↓
AWS API Request
     ↓
S3 Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Normally those requests go to AWS.&lt;/p&gt;

&lt;p&gt;With Floci, the destination changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells AWS CLI to communicate with the local emulator instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  Learning S3
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Is S3?
&lt;/h3&gt;

&lt;p&gt;S3 is an object storage service.&lt;/p&gt;

&lt;p&gt;A useful mental model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bucket
 ├── resume.pdf
 ├── image.png
 └── hello.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bucket acts as a container.&lt;/p&gt;

&lt;p&gt;Everything stored inside it is called an object.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe5f6w1uf2k0wb45pjizy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe5f6w1uf2k0wb45pjizy.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Creating a Bucket
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 &lt;span class="se"&gt;\&lt;/span&gt;
s3 mb s3://notes-app-bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Uploading a File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello from AWS Learning"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; hello.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 &lt;span class="se"&gt;\&lt;/span&gt;
s3 &lt;span class="nb"&gt;cp &lt;/span&gt;hello.txt s3://notes-app-bucket/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Listing Objects
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 &lt;span class="se"&gt;\&lt;/span&gt;
s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://notes-app-bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Downloading an Object
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 &lt;span class="se"&gt;\&lt;/span&gt;
s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://notes-app-bucket/hello.txt &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Key Insight
&lt;/h3&gt;

&lt;p&gt;S3 stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Videos&lt;/li&gt;
&lt;li&gt;Documents&lt;/li&gt;
&lt;li&gt;Backups&lt;/li&gt;
&lt;li&gt;Application assets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;S3 is not a database.&lt;/p&gt;

&lt;p&gt;It is object storage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Learning DynamoDB
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Is DynamoDB?
&lt;/h3&gt;

&lt;p&gt;DynamoDB is a NoSQL database service.&lt;/p&gt;

&lt;p&gt;Instead of storing files, it stores records.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"noteId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Learning AWS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Today I learned S3"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fes2s2spfkiobf71rc98v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fes2s2spfkiobf71rc98v.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Creating a Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 &lt;span class="se"&gt;\&lt;/span&gt;
dynamodb create-table &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--table-name&lt;/span&gt; Notes &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--attribute-definitions&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;noteId,AttributeType&lt;span class="o"&gt;=&lt;/span&gt;S &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--key-schema&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;noteId,KeyType&lt;span class="o"&gt;=&lt;/span&gt;HASH &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--billing-mode&lt;/span&gt; PAY_PER_REQUEST
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Inserting Data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 &lt;span class="se"&gt;\&lt;/span&gt;
dynamodb put-item &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--table-name&lt;/span&gt; Notes &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--item&lt;/span&gt; &lt;span class="s1"&gt;'{
  "noteId":{"S":"1"},
  "title":{"S":"Learning AWS"},
  "content":{"S":"Today I learned S3 and DynamoDB"}
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Retrieving Data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 &lt;span class="se"&gt;\&lt;/span&gt;
dynamodb get-item &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--table-name&lt;/span&gt; Notes &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--key&lt;/span&gt; &lt;span class="s1"&gt;'{"noteId":{"S":"1"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Key Insight
&lt;/h3&gt;

&lt;p&gt;A useful distinction is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S3
 ↓
Stores Files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;versus&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DynamoDB
 ↓
Stores Records
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding this difference made both services much easier to reason about.&lt;/p&gt;




&lt;h2&gt;
  
  
  Learning Lambda
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Is Lambda?
&lt;/h3&gt;

&lt;p&gt;Lambda is a serverless compute service.&lt;/p&gt;

&lt;p&gt;Unlike S3 or DynamoDB, Lambda does not primarily store data.&lt;/p&gt;

&lt;p&gt;Its job is to execute code.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello from Lambda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;[Insert Figure 4: Lambda Execution Flow Here]&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Packaging the Function
&lt;/h3&gt;

&lt;p&gt;Lambda expects a deployment artifact.&lt;/p&gt;

&lt;p&gt;The simplest option is a ZIP file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zip &lt;span class="k"&gt;function&lt;/span&gt;.zip handler.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ZIP package contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application code&lt;/li&gt;
&lt;li&gt;Supporting files&lt;/li&gt;
&lt;li&gt;Dependencies&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Creating the Function
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 &lt;span class="se"&gt;\&lt;/span&gt;
lambda create-function &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--function-name&lt;/span&gt; HelloLambda &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--runtime&lt;/span&gt; python3.11 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--handler&lt;/span&gt; handler.lambda_handler &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zip-file&lt;/span&gt; fileb://function.zip &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--role&lt;/span&gt; arn:aws:iam::000000000000:role/lambda-role
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  The First Failure
&lt;/h3&gt;

&lt;p&gt;The first invocation failed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failed to start Lambda container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turned out to be a Docker issue.&lt;/p&gt;

&lt;p&gt;Lambda execution inside Floci relies on runtime containers.&lt;/p&gt;

&lt;p&gt;Floci needed access to Docker itself.&lt;/p&gt;




&lt;h3&gt;
  
  
  Fixing the Issue
&lt;/h3&gt;

&lt;p&gt;The solution was mounting Docker's socket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;floci&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;floci/floci:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4566:4566"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once Floci could communicate with Docker, Lambda executed successfully.&lt;/p&gt;




&lt;h3&gt;
  
  
  Invoking Lambda
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 &lt;span class="se"&gt;\&lt;/span&gt;
lambda invoke &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--function-name&lt;/span&gt; HelloLambda &lt;span class="se"&gt;\&lt;/span&gt;
response.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello from Lambda"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  A Brief Note on IAM
&lt;/h2&gt;

&lt;p&gt;One interesting observation was that Lambda creation succeeded even though no real IAM role was created.&lt;/p&gt;

&lt;p&gt;The emulator is intentionally more forgiving than AWS.&lt;/p&gt;

&lt;p&gt;In real AWS, IAM is responsible for controlling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who can access resources&lt;/li&gt;
&lt;li&gt;Which actions are allowed&lt;/li&gt;
&lt;li&gt;Which services can communicate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Lambda execution role acts as the identity under which the function runs.&lt;/p&gt;

&lt;p&gt;Even simple functions require one because AWS needs to know what permissions the function should have if it later interacts with services such as S3 or DynamoDB.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Exercise Taught Me
&lt;/h2&gt;

&lt;p&gt;By the end of the experiment, the following concepts became much clearer:&lt;/p&gt;

&lt;h3&gt;
  
  
  S3
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Creating buckets&lt;/li&gt;
&lt;li&gt;Uploading objects&lt;/li&gt;
&lt;li&gt;Downloading objects&lt;/li&gt;
&lt;li&gt;Listing objects&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DynamoDB
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Creating tables&lt;/li&gt;
&lt;li&gt;Inserting records&lt;/li&gt;
&lt;li&gt;Querying records&lt;/li&gt;
&lt;li&gt;Understanding primary keys&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lambda
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Packaging code&lt;/li&gt;
&lt;li&gt;Deploying functions&lt;/li&gt;
&lt;li&gt;Executing code&lt;/li&gt;
&lt;li&gt;Troubleshooting runtime issues&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Infrastructure Concepts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Docker containers&lt;/li&gt;
&lt;li&gt;Service emulation&lt;/li&gt;
&lt;li&gt;Runtime environments&lt;/li&gt;
&lt;li&gt;Deployment artifacts&lt;/li&gt;
&lt;li&gt;IAM fundamentals&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Mental Model
&lt;/h2&gt;

&lt;p&gt;When I started, S3, DynamoDB, and Lambda felt like unrelated AWS services.&lt;/p&gt;

&lt;p&gt;After running them locally, a much simpler mental model emerged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS CLI
     ↓
Floci
     ├── S3       → Stores Files
     ├── DynamoDB → Stores Records
     └── Lambda   → Runs Code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes the fastest way to understand cloud services isn't reading more documentation.&lt;/p&gt;

&lt;p&gt;It's building a small environment, creating resources, breaking things, fixing them, and observing how the pieces fit together.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>docker</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>From Home Networking to Enterprise Networking: What Changes Behind the Scenes?</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Sat, 20 Jun 2026 07:26:15 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/from-home-networking-to-enterprise-networking-what-changes-behind-the-scenes-1h6l</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/from-home-networking-to-enterprise-networking-what-changes-behind-the-scenes-1h6l</guid>
      <description>&lt;h1&gt;
  
  
  From Home Networking to Enterprise Networking: What Changes Behind the Scenes?
&lt;/h1&gt;

&lt;p&gt;For a long time, networking felt relatively straightforward.&lt;/p&gt;

&lt;p&gt;A device connects to a router, the router connects to the Internet, and traffic eventually reaches its destination.&lt;/p&gt;

&lt;p&gt;A simplified view looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop
   ↓
Router
   ↓
ISP
   ↓
Internet
   ↓
Website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While studying networking, I became curious about how enterprise environments work.&lt;/p&gt;

&lt;p&gt;Corporate devices often have additional security software installed, VPN clients are common, and traffic sometimes appears to follow entirely different paths than it does on a home network.&lt;/p&gt;

&lt;p&gt;This raised an interesting question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What actually changes when networking moves from a home environment to an enterprise environment?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Problem Organizations Are Trying To Solve
&lt;/h2&gt;

&lt;p&gt;Home networks are usually optimized for convenience.&lt;/p&gt;

&lt;p&gt;Enterprise networks have a different set of priorities.&lt;/p&gt;

&lt;p&gt;Organizations need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Protect sensitive information&lt;/li&gt;
&lt;li&gt;Restrict access to certain websites&lt;/li&gt;
&lt;li&gt;Monitor traffic for security threats&lt;/li&gt;
&lt;li&gt;Enforce compliance requirements&lt;/li&gt;
&lt;li&gt;Control how applications communicate&lt;/li&gt;
&lt;li&gt;Route traffic through approved paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simply allowing devices to communicate directly with the Internet is often insufficient.&lt;/p&gt;

&lt;p&gt;Additional security layers are introduced to enforce these requirements.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvipjbbxeuw7suailq5vd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvipjbbxeuw7suailq5vd.png" alt=" " width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is A Secure Web Gateway?
&lt;/h2&gt;

&lt;p&gt;One common component in enterprise environments is a &lt;strong&gt;Secure Web Gateway (SWG)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of traffic flowing directly to the Internet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
   ↓
Internet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;traffic may follow a path like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
   ↓
Security Client
   ↓
Secure Web Gateway
   ↓
Internet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gateway acts as an intermediary.&lt;/p&gt;

&lt;p&gt;Before traffic reaches its destination, the gateway can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apply security policies&lt;/li&gt;
&lt;li&gt;Filter websites&lt;/li&gt;
&lt;li&gt;Inspect requests&lt;/li&gt;
&lt;li&gt;Generate logs&lt;/li&gt;
&lt;li&gt;Enforce compliance requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The user still experiences a normal browsing session, but additional checks occur behind the scenes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Identity Matters More Than MAC Addresses
&lt;/h2&gt;

&lt;p&gt;One misconception many beginners have is that organizations identify users primarily through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MAC addresses&lt;/li&gt;
&lt;li&gt;Local IP addresses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In reality, enterprise environments usually rely on richer forms of identity.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User authentication&lt;/li&gt;
&lt;li&gt;Device certificates&lt;/li&gt;
&lt;li&gt;Security agents&lt;/li&gt;
&lt;li&gt;Corporate identity providers&lt;/li&gt;
&lt;li&gt;Session information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows an organization to distinguish between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An approved corporate device&lt;/li&gt;
&lt;li&gt;A personal device&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;even when both devices are connected to the same home Wi-Fi network.&lt;/p&gt;

&lt;p&gt;The decision is often based on identity and trust rather than simply an IP address.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Traffic Steering
&lt;/h2&gt;

&lt;p&gt;One of the more interesting concepts in enterprise networking is &lt;strong&gt;traffic steering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The basic idea is simple:&lt;/p&gt;

&lt;p&gt;Different types of traffic may follow different paths.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Web Browsing
        ↓
Gateway A

Internal Applications
        ↓
Gateway B

Client Systems
        ↓
Gateway C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The path can be chosen based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User identity&lt;/li&gt;
&lt;li&gt;Application type&lt;/li&gt;
&lt;li&gt;Destination&lt;/li&gt;
&lt;li&gt;Security policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful analogy is an airport.&lt;/p&gt;

&lt;p&gt;Passengers may enter through the same building, but different groups are routed through different checkpoints depending on where they are going.&lt;/p&gt;

&lt;p&gt;Enterprise networks often work in a similar way.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F465zojyh2alezlpe89c1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F465zojyh2alezlpe89c1.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Network Behavior Can Change Over Time
&lt;/h2&gt;

&lt;p&gt;One observation that initially confused me was that network behavior sometimes changes without any visible action from the user.&lt;/p&gt;

&lt;p&gt;A website that was inaccessible one day may suddenly become accessible later.&lt;/p&gt;

&lt;p&gt;This often happens because enterprise security platforms periodically receive updated policies.&lt;/p&gt;

&lt;p&gt;Those policies may modify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access permissions&lt;/li&gt;
&lt;li&gt;Routing behavior&lt;/li&gt;
&lt;li&gt;Security controls&lt;/li&gt;
&lt;li&gt;Gateway selection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a result, the path traffic follows today may not be identical to the path it follows next month.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where VPNs Fit Into The Picture
&lt;/h2&gt;

&lt;p&gt;Before learning more about networking, I assumed traffic always followed a simple path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop
   ↓
Router
   ↓
Internet
   ↓
Server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VPNs introduce an additional layer.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop
   ↓
Encrypted Tunnel
   ↓
VPN Gateway
   ↓
Destination
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The VPN gateway becomes a trusted entry point into an organization's network.&lt;/p&gt;

&lt;p&gt;Instead of communicating directly with internal resources, traffic first reaches the VPN infrastructure.&lt;/p&gt;

&lt;p&gt;From there it is forwarded according to organizational policies.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyhtm2wtsanfye7j4dm3r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyhtm2wtsanfye7j4dm3r.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  One Device, Multiple IP Addresses
&lt;/h2&gt;

&lt;p&gt;Another important concept is that a device may be associated with multiple IP addresses simultaneously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local IP Address
&lt;/h3&gt;

&lt;p&gt;Assigned by the home router.&lt;/p&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.x.x
10.x.x.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Used only inside the local network.&lt;/p&gt;




&lt;h3&gt;
  
  
  Public IP Address
&lt;/h3&gt;

&lt;p&gt;Assigned by the Internet Service Provider.&lt;/p&gt;

&lt;p&gt;This is typically what external websites see.&lt;/p&gt;




&lt;h3&gt;
  
  
  VPN Address
&lt;/h3&gt;

&lt;p&gt;Assigned by the VPN infrastructure.&lt;/p&gt;

&lt;p&gt;When connected to organizational resources, this address may be used instead of the public ISP address.&lt;/p&gt;

&lt;p&gt;Understanding these different layers helps explain why network traffic can appear differently depending on where it is observed.&lt;/p&gt;




&lt;h2&gt;
  
  
  DNS Still Plays A Critical Role
&lt;/h2&gt;

&lt;p&gt;Regardless of whether traffic is flowing through a home network or an enterprise network, DNS remains fundamental.&lt;/p&gt;

&lt;p&gt;Humans prefer names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Computers require IP addresses.&lt;/p&gt;

&lt;p&gt;DNS performs the translation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com
        ↓
IP Address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One useful command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nslookup example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to observe how names are resolved into addresses.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building On Earlier Networking Concepts
&lt;/h2&gt;

&lt;p&gt;While learning networking, I spent a lot of time understanding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ARP&lt;/li&gt;
&lt;li&gt;DHCP&lt;/li&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;li&gt;ICMP&lt;/li&gt;
&lt;li&gt;IP&lt;/li&gt;
&lt;li&gt;MAC Addresses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These protocols solve lower-level networking problems.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;ARP asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I know the IP address. What is the MAC address?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;DHCP asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What IP address should I use?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;DNS asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What IP address corresponds to this domain name?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Enterprise networking introduces a different set of questions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Who is the user?&lt;/p&gt;

&lt;p&gt;Is this device trusted?&lt;/p&gt;

&lt;p&gt;Which policy applies?&lt;/p&gt;

&lt;p&gt;Which gateway should handle this traffic?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The focus shifts from simple connectivity to identity, security, and policy enforcement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;One thing that became clear while learning networking is that enterprise networking is not a completely different world.&lt;/p&gt;

&lt;p&gt;The same fundamentals still exist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IP addresses&lt;/li&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;TCP/IP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What changes is the addition of security, identity, and policy layers on top of those fundamentals.&lt;/p&gt;

&lt;p&gt;The more I studied packet flows and network paths, the easier these concepts became to understand.&lt;/p&gt;

&lt;p&gt;Rather than memorizing protocols individually, it became much more useful to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where is this packet going, and why is it taking that path?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That simple question often reveals how the entire system works.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>devops</category>
      <category>linux</category>
      <category>cloud</category>
    </item>
    <item>
      <title>One Public IP, Many Devices: How Your Router Knows Where Replies Belong</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Sat, 13 Jun 2026 17:26:18 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/one-public-ip-many-devices-how-your-router-knows-where-replies-belong-2o30</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/one-public-ip-many-devices-how-your-router-knows-where-replies-belong-2o30</guid>
      <description>&lt;p&gt;After learning about DHCP, ARP, DNS, TCP, and NAT, I ran into a question that completely changed how I thought about home networking.&lt;/p&gt;

&lt;p&gt;I already understood the basics of NAT.&lt;/p&gt;

&lt;p&gt;My laptop might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while my router has a public address such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;49.43.12.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router replaces the private address with its public address before sending packets to the Internet.&lt;/p&gt;

&lt;p&gt;That part made sense.&lt;/p&gt;

&lt;p&gt;But then a new question appeared.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Imagine a home network with three devices.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop
192.168.1.100

Phone
192.168.1.101

Tablet
192.168.1.102
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three devices are browsing websites simultaneously.&lt;/p&gt;

&lt;p&gt;After NAT, every packet appears to originate from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;49.43.12.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To the Internet, all three devices look identical.&lt;/p&gt;

&lt;p&gt;Which raises an obvious question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When replies come back, how does the router know which device should receive them?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is where ports become extremely important.&lt;/p&gt;




&lt;h2&gt;
  
  
  Think of NAT Like an Apartment Building
&lt;/h2&gt;

&lt;p&gt;Imagine a large apartment building.&lt;/p&gt;

&lt;p&gt;The building has a single address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;49.43.12.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the building are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Apartment 101
Apartment 102
Apartment 103
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The public IP address is the building.&lt;/p&gt;

&lt;p&gt;The ports are the apartment numbers.&lt;/p&gt;

&lt;p&gt;Without apartment numbers, a package arriving at the building could not be delivered to the correct resident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[Insert Figure 1: Apartment Building Analogy Here]&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A Real Example
&lt;/h2&gt;

&lt;p&gt;Suppose we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop:
192.168.1.100

Phone:
192.168.1.101

Router:
49.43.12.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The laptop performs a DNS lookup.&lt;/p&gt;

&lt;p&gt;Its operating system chooses a temporary source port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;53001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The packet looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source IP:
192.168.1.100

Source Port:
53001

Destination IP:
8.8.8.8

Destination Port:
53
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Phone Does the Same Thing
&lt;/h2&gt;

&lt;p&gt;Now the phone also performs a DNS lookup.&lt;/p&gt;

&lt;p&gt;It chooses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;53001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as well.&lt;/p&gt;

&lt;p&gt;The packet becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source IP:
192.168.1.101

Source Port:
53001

Destination IP:
8.8.8.8

Destination Port:
53
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance this looks problematic.&lt;/p&gt;

&lt;p&gt;Both devices chose the same source port.&lt;/p&gt;

&lt;p&gt;Surprisingly, this is completely valid.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because their source IP addresses are different.&lt;/p&gt;

&lt;p&gt;Inside the LAN they are still unique connections.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Router Receives the First Packet
&lt;/h2&gt;

&lt;p&gt;The router sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.100:53001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and creates a translation entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.100:53001
        ↓
49.43.12.10:40001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice something interesting.&lt;/p&gt;

&lt;p&gt;The router did not keep:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;53001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It selected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;40001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead.&lt;/p&gt;

&lt;p&gt;This becomes the public-facing connection.&lt;/p&gt;

&lt;p&gt;The outgoing packet now looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source IP:
49.43.12.10

Source Port:
40001

Destination IP:
8.8.8.8

Destination Port:
53
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Router Receives the Second Packet
&lt;/h2&gt;

&lt;p&gt;Now the phone's packet arrives.&lt;/p&gt;

&lt;p&gt;The router creates another entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.101:53001
        ↓
49.43.12.10:40002
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what changed.&lt;/p&gt;

&lt;p&gt;The public IP stayed the same.&lt;/p&gt;

&lt;p&gt;The public port changed.&lt;/p&gt;

&lt;p&gt;The translation table now contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.100:53001
        ↓
49.43.12.10:40001

192.168.1.101:53001
        ↓
49.43.12.10:40002
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This table is the real magic behind modern home networking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[Insert Figure 2: PAT Translation Table Here]&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Replies Return
&lt;/h2&gt;

&lt;p&gt;Google sends a reply to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;49.43.12.10:40001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router checks its translation table.&lt;/p&gt;

&lt;p&gt;It finds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;40001
        ↓
192.168.1.100:53001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The packet is rewritten and delivered to the laptop.&lt;/p&gt;




&lt;p&gt;A second reply arrives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;49.43.12.10:40002
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router checks the table again.&lt;/p&gt;

&lt;p&gt;It finds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;40002
        ↓
192.168.1.101:53001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The packet is forwarded to the phone.&lt;/p&gt;

&lt;p&gt;Both devices receive the correct response even though they share the same public IP address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[Insert Figure 3: Reply Mapping Diagram Here]&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  This Is Actually PAT
&lt;/h2&gt;

&lt;p&gt;Most people casually refer to this process as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More specifically, what is happening here is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which stands for:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Port Address Translation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another common name is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAT Overload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because many devices are sharing a single public IP address.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why PAT Is So Powerful
&lt;/h2&gt;

&lt;p&gt;A port number can range from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;65535
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;65,000+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;possible ports.&lt;/p&gt;

&lt;p&gt;That means thousands of simultaneous connections can share the same public IP address.&lt;/p&gt;

&lt;p&gt;Without PAT, home networking as we know it would be much more difficult.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Does the Router Actually Store?
&lt;/h2&gt;

&lt;p&gt;The router does not simply remember IP addresses.&lt;/p&gt;

&lt;p&gt;It maintains a connection table.&lt;/p&gt;

&lt;p&gt;A simplified entry might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inside IP:
192.168.1.100

Inside Port:
53001

Outside IP:
49.43.12.10

Outside Port:
40001

Destination IP:
8.8.8.8

Destination Port:
53
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real routers store additional information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Protocol&lt;/li&gt;
&lt;li&gt;TCP state&lt;/li&gt;
&lt;li&gt;Timeouts&lt;/li&gt;
&lt;li&gt;Flags&lt;/li&gt;
&lt;li&gt;Session metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what allows thousands of connections to coexist simultaneously.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happens When the Connection Ends?
&lt;/h2&gt;

&lt;p&gt;Suppose the browser tab is closed.&lt;/p&gt;

&lt;p&gt;Eventually the TCP session terminates.&lt;/p&gt;

&lt;p&gt;The router notices.&lt;/p&gt;

&lt;p&gt;The translation entry is removed.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;49.43.12.10:40001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes available for reuse.&lt;/p&gt;

&lt;p&gt;This process happens continuously behind the scenes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Random Incoming Traffic Gets Dropped
&lt;/h2&gt;

&lt;p&gt;Imagine a random server sends a packet to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;49.43.12.10:45000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router checks its translation table.&lt;/p&gt;

&lt;p&gt;No matching entry exists.&lt;/p&gt;

&lt;p&gt;The router effectively says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I don't know who requested this connection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The packet is dropped.&lt;/p&gt;

&lt;p&gt;This behavior is one reason home routers provide a basic level of protection against unsolicited traffic.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Insight That Changed My Mental Model
&lt;/h2&gt;

&lt;p&gt;Initially I thought NAT was simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Private IP
        ↓
Public IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is only part of the story.&lt;/p&gt;

&lt;p&gt;The real magic is the translation table.&lt;/p&gt;

&lt;p&gt;The router continuously maintains mappings between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(Private IP, Private Port)
                ↔
(Public IP, Public Port)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for every active connection.&lt;/p&gt;

&lt;p&gt;That is how one public IP address can support dozens of devices and thousands of simultaneous network conversations.&lt;/p&gt;

&lt;p&gt;And once this idea clicked, ports stopped feeling like random numbers and started feeling like apartment numbers in a giant building.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>devops</category>
      <category>linux</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Following a Packet: What Really Happens Between Opening Your Laptop</title>
      <dc:creator>Micheal Angelo</dc:creator>
      <pubDate>Sat, 13 Jun 2026 10:30:24 +0000</pubDate>
      <link>https://dev.to/micheal_angelo_41cea4e81a/following-a-packet-what-really-happens-between-opening-your-laptop-4fc</link>
      <guid>https://dev.to/micheal_angelo_41cea4e81a/following-a-packet-what-really-happens-between-opening-your-laptop-4fc</guid>
      <description>&lt;p&gt;For a long time, networking felt like a collection of unrelated acronyms.&lt;/p&gt;

&lt;p&gt;DHCP.&lt;/p&gt;

&lt;p&gt;ARP.&lt;/p&gt;

&lt;p&gt;DNS.&lt;/p&gt;

&lt;p&gt;TCP.&lt;/p&gt;

&lt;p&gt;IP.&lt;/p&gt;

&lt;p&gt;NAT.&lt;/p&gt;

&lt;p&gt;VPN.&lt;/p&gt;

&lt;p&gt;I could explain each one individually.&lt;/p&gt;

&lt;p&gt;But if someone had asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What actually happens when you open a website?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I would have struggled to answer.&lt;/p&gt;

&lt;p&gt;The turning point came when I stopped learning protocols individually and started following a single packet through the network.&lt;/p&gt;

&lt;p&gt;This article follows that packet from the moment a laptop connects to Wi-Fi until a webpage finally appears in the browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before We Begin: Data, Segments, Packets, and Frames
&lt;/h2&gt;

&lt;p&gt;One of the biggest beginner misconceptions is that everything is called a packet.&lt;/p&gt;

&lt;p&gt;Technically, that is not true.&lt;/p&gt;

&lt;p&gt;As data moves through the networking stack, its name changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application Layer
&lt;/h3&gt;

&lt;p&gt;The browser creates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this stage it is simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Transport Layer
&lt;/h3&gt;

&lt;p&gt;TCP adds a header containing information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source Port&lt;/li&gt;
&lt;li&gt;Destination Port&lt;/li&gt;
&lt;li&gt;Sequence Number&lt;/li&gt;
&lt;li&gt;Acknowledgement Number&lt;/li&gt;
&lt;li&gt;Flags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TCP Header + Data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Segment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Network Layer
&lt;/h3&gt;

&lt;p&gt;IP adds another header containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source IP Address&lt;/li&gt;
&lt;li&gt;Destination IP Address&lt;/li&gt;
&lt;li&gt;TTL&lt;/li&gt;
&lt;li&gt;Protocol&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IP Header + Segment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Packet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Data Link Layer
&lt;/h3&gt;

&lt;p&gt;Ethernet or Wi-Fi adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source MAC Address&lt;/li&gt;
&lt;li&gt;Destination MAC Address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ethernet Header + Packet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frame
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;The final structure looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frame
└── Packet
    └── Segment
        └── Data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This process is known as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Encapsulation&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every packet on the Internet begins this way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Our Example Network
&lt;/h2&gt;

&lt;p&gt;To keep things simple, imagine the following setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Laptop
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MAC = BB:BB:BB:BB:BB:BB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Router
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LAN IP  = 192.168.1.1
LAN MAC = AA:AA:AA:AA:AA:AA
Public IP = 49.43.12.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DNS Server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Website
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com
104.26.10.50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's follow the packet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Laptop Boots
&lt;/h2&gt;

&lt;p&gt;Immediately after startup, the laptop knows surprisingly little.&lt;/p&gt;

&lt;p&gt;It knows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;My MAC Address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it does not yet know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IP Address&lt;/li&gt;
&lt;li&gt;Default Gateway&lt;/li&gt;
&lt;li&gt;DNS Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those must be discovered.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Connecting to Wi-Fi
&lt;/h2&gt;

&lt;p&gt;The laptop successfully joins the wireless network.&lt;/p&gt;

&lt;p&gt;At this point many people assume networking is ready.&lt;/p&gt;

&lt;p&gt;It is not.&lt;/p&gt;

&lt;p&gt;The laptop still lacks an IP address.&lt;/p&gt;

&lt;p&gt;Without one, meaningful communication cannot occur.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: DHCP Gives the Laptop an Identity
&lt;/h2&gt;

&lt;p&gt;The laptop broadcasts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DHCP Discover
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meaning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is there a DHCP server available?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The router responds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DHCP Offer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;containing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IP Address = 192.168.1.100
Gateway    = 192.168.1.1
DNS Server = 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The laptop accepts.&lt;/p&gt;

&lt;p&gt;Now it finally knows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Who am I?
Where should I send traffic?
Which DNS server should I use?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DHCP has completed its job.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: The User Opens a Website
&lt;/h2&gt;

&lt;p&gt;Suppose the user enters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser immediately faces a problem.&lt;/p&gt;

&lt;p&gt;It knows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but websites are reached using IP addresses.&lt;/p&gt;

&lt;p&gt;The browser asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What IP address belongs to example.com?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 5: Before DNS, ARP Happens
&lt;/h2&gt;

&lt;p&gt;To contact the DNS server, the laptop must send traffic through the router.&lt;/p&gt;

&lt;p&gt;It knows the router's IP address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it does not know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Router MAC Address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So it broadcasts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Who has 192.168.1.1?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an ARP request.&lt;/p&gt;

&lt;p&gt;The router replies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.1 is AA:AA:AA:AA:AA:AA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The laptop stores this information in its ARP cache.&lt;/p&gt;

&lt;p&gt;Problem solved.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: The DNS Query Leaves the Laptop
&lt;/h2&gt;

&lt;p&gt;The laptop creates an IP packet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source IP      = 192.168.1.100
Destination IP = 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it creates a frame.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source MAC      = BB:BB:BB:BB:BB:BB
Destination MAC = AA:AA:AA:AA:AA:AA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reveals an important networking principle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Destination IP  = Final Destination

Destination MAC = Next Hop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For many beginners, this is the first surprising realization.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: NAT Changes the Source Address
&lt;/h2&gt;

&lt;p&gt;The router receives the packet.&lt;/p&gt;

&lt;p&gt;The router notices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is a private IP address.&lt;/p&gt;

&lt;p&gt;Private addresses cannot travel across the public Internet.&lt;/p&gt;

&lt;p&gt;The router performs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Network Address Translation.&lt;/p&gt;

&lt;p&gt;The source address changes from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;49.43.12.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which is the router's public IP.&lt;/p&gt;

&lt;p&gt;The router also records this translation in its NAT table.&lt;/p&gt;

&lt;p&gt;This is how replies find their way back later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Routers Forward the Packet
&lt;/h2&gt;

&lt;p&gt;The packet begins traveling across the Internet.&lt;/p&gt;

&lt;p&gt;An important detail often goes unnoticed.&lt;/p&gt;

&lt;p&gt;At every hop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Old Ethernet Frame
      ↓
Discarded

New Ethernet Frame
      ↓
Created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MAC Addresses
Change at every hop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IP Addresses
Remain largely unchanged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why networking engineers often say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MAC = Hop-to-Hop

IP = End-to-End
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 9: The DNS Server Responds
&lt;/h2&gt;

&lt;p&gt;Eventually the packet reaches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google DNS receives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source IP = 49.43.12.10
Destination IP = 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice something important.&lt;/p&gt;

&lt;p&gt;Google never sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It only sees the public address created by NAT.&lt;/p&gt;

&lt;p&gt;Google replies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com = 104.26.10.50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 10: The Reply Finds Its Way Home
&lt;/h2&gt;

&lt;p&gt;The DNS reply eventually returns to the home router.&lt;/p&gt;

&lt;p&gt;The router consults its NAT table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;49.43.12.10:53001
        ↓
192.168.1.100:53001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router now knows exactly which device requested the information.&lt;/p&gt;

&lt;p&gt;The reply is forwarded back to the laptop.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 11: TCP Establishes a Connection
&lt;/h2&gt;

&lt;p&gt;Now the browser finally knows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com = 104.26.10.50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before sending data, TCP establishes a connection.&lt;/p&gt;

&lt;p&gt;The famous three-way handshake occurs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SYN
 ↓
SYN-ACK
 ↓
ACK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The connection is now established.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 12: TLS Creates Encryption
&lt;/h2&gt;

&lt;p&gt;Because the website uses HTTPS, encryption must be negotiated.&lt;/p&gt;

&lt;p&gt;The browser and server exchange:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Certificates&lt;/li&gt;
&lt;li&gt;Cryptographic parameters&lt;/li&gt;
&lt;li&gt;Session keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An encrypted channel is created.&lt;/p&gt;

&lt;p&gt;Only after this step can secure communication begin.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 13: The Actual Website Loads
&lt;/h2&gt;

&lt;p&gt;Finally, the browser sends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server responds with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser renders everything.&lt;/p&gt;

&lt;p&gt;The webpage appears.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changes When a VPN Is Enabled?
&lt;/h2&gt;

&lt;p&gt;Without a VPN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop
 ↓
Router
 ↓
ISP
 ↓
Website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a VPN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop
 ↓
Router
 ↓
ISP
 ↓
VPN Server
 ↓
Website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router does not disappear.&lt;/p&gt;

&lt;p&gt;ARP still happens.&lt;/p&gt;

&lt;p&gt;DHCP still happens.&lt;/p&gt;

&lt;p&gt;Frames still exist.&lt;/p&gt;

&lt;p&gt;The difference is that traffic is encrypted and sent to the VPN server first.&lt;/p&gt;

&lt;p&gt;To the ISP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VPN Server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;appears to be the destination.&lt;/p&gt;

&lt;p&gt;The actual website remains hidden inside the encrypted tunnel.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mental Model That Finally Helped
&lt;/h2&gt;

&lt;p&gt;Networking became easier once I stopped imagining the Internet as a single connection.&lt;/p&gt;

&lt;p&gt;Instead, I started viewing it as many small conversations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop ↔ Router

Router ↔ ISP Router

ISP Router ↔ ISP Router

ISP Router ↔ Destination
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each conversation uses different MAC addresses.&lt;/p&gt;

&lt;p&gt;The IP packet survives the journey.&lt;/p&gt;

&lt;p&gt;Once that idea clicked, concepts such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DHCP&lt;/li&gt;
&lt;li&gt;ARP&lt;/li&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;li&gt;NAT&lt;/li&gt;
&lt;li&gt;VPNs&lt;/li&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;Wireshark captures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;started feeling like pieces of the same puzzle instead of isolated protocols.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;For a long time, networking felt overwhelming because every protocol seemed independent.&lt;/p&gt;

&lt;p&gt;The breakthrough came when I followed a single packet from start to finish.&lt;/p&gt;

&lt;p&gt;Rather than memorizing definitions, I began asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What problem is this protocol solving right now?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Viewed through that lens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DHCP provides identity.&lt;/li&gt;
&lt;li&gt;ARP finds local devices.&lt;/li&gt;
&lt;li&gt;DNS finds destinations.&lt;/li&gt;
&lt;li&gt;NAT enables Internet access.&lt;/li&gt;
&lt;li&gt;TCP creates reliable communication.&lt;/li&gt;
&lt;li&gt;TLS secures it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And together, they make something as ordinary as loading a webpage possible.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>devops</category>
      <category>linux</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
