Background reading · 30 min read
The Pieces You Keep Hearing About
Lessons 0 to 4 mention Nginx, PM2, load balancers, RDS, CDN and VPC before their own lessons arrive. This is the missing chapter. For each piece you will see what it is, which problem it solves, where it sits in the flow, and a little of the “how”. That makes every later lesson easier to follow.
What this reading covers
- The map — one request through everything
- Nginx — the receptionist (reverse proxy)
- PM2 — keeping the app alive
- VPC — public and private rooms
- RDS — the managed database
- S3 + CloudFront — files, and files near the user
- ALB + Auto Scaling — one server becomes many
- Redis — the fast memory beside the database
- Docker — the same box everywhere
- CI/CD — automated testing and deployment
- Terraform — the cloud written down
- CloudWatch — knowing before users tell you
- How it all fits: from git push to a user's screen
- Who does what, and which lesson builds it
Each section has the same four parts. What it is uses one everyday comparison. The problem says why the piece exists. Where it sits shows a small flow. The minimal how explains the basic steps in plain words and names the lesson that does it for real. You do not need to run anything today. When a lesson mentions a piece and you have forgotten it, come back to that section.
The map — one request through everything
Lesson 0 showed the target architecture as five boxes. Lessons 2, 3 and 4 explained the arrows at the top. They showed how a name becomes an IP address, how a port is reached and how the connection is encrypted. Here is the same picture with every box filled in. The boxes are in the order that one request meets them.
- User's browsertypes https://yourapp.com
- DNS: name → IP (Lesson 3, 13)Route 53answers with the CloudFront or ALB address
- static file? served from the edge (Lesson 11)CloudFront (CDN)cache near the user; passes dynamic requests on
- TLS ends here or at the ALB (Lesson 4)ALB (load balancer)picks a healthy server; Auto Scaling adds more under load
- inside your VPC, public subnet (Lesson 6, 12)EC2 · Nginxreceptionist on :80/:443 — forwards to the app on :3000
- 127.0.0.1:3000 (Lesson 7, 10)EC2 · PM2 → Next.js / Nodeyour code, kept alive and restarted on crash
- private subnet only (Lesson 8, 16)RDS (PostgreSQL) · Redis · S3data, cache/sessions, files — never reachable from the internet
Notice two things before we go piece by piece. First, the request crosses a border at the ALB (Application Load Balancer). Everything above it is public. Everything below it is inside your VPC (Virtual Private Cloud, your private network in AWS). Second, the app itself is only two boxes: Nginx and the Node process. Everything else exists to make those two boxes faster, safer, or more numerous.
Nginx — the receptionist
What it is
Nginx (say “engine-x”) is a web server. It is very good at one job: it accepts a huge number of connections cheaply and decides what to do with each one. In Lesson 2's office building it is the receptionist, the only person visitors talk to. In our setup it works as a reverse proxy. A reverse proxy is a server that stands in front of your app. It takes each public request on port 443 and passes it to your Next.js app on a private port. The visitor never talks to the app directly.
The problem it solves
Node.js is a program that runs JavaScript on a server, and a port is a numbered door on a server that a program listens on. Your Node app can listen on port 443 directly. But then it must also handle TLS certificates (TLS is the encryption that makes HTTPS safe). It must serve images and CSS, compress responses, block abusive clients and add security headers. It must also survive 10,000 people opening a connection at once. Node is a good place to run your application code, but a weak front door. Nginx is an excellent front door, but it knows nothing about what your application does. So we split the jobs, and each tool does what it is best at.
Where it sits
- Browserhttps://yourapp.com/dashboard
- port 443 — the only public doorNginxends TLS · serves /static files itself · gzips · rate-limits · adds headers
- proxy_pass http://127.0.0.1:3000 — privateNext.js / Node appsees a plain HTTP request with X-Forwarded-* headers
- response goes back the same way, compressed by NginxBrowsergets the page — never knew port 3000 existed
The minimal how
Write one server block for each site. It listens on port 443 with the certificate. It sends every request to http://127.0.0.1:3000 with the proxy_pass setting. It also adds headers that say who the real visitor was. (The address 127.0.0.1 means “this same computer”.) A second small block on port 80 redirects visitors to HTTPS. That is about twenty lines in all. Lesson 10 builds it line by line.
Day to day you only need four moves. Install it. Test the config with nginx -t. Reload it, which applies new settings without dropping connections. Read its access log and error log.
Your app only ever sees a connection from 127.0.0.1, which is Nginx. Without those headers, the app would think every user is on the same computer and uses plain HTTP. X-Forwarded-For carries the real client IP address. The app needs it for rate limits and logs. X-Forwarded-Proto tells the app that the original request used HTTPS. Secure cookies and redirects need that. A missing header is also the cause of the ERR_TOO_MANY_REDIRECTS loop from Lesson 4.
PM2 — keeping the app alive
What it is
PM2 is a process manager for Node. A process manager is a program that starts other programs, watches them and restarts them when they stop. PM2 runs your app in the background as a daemon (a program that runs without a terminal). It brings the app back after a crash, after a reboot, or after you close your terminal. Think of a caretaker whose only job is to keep the lights on.
The problem it solves
Lesson 1 ended with a classic trap. You run npm start in an SSH session (SSH is a secure way to log in to a server and type commands), close your laptop, and the site goes down. A process (a running program) started from a shell is tied to that shell. When the SSH session closes, the shell sends its programs a hang-up signal, and they stop. Even if the app survived, one unhandled error would end it and nothing would restart it. A live site needs something that outlives your session and reacts to failure on its own.
| Event | npm start | pm2 start |
|---|---|---|
| You close the SSH terminal | app dies | keeps running (daemon) |
| App throws an uncaught error | process exits, site down | restarted in ~100 ms, logged |
| Server reboots (patch, crash) | nothing starts, site down | pm2 startup brings it back |
| You deploy new code | kill, start, seconds of downtime | pm2 reload — zero-downtime swap |
| Machine has 2 CPU cores | 1 core used | -i max: one process per core |
The minimal how
Start the app under PM2 and give it a name. Tell PM2 to start itself when the server boots. Save the process list. After that, PM2 restarts the app after every crash and every reboot. Lesson 7 does it in three commands.
For a real app, keep the settings in a small file called ecosystem.config.js. The settings are the name, the start command and how many copies to run. This way you do not have to remember command flags.
All three can keep a process alive. systemd is the service manager built into Linux. PM2 is the easiest choice for one Node app on one server, so Lesson 7 uses it. Once the app runs in a Docker container (Lesson 9), Docker does the restarting and you usually drop PM2. The idea is the same in every case: something other than your shell owns the process.
VPC — public and private rooms
What it is
A VPC (Virtual Private Cloud) is your own private network inside AWS. It is the building from Lesson 2, now with floors. Inside it you create subnets. A subnet is a smaller range of IP addresses inside the VPC. A subnet is either public (it has a route to the internet) or private (it does not). A server's subnet decides whether the internet can reach it at all. This happens before Security Groups (AWS firewall rules for each server) get a say.
The problem it solves
Lesson 2 protected the database with a Security Group rule (“only from the app's SG”). That is good, but someone can change a rule by mistake. A database in a private subnet has no path from the internet. There is no route, so no rule can open it by accident. This is called defence in depth: the network layout and the firewall both say no.
Where it sits
The minimal how
Five objects make a VPC useful. Lesson 6 creates each one in the console. Lesson 15 writes them as Terraform. Learn the names now:
| Object | What it does | Ours |
|---|---|---|
| VPC | The whole range of private IP addresses | 10.0.0.0/16 (65,536 addresses) |
| Subnet | A slice of it, inside one availability zone (one separate data centre area in a region) | public 10.0.1.0/24, private 10.0.2.0/24 |
| Internet Gateway | The VPC's door to the internet. It is a gateway you attach to the VPC | one per VPC |
| Route table | A list of rules that say where traffic for each destination goes | public: 0.0.0.0/0 → igw · private: no such row |
| NAT Gateway | Lets private servers call out (for updates and APIs) without being reachable from the outside in | optional. It costs about $35 a month (more in some regions, plus data fees), so Lesson 6 explains when to skip it |
Only one thing. A subnet is public because its route table has a 0.0.0.0/0 row that points at the Internet Gateway. (0.0.0.0/0 means “every address on the internet”.) Delete that row and every server in the subnet silently becomes private. In Lesson 6 you may ask “why can't I SSH to my instance?” Check the Security Group first, then check the route table.
RDS — the managed database
What it is
RDS (Relational Database Service) is a database such as PostgreSQL or MySQL that AWS runs for you. You get a hostname, a port and a password. You never SSH into the machine, because you have no access to it. AWS looks after the operating system, the disk, the updates (patches), the backups and the failover. Failover means switching to a spare copy when the main one fails.
The problem it solves
You could install PostgreSQL on your EC2 server tonight. Then all of this is your job. You must make nightly backups and test that they restore. You must apply security patches. You must watch the disk so it does not fill up. The database and your app would compete for CPU and memory. And if that one server dies, all your customers' data dies with it. RDS turns that list into a monthly bill. The smallest instance costs roughly $15 a month. RDS also makes the app servers stateless. Stateless means a server keeps no important data of its own, so it can be thrown away and replaced. That is what lets Auto Scaling delete servers and create new ones safely.
Where it sits
It sits in the private subnet. Its Security Group accepts port 5432 (the PostgreSQL port) only from the app server's Security Group. From the app it looks like any other Postgres database.
The app connects with one line in .env: DATABASE_URL=postgresql://user:password@host:5432/myapp.
The minimal how
Create the smallest Postgres instance. Make it not publicly accessible, put it in the private subnets, and give it the database security group. Copy its endpoint (its hostname) into DATABASE_URL. Test the connection from the app server, not from your laptop, because the database is private. Lesson 8 explains every field.
Multi-AZ means a standby copy of the database in a second Availability Zone, with automatic failover. It costs about double, and it is worth it for real production. Snapshot means a full backup. You can restore it into a new instance. Parameter group means the settings of the database. It replaces the postgresql.conf file that you would edit by hand.
S3 + CloudFront — files, and files near the user
What they are
S3 (Simple Storage Service) is a warehouse for files, which S3 calls “objects”. You can store any number of files in it (one file can be up to 5 TB), it is cheap (about $0.023 per GB per month in the main US region; other regions differ), and each file has its own HTTPS URL. It is very durable: “eleven nines” means 99.999999999%, so you almost never lose a file. A bucket is a top-level folder with a name that is unique across all of AWS. CloudFront is a CDN (Content Delivery Network): a network of servers around the world that keep copies of your files. AWS has hundreds of these locations, called edge locations or points of presence. CloudFront serves each user from a location near them.
The problem they solve
There are two problems. First, files on an EC2 disk vanish when Auto Scaling replaces the server. Two servers also cannot share one disk. So user uploads must live somewhere that all servers can reach. That place is S3. Second, distance makes things slow. A user in London who fetches a 2 MB image from a server in Mumbai waits more than 100 ms for every round trip. From a London edge location the wait is only a few milliseconds. That is what CloudFront fixes. It also handles traffic spikes: a million requests for a cached file never reach your server.
Where they sit
- User in Mumbai
- ↓Mumbai edge: not cached
- ↓S3 in ap-south-1 (origin)
- ↓edge stores a copy
- ↓user gets file — ~120 ms
- User in Mumbai
- ↓Mumbai edge: cached ✓
- ↓user gets file — ~15 ms
- ↓origin never touched
- ↓repeat until TTL expires
The minimal how
Create a bucket with public access blocked. Then copy files in, list them and sync a folder. You can also give out pre-signed links. A pre-signed link is a URL with a built-in signature that lets someone use one private file for a short time, for example a few minutes. Lesson 11 puts CloudFront in front.
From code you use the SDK (Software Development Kit, a library of ready-made functions). For S3 it is @aws-sdk/client-s3. It has the same actions: PutObject and GetObject. With pre-signed URLs, the browser can upload a file straight to S3, and the file never passes through your server. CloudFront sits in front with a “distribution”. A distribution is one CloudFront setup. Its origin (the place CloudFront fetches files from) is the bucket for assets, or your ALB for pages. An ACM certificate (Lesson 4) gives it HTTPS.
Buckets are private by default. Keep them that way. Serve public assets through CloudFront with an origin access control. This is a setting that lets only CloudFront read the bucket. Serve private files through pre-signed URLs. Public buckets have caused many data leaks that made the news.
ALB + Auto Scaling — one server becomes many
What they are
An Application Load Balancer (ALB) is one public address that spreads incoming requests across a group of servers. It checks each server with a health check, a small test request sent again and again. It stops sending traffic to any server that fails. An Auto Scaling group decides how many servers you should have right now. When load rises, it starts identical copies from a template. When load falls, it shuts some down.
The problem they solve
One EC2 server can fail in three ways. It can get too busy. It can die. It can need to go down for an update. One move fixes all three: run more than one server, and put something in front that knows which servers are healthy. That “something” is the ALB. Then comes the next question: how many servers? Auto Scaling answers it. You might run two at night and six at month-end, when every tenant logs in. (A tenant is one customer company that uses your SaaS app.) You pay only for the hours each server runs.
Where they sit
The minimal how
Three objects, always in this order:
- Target group: a list of servers that can receive traffic, with rules for them. For example: “port 3000 on these instances. Run the health check
GET /api/healthevery 30 seconds. Two failures in a row means unhealthy.” - Load balancer and listener: the load balancer is public and sits in the public subnets. A listener is a rule that waits for traffic on one port. One listener on port 443 has an ACM certificate and forwards to the target group. Another listener on port 80 redirects to 443.
- Launch template and Auto Scaling group: the launch template is the recipe for a new server. It names the AMI (Amazon Machine Image, a saved copy of a configured server disk), the instance size, the app's Security Group and a script that starts the app on boot. The Auto Scaling group then sets a minimum, a desired and a maximum number of servers. It also has a scaling rule such as “keep average CPU at 60%”.
export async function GET() {
// keep it cheap: no database call, no login check. It only answers "is the app up?"
return Response.json({ ok: true, ts: Date.now() });
}Any request can land on any server. So the app must not keep anything on its own disk or in its own memory that a later request will need. Sessions go to Redis. Uploads go to S3. Other data goes to RDS. This is the “stateless” rule from the hosting-platforms reading. It is also why RDS, S3 and Redis come before Lesson 12.
Redis — the fast memory beside the database
What it is
Redis is a key-value store that keeps its data in RAM (the computer's fast working memory). A key-value store is like a big dictionary: you save a value under a name (the key) and read it back by that name. Reads and writes usually take well under a millisecond, which is much faster than a typical database query. The price is that Redis can only hold what fits in memory. It is also a poor choice for data with many linked tables. On AWS, a managed version is called ElastiCache.
The problem it solves
It solves three common problems. Sessions: a session is the record that says a user is logged in. With several app servers, a login on server 1 must be visible to server 2. Asking Postgres “who is this?” on every request is wasteful. Caching: a cache is a copy of an answer that is slow to compute. Examples are a tenant's settings, the dashboard totals or the product list. You compute them once and serve them from Redis for the next 60 seconds. Rate limiting and queues: rate limiting means capping how many requests one user can make. Redis counters can be safely increased by many servers at once. A queue is a list of jobs that a background worker takes one by one, such as the PDF generation from Lesson 0.
The minimal how
import Redis from "ioredis";
const redis = new Redis(process.env.REDIS_URL); // for example redis://myapp-cache.abc.ap-south-1.cache.amazonaws.com:6379
export async function getTenantSettings(tenantId: string) {
const key = `tenant:${tenantId}:settings`;
const cached = await redis.get(key); // 1. try the fast path (the cache)
if (cached) return JSON.parse(cached);
const fresh = await db.tenantSettings.findUnique({ where: { tenantId } }); // 2. not found → ask the database
await redis.set(key, JSON.stringify(fresh), "EX", 60); // 3. keep it for 60 seconds
return fresh;
}You can open a command-line shell to Redis. There you can set, read, count and expire keys by hand. This helps you see what the app stored. Lesson 16 does it on your own computer first, then on ElastiCache.
Docker — the same box everywhere
What it is
Docker is a tool that packs your app and everything it needs into one image. That includes Node itself, system libraries, your node_modules and the built output. An image is a read-only package. A running copy of an image is a container. The image you build on your laptop is exactly what runs on EC2. So “it works on my machine” becomes “it works everywhere, or it fails everywhere”.
The problem it solves
Lesson 7's manual deploy installs Node on the server by hand. It clones the code, runs npm install on the server and builds on the server. This works once. A second server must be set up in exactly the same way. A Node upgrade must be repeated on every server. A library that behaves differently on Ubuntu than on macOS shows its problem only in production. Docker puts all of that into one image that you build once and ship as one unit.
The minimal how
A Dockerfile is a text file with the steps to build an image. Start from a Node image. Install the dependencies. Build the app. Then copy only the built output into a small final image that runs node server.js. Lesson 9 explains every line.
The loop has four steps. Build an image and give it a version tag. Run it on your computer to test it. Push it to a registry, which is a store for images (on AWS it is ECR, Elastic Container Registry). On the server, pull that exact tag and run it.
An image is the recipe with all the ingredients, frozen. It is a file. A container is one dish cooked from it. It is a running process. You build an image once and run as many containers from it as you like. On a 4-core server you might run one container with PM2 inside, or four containers with no PM2.
CI/CD — automated testing and deployment
What it is
Continuous Integration (CI) means every push automatically runs the tests and the build on a clean machine. Broken code is caught before anyone merges it. Continuous Deployment (CD) means that when the main branch passes everything, the same automation sends the code to production. GitHub Actions provides this automation. A YAML file in your repo lists the steps (YAML is a simple text format for settings). GitHub supplies the machines that run them.
The problem it solves
Manual deploys are slow, risky and uneven. Someone connects by SSH at 6pm, forgets one step, and the fix takes an hour. A pipeline (an automatic series of steps) does the same steps every time, in minutes, and keeps a log. It also refuses to deploy anything that failed a test. So “deploy” stops being one person's job and becomes a merge button.
The minimal how
A deploy workflow is a list of steps that run on every push to main:
- check out the code, install, lint and test — stop if anything fails;
- log in to AWS (with OIDC, a way to get short-lived access without stored passwords or keys);
- build the Docker image and push it to ECR;
- tell the servers to switch to the new image, one at a time.
That is exactly Lesson 7's manual sequence, written down. That is the whole trick. You cannot automate a deploy you have never done by hand. This is why Lesson 7 comes before Lesson 14.
Terraform — the cloud written down
What it is
Terraform is a tool that lets you describe every AWS object in text files. This includes the VPC, subnets, Security Groups, EC2, RDS, ALB and buckets. Then one command makes the real setup match the files. This idea is called Infrastructure as Code. If you change a file and run the command again, Terraform applies only the difference. The files live in git, so your infrastructure gets pull requests, reviews and history, just like code.
The problem it solves
After Lessons 6 to 13 you will have clicked through perhaps forty console screens. Nobody remembers forty screens. To rebuild the setup in a second region, for a staging copy (a practice copy of production), or after an accident, you would repeat all of them from memory. Terraform makes the setup reproducible, which means you can build it again exactly. It also lets you answer “what exactly is running in production?” by reading a folder.
The minimal how
Each piece of infrastructure becomes a block of code. For example, a security group is a resource "aws_security_group" block that lists its inbound rules. A server is a resource "aws_instance" block that refers to that group by name.
The loop has these commands. init sets up the project (run it once). plan shows what would change. apply makes the changes. In a practice account you can also use destroy to delete everything. Read every plan before you apply it.
Terraform keeps a terraform.tfstate file. It records what Terraform created and the real IDs. If you lose it, Terraform forgets that it owns anything. Lesson 15 stores it in S3 with locking (locking stops two runs from changing it at the same time). Every team does this first. Many solo developers forget it.
CloudWatch — knowing before users tell you
What it is
CloudWatch is the AWS service that collects three things. Metrics are numbers over time, such as CPU use, request count, the rate of 5xx errors (server errors) and free disk space. Logs are lines of text that your app and Nginx write. Alarms are rules such as “if this metric crosses this line for N minutes, tell someone”. Most AWS services send metrics to CloudWatch automatically. For your own logs you install one small program, called an agent.
The problem it solves
On Vercel, the dashboard was free and already there. On EC2, if the disk fills up at 3am, the first sign may be a customer email at 9am. That happens unless something is watching. In Lesson 1, tail -f and df -h were how you looked yourself. CloudWatch looks for you, all the time, on every server at once.
The minimal how
Send every server's logs to CloudWatch Logs. Then you can read and search all of them in one place. Start with one alarm. For example: if the load balancer sees more than ten errors a minute for five minutes, send a message to the on-call channel (the chat channel of the person who is responsible right now).
ALB 5xx rate (the load balancer returns many server errors) · healthy host count below the minimum · RDS free storage below 10% · EC2 status check failed. With these four, almost every outage in this course's architecture alerts you before a customer notices. Lesson 17 sets them up in fifteen minutes.
How it all fits: from git push to a user's screen
The map at the top followed a request. This one follows a change, from your keyboard to production. Every box here is a piece from above, doing its job in order.
- Your laptopgit push origin main
- GitHub receives the commitGitHub Actions (CI)installs, lints, runs tests, npm run build
- any step fails → stop here, nothing deployedDocker buildpackages app + Node + deps into one image, pushed to a registry
- same image runs on your laptop and on EC2Deploy (CD)EC2 pulls the image · or Terraform/ASG rolls new servers
- health check passes → old version retiredPM2 / container restartedzero-downtime reload
- Nginx keeps serving throughoutUserssee the new version — minutes after the push, no SSH
Put the two flows together and you can see the whole course in one sentence:
Code goes from your laptop to GitHub, to Actions, to a Docker image, to EC2. Terraform builds the EC2 servers inside a VPC. On each server, Nginx sits in front of a Node app run by PM2 or by Docker. The app talks to RDS, Redis and S3. Users arrive through Route 53 (DNS), then CloudFront, then the ALB, then that Nginx. CloudWatch watches all of it.
Who does what, and which lesson builds it
| Piece | Role | In one line | Lesson |
|---|---|---|---|
| Nginx | receptionist | The public door on ports 80 and 443. Ends TLS, sends requests to the app on port 3000, serves static files | Lesson 10 → |
| PM2 | caretaker | Keeps the Node process running. Restarts it after a crash or a reboot. Can reload with no downtime | Lesson 7 → |
| VPC | the building | Your private network in AWS. A public subnet faces the internet. A private subnet never does | Lesson 6 → |
| RDS | record room | PostgreSQL run by AWS. AWS does the backups, updates and failover. Lives in the private subnet only | Lesson 8 → |
| S3 | warehouse | Storage for files such as uploads, images, backups and built assets | Lesson 11 → |
| CloudFront | local depots | A CDN. It keeps copies of S3 and app responses at hundreds of locations near users | Lesson 11 → |
| ALB | traffic officer | One public address. Spreads requests over the servers that are healthy | Lesson 12 → |
| Auto Scaling | hiring manager | Adds servers when it is busy and removes them when it is quiet | Lesson 12 → |
| Redis | sticky notes | A store that keeps data in memory. Used for sessions, caches and rate limits | Lesson 16 → |
| Docker | shipping container | Packs the app, its runtime and its libraries into one image that runs the same everywhere | Lesson 9 → |
| GitHub Actions | the automation | Runs tests, builds and deploys on every push | Lesson 14 → |
| Terraform | the blueprint | Describes all the pieces above in files. One command builds them | Lesson 15 → |
| CloudWatch | the dashboard | Numbers, logs and alarms for every piece | Lesson 17 → |
None of these pieces is hard on its own. AWS feels hard when you meet all thirteen at once with no map. Now you have the map. When a lesson explains one piece in full, you will add detail to a shape you already know. If you forget a piece, the glossary tooltip on its name brings you back here.
Background reading for the AWS DevOps course. Best read after Lesson 4 and before Lesson 5.