Introduction
Why modern apps don’t expose EC2 directly
In theory, you can deploy an application on an EC2 instance, grab its public IP, and let users hit it directly. In practice, this fails almost immediately in real-world systems because:
We want users to be able to access the web application with a human-readable domain name such as google.com, etc. The public IP keeps changing when instances are stopped, scaled, or replaced. Users also won’t be able to establish a secure HTTPS connection to the website reliably.
As traffic on our web application grows, a single server becomes a bottleneck. You need a mechanism to add or remove backend instances without letting users know about the change.
Another challenge when making EC2 instances available directly to the internet is that, in a highly available architecture, you need to distribute traffic across multiple availability zones to ensure that a single data center failure doesn’t bring down your entire application.
AWS solves the above problems through a carefully designed pipeline. This article explains the complete end-to-end request flow, from the moment a user types your domain in their browser to the moment a Docker container receives the request.
How DNS Resolution Works
What Happens when a User enters a domain?
- User's Browser (stub resolver) doesnt know the IP address of the domain you typed for ex. app.example.com, it sends a DNS query to the configured DNS Resolver which is usually your ISP's DNS Resolver or a Public Resolver like 1.1.1.1
- This DNS Resolver performs a recursive lookup on the DNS registry on your browser's behalf, It queries the root nameservers to find the authoritative nameserver for the .com TLD for our earlier example.
- The Root Nameserver responds with the address of the TLD authoritative nameserver for .com.
- The TLD Authoritative Nameserver then responds with the address of the authoritative nameserver for example.com.
- This step is where AWS Route 53 comes into play, Route 53 acts as the authoritative nameserver for example.com and managing all the DNS Records for the example.com domain. When a resolver queries Route 53 with a request for app.example.com, Route 53 returns the IP address(es) associated with that subdomain.
- The Recursive Resolver caches this response and sends it back to the User's Browser
- The User's Browser then establishes a TCP connection to app.example.com's Resolved IP Address.
This whole process typically takes 10 to 100 milliseconds but could be optimised to be faster by implementing caching at multiple levels meaning the subsquent requests will skip several steps.
Difference between Authoritative and Recursive DNS Servers
Authoritative DNS Servers store the actual DNS Records for the domain and respond to queries about domains they are responsible for. AWS acts as your Authoritative DNS
Recursive DNS servers on the other hand perform the full resolution process on behalf of clients, querying other servers and caching results. Your ISP typically provides these.
Route 53 can also function as a recursive resolver (through Route 53 Resolver) when you need to resolve private VPC domain names or forward queries to on premise DNS servers.
Difference between Route 53 Alias Records and CNAME Records
A CNAME Record is a "canonical name" that points to another domain name. For ex. app.example.com points to a CNAME record that points to the ALB's Domain (my-alb-123456.us-east-1.elb.amazonaws.com). In this case the resolver must perform another DNS Query to resolve ALB's domain name to its IP Address.
Using CNAME creates increased latency because of two DNS Lookups instead of one and Increased cost as AWS charges per query so CNAME doubles your query cost.
An Alias Record is a solution specifically for AWS Resources, When Route 53 receives a query for app.example.com, it internally resolves the ALB's DNS name to its current IP addresses and returns those IP addresses directly to the client, This is advantageous because there is only a single DNS Lookup reducing latency, lower costs as Alias queries to AWS Resources are free.
Application Load Balancer Architecture
Layer 7 Load Balancing
The Application Load Balancer operates at Layer 7 (Application Layer) of the OSI model, meaning it can read and understand HTTP/HTTPS traffic.
At Layer 7, the ALB can make routing decisions based on:
HTTP Host header (e.g., route api.example.com differently from www.example.com)
HTTP path (e.g., route /api/* to API servers, /static/* to static file servers)
HTTP methods (GET, POST, etc.)
Query parameters
HTTP headers (custom headers, user-agent, etc.)
Hostname patterns and regex matching
Listeners
Each ALB have one or more listeners, that listen for incoming connections.
A listener defines:
Protocol: HTTP or HTTPS (ALB only supports application protocols, not TCP/UDP)
Port: The port the ALB listens on (typically 80 for HTTP, 443 for HTTPS)
Certificate (for HTTPS): The SSL/TLS certificate from AWS Certificate Manager (ACM)
Rules: Conditions that determine how traffic is routed
When the ALB receives a connection on port 443 it,
- Accepts the TLS handhshake connection using the configured certificate
- Decrypts the incoming HTTPS Traffic
- Reads the HTTP headers to evaluate listener rules
- Routes the request based on matching rules
SSL/TLS Termination with AWS Certificates Manager (ACM)
SSL Termination means the ALB decrypts HTTPS traffic, reads the unencrypted HTTP headers to make routing decisions, and then sends traffic to the backend targets.
AWS Certificate Manager (ACM) provides free SSL/TLS certificates with automatic renewal. The process to obtain a certificate is:
Request a certificate for example.com and all subdomains.
ACM sends a validation email or creates a CNAME record in Route 53 that you must confirm.
Once validated, attach the certificate to your ALB listener.
ACM renews the certificate automatically before expiration (no configuration needed).
After decrypting the HTTPS traffic, it can direct the unencrypted HTTP to the backend or re-encrypt them and send it to the backend for end to end encryption.
For most applications, plaintext HTTP between ALB and backend is acceptable because both are within your VPC (network isolation), security groups restrict who can communicate with the backend, and the expensive crypto happens once (at the user-facing edge).
Listener Rules
Listener rules determine which target group can recieve this request. Rules are evaluated in priority order (lowest number first), and the first matching rule wins
Example Listener rule set:
Rule 1 (Priority: 1):
Condition: Host header = "api.example.com"
Action: Forward to API Target Group
Rule 2 (Priority: 2):
Condition: Path = "/health"
Action: Return fixed response (200 OK)
Rule 3 (Priority: 3):
Condition: Path starts with "/uploads" AND Host = "example.com"
Action: Forward to Uploads Target Group
Default Rule (Priority: 10000):
Action: Return 404 Not Found
Listener Rules are particularly useful in scenarios when you want the ALB to route traffic to different environemnt target groups like production and development based on host headers.
Some caveats for Listener Rules are:
- Conditions use pattern matching. For example, path starts with "/api" matches /api/users, /api/products, /api/v1/health, etc.
- When a rule has multiple conditions, all of them have to be TRUE (AND logic), If you need OR logic, you will have to create seperate rule
Target Groups and Traffic Forwarding
A target group is the component that turns a routing decision into an actual backend call. Once a listener rule decides where a request should go, the target group defines how the load balancer delivers it which port and protocol to use, how to determine whether a backend is healthy, and how to choose one healthy target among many at runtime. Instead of pointing traffic at fixed servers, the ALB forwards every request through a target group, which constantly tracks backend health and availability and dynamically routes traffic to EC2 instances, IPs, or containers as they scale, fail, or get replaced.
Example Target Group Configuration:
Target Group Configuration:
- Name: "production-api"
- Protocol: HTTP (or HTTPS for re-encryption)
- Port: 8080 (where the application listens inside containers)
- VPC: vpc-12345678
- Health Check:
- Protocol: HTTP
- Path: /health
- Port: 8080
- Interval: 30 seconds
- Timeout: 5 seconds
- Healthy threshold: 2 successful checks
- Unhealthy threshold: 3 failed checks
- Stickiness: Enabled (AWSALB cookie, 24 hour duration)
- Targets:
- i-0a1b2c3d4e5f6g7h8 (EC2 instance, port 8080)
- i-0x1y2z3a4b5c6d7e8 (EC2 instance, port 8080)
Target Groups are used for health checks as follows:
Every 30 seconds (configurable), the ALB sends an HTTP GET request to each registered target.
The target must respond with a 200-299 HTTP status code (configurable).
-
Threshold Logic:
- If 2 consecutive health checks succeed (healthy threshold), mark as healthy.
- If 3 consecutive health checks fail (unhealthy threshold), mark as unhealthy.
Traffic Routing: The ALB only routes traffic to targets marked as healthy.
Graceful Degradation: If ALL targets in a target group become unhealthy, the ALB enters "fail-open" mode and routes traffic to all targets regardless (better than returning 503 Service Unavailable to users).
Your application must implement a "/health" endpoint that responds quickly (before the 5-second timeout), returns a 200 status code when healthy, performs meaningful health checks (database connectivity, cache availability, disk space, etc.), and returns 500+ status when unhealthy.
After a request is mapped to a target group, the ALB chooses a destination from the set of currently healthy targets using a load-balancing strategy.
It uses Round Robin Strategy by default where requests are routed sequentially across all healthy targets. This is simple and works well for simple stateless applications
For workloads with uneven or long-running requests, the ALB can instead prefer the target with the fewest in-flight requests, reducing tail latency under load.
In deployment scenarios like canary releases or blue-green rollouts, traffic can be split using weighted routing so only a controlled percentage of users reach a new version.
Optionally, the ALB can enable sticky sessions, where a client is consistently routed to the same backend via an ALB-managed cookie; this can be useful for stateful or expensive-to-initialize applications, but it weakens fault tolerance and requires a clear strategy for handling backend failures.
Container Port Mapping
Docker containers listen on internal ports and are mapped to host ports:
Host:8080 → Container:8080
ALB → EC2 (8080) → Docker bridge → Container → App process
The target group port must match the exposed container port.
EC2 And Docker, Where your Application actually runs
EC2 as the compute layer
The EC2 instance is the host machine where your Docker containers run. From the ALB's perspective, the EC2 instance is identified by:
Private IP Address: The IP within the VPC (e.g., 10.0.1.100)
Port: The port the container is listening on (e.g., 8080)
The ALB sends traffic directly to this private IP, assuming network connectivity exists, This requires:
VPC Configuration - The ALB and EC2 instance must be in the same VPC or connected via VPC peering/Transit Gateway.
Subnet Routing - The ALB can route to targets in any subnet within the VPC.
Security Groups - The EC2 instance's security group must allow inbound traffic from the ALB's security group.
Using security group references (not IP addresses) ensures that even if the ALB's IP addresses change, the routing still works because security groups dynamically resolve to all ENIs in that group
Docker Container recieveing traffic
Inside the EC2 instance, Docker creates a virtual network bridge for each container
Full End to End Request Life cycle
Let's trace a complete HTTPS request from a user in London to your application in us-east-1:
A user in London requests https://app.example.com. The browser resolves the domain via a recursive DNS resolver which reaches Route 53 and receives an Alias to the Application Load Balancer. The ALB hostname resolves to multiple IP addresses cached for 59 seconds.
The browser connects to one IP on port 443, completes TCP and TLS handshakes using SNI app.example.com, and establishes an encrypted connection.
The browser sends GET /api/users?id=123. The ALB terminates TLS, evaluates listener rules, and forwards the request to the API target group.
A healthy EC2 target is selected using round robin and the request is forwarded with X Forwarded For and X Forwarded Proto https headers to a Docker container running the Node.js application. The application validates the session, processes the request, and returns a JSON response.
The ALB re-encrypts the response and returns it to the browser. Subsequent requests include the AWSALB cookie, ensuring traffic is routed to the same backend instance for up to 24 hours.
To the user, this appears as a single HTTPS request while internally handling DNS resolution, secure transport, rule based routing, load balancing, container execution, and session persistence.
Conclusion
The architecture connecting Route 53 to ALB to EC2/Docker represents decades of distributed systems engineering. Each component solves a specific problem:
Route 53 provides dynamic DNS resolution that automatically tracks your load balancer's changing IP addresses.
ALB provides Layer 7 routing, SSL termination, and health-based traffic steering across multiple backends.
EC2 and Docker provides isolated, scalable compute with port mapping that connects load balanced traffic to your application.


Top comments (0)