What you'll build
SSH Key Pair
The cryptographic key that lets you log into your server securely
Security Group
A virtual firewall that only allows the traffic you specify
EC2 Instance
A real Linux server, running on Amazon's hardware, controlled by you
Nginx Web Server
The same web server software powering Netflix, Airbnb, GitHub
Live Web Page
A real public URL serving HTML you wrote
Bonus: Node API
A tiny dynamic API that proves you can do more than static
Architecture
Before you start — what you need
You should have completed Lab 1 first
Read this first
Lab 1 was about static hosting — drop files in a bucket, done. Lab 2 is the next level: you control the actual server. You'll SSH in like a real engineer, install software, and run it. This is what people mean when they say "I deployed it to the cloud".
A few moments will feel weird at first — like getting a "permission denied" on a file YOU just downloaded. That's normal. We'll explain everything as we go.
-
1Lab 1 completed You should have an active AWS account with billing alarm set. If not, do Lab 1 first — it teaches the basics we'll build on here.
-
2A working terminal You'll be typing SSH commands.Mac / Linux
Built-in Terminal app. You're set.Windows
Use WSL, Git Bash, or PowerShell. WSL recommended. -
3Find your current IP address Visit whatismyip.com and note it. We'll use it to lock SSH access to ONLY your laptop. AWS has a "My IP" button that auto-fills it.
EC2 t2.micro is FREE for 750 hours/month for 12 months. That's 31 days × 24 hours = 744 hours — enough to run ONE instance 24/7 all month. Don't run two at the same time — that doubles your hours and you'll get charged. Stop the instance when not using it.
✅ Ready to go when:
Create an SSH key pair
The "password" for your future server
What is an SSH key pair?
SSH (Secure Shell) is how you remotely log into a Linux server. Instead of using a password, AWS uses key pairs — two cryptographic files that work together: a public key (lives on the server) and a private key (stays on your laptop, NEVER shared). When you connect, the keys "shake hands" mathematically. Much more secure than a password.
-
1Navigate to EC2 → Key Pairs
-
2Click "Create key pair" (orange button)
-
3Click "Create key pair" Your browser will IMMEDIATELY download a file called
cloudwithshad-key.pem
AWS will NEVER let you download this file again. If you lose it, you permanently lose access to any server using it. You'd have to delete the server and start over.
Recommended: create a folder called ~/aws-keys/ and move the .pem file there. Don't put it in Downloads where you'll accidentally clear it.
✅ Check before moving on:
Create a security group
The firewall that decides who can reach your server
What is a security group?
A security group is a virtual firewall attached to your EC2 instance. By default, it blocks ALL inbound traffic. You explicitly allow what you need:
- Port 22 (SSH) — so YOU can log in from your laptop
- Port 80 (HTTP) — so visitors can see your website
It's STATEFUL — return traffic is automatically allowed. You only configure the inbound side.
-
1Go to Security Groups
-
2Click "Add rule" under Inbound rules
-
3Click "Add rule" again
SSH gives shell access — full control of the server. You don't want bots in Russia trying to brute-force in. Restricting to your IP means even if someone steals your .pem, they can't use it from a different network.
HTTP is for your visitors — they could be anywhere in the world, so we open it to everyone. The web server itself only serves the files you give it; no shell access.
-
4Leave Outbound rules as default Default = "all traffic to anywhere". The server needs to reach the internet to download Nginx etc.
-
5Click "Create security group"
✅ Check before moving on:
Launch your EC2 instance
Click a few buttons → real Linux server
-
1Go to EC2 → Instances
1 vCPU, 1 GB RAM. Tiny — but plenty for learning, testing, and small workloads. Real production apps use bigger instances (t3.large, m5.xlarge, etc.) but the launch process is identical.
-
2Click "Launch instance" AWS provisions the server in 1–2 minutes. Status will go from "Pending" → "Running" → and after about a minute, "Status check: 2/2 checks passed".
-
3Note the Public IPv4 address Once running, click on your instance → look for Public IPv4 address in the details panel. Looks like
54.123.45.67. Write it down.
✅ Check before moving on:
SSH into your server
The "I'm actually inside the cloud" moment
What's about to happen
You're going to open a terminal on your laptop in Accra → connect over SSH to a Linux server in (probably) Virginia → and your typing will run on that remote machine, not yours. The first time it works, it feels magical.
-
1Open your terminal and navigate to where the .pem is e.g.
cd ~/aws-keys
# SSH refuses to use a key that's readable by other users — security feature chmod 400 cloudwithshad-key.pem # Verify — should show: -r-------- 1 you you ... cloudwithshad-key.pem ls -la cloudwithshad-key.pem
# -i = identity file (your private key) ssh -i cloudwithshad-key.pem ec2-user@YOUR_PUBLIC_IP # Real example: ssh -i cloudwithshad-key.pem ec2-user@54.123.45.67
You'll see something like: "The authenticity of host '54.x.x.x' can't be established. Are you sure you want to continue connecting (yes/no)?". Type yes + Enter. This is normal — SSH is asking you to verify it's the right server. After you say yes, it remembers and won't ask again.
✅ What you should see after connecting
- An ASCII Amazon Linux banner
- Your prompt changes to something like:
[ec2-user@ip-172-31-x-x ~]$ - You're now inside the server — every command you type runs there
# What machine am I on? hostname # What OS? cat /etc/os-release # Where in the world is this server? curl http://169.254.169.254/latest/meta-data/placement/region # How much RAM and CPU does it have? free -h nproc
🔧 Common SSH problems
chmod 400 OR you're using the wrong username. For Amazon Linux always use ec2-user. (For Ubuntu it would be ubuntu.)chmod 400 cloudwithshad-key.pem first.✅ Check before moving on:
Install Nginx
The world's most popular web server
Why Nginx?
Nginx (pronounced "engine-X") is a web server that takes incoming HTTP requests and returns HTML pages. It powers Netflix, Airbnb, Cloudflare, GitHub, and most of the high-traffic web. It's free, fast, and easier to learn than Apache.
# Update the package manager (good habit on a fresh server) sudo dnf update -y # Install nginx sudo dnf install -y nginx # Start the service sudo systemctl start nginx # Make it auto-start on reboot sudo systemctl enable nginx # Verify it's running — should say "active (running)" sudo systemctl status nginx
sudo?sudo = "Super User DO". Linux protects system-level operations (installing software, starting services) by requiring elevated privileges. Putting sudo in front of a command says: "run this as the admin (root) user". It's a safety feature.
-
1Open a browser on your laptop
-
2Visit your server's public IP Just paste the public IP into the URL bar — no
https://needed (we're using port 80, plain HTTP).
✅ What you should see
- Default Nginx welcome page: "Welcome to nginx!"
- This means: your laptop hit your security group → which let port 80 through → which reached your EC2 → which Nginx answered with its default page
Most likely: you typed https:// — remove the s. We didn't set up HTTPS yet, only HTTP. OR check the security group has port 80 open from 0.0.0.0/0.
✅ Check before moving on:
Deploy your own HTML page
Replace the default Nginx page with something yours
How web servers serve files
Nginx looks in a specific folder on the server (called the "document root") whenever a request comes in. On Amazon Linux, that folder is /usr/share/nginx/html/. We replace the default index.html there with our own.
# Back up the default page (just in case) sudo mv /usr/share/nginx/html/index.html /usr/share/nginx/html/index.html.bak # Create your own (using a heredoc — multi-line input) sudo tee /usr/share/nginx/html/index.html > /dev/null <<'EOF' <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>My EC2 Server | cloudwithshad</title> <style> body { font-family: system-ui, sans-serif; background: #0A1628; color: white; max-width: 720px; margin: 60px auto; padding: 0 20px; line-height: 1.6; } h1 { color: #00B4D8; font-size: 42px; } .badge { background: #00B4D8; color: #0A1628; padding: 4px 10px; border-radius: 14px; font-size: 13px; font-weight: 700; } .info { background: rgba(255,255,255,0.05); padding: 16px; border-radius: 8px; margin-top: 20px; } code { background: rgba(255,255,255,0.1); padding: 2px 6px; border-radius: 3px; } </style> </head> <body> <span class="badge">LIVE FROM EC2</span> <h1>Hello from my AWS server!</h1> <p>This page is being served by Nginx running on Amazon Linux 2023, on a t2.micro EC2 instance.</p> <div class="info"> <p>Built in Week 1 of the cloudwithshad bootcamp by <strong>Your Name</strong>.</p> <p>Stack: <code>EC2</code> · <code>Nginx</code> · <code>Linux</code></p> </div> </body> </html> EOF # Reload Nginx to pick up the change (faster than restart) sudo systemctl reload nginx
tee and not nano?You CAN use sudo nano /usr/share/nginx/html/index.html if you prefer an editor — it works fine. The tee + heredoc approach lets us paste a whole file in one command, which is reproducible and copy-friendly. Both are valid.
-
1Refresh http://<your-IP> in your browser You should see your dark-themed page with "Hello from my AWS server!"
-
2Edit your name in the HTML and reload Nginx SSH session:
sudo nano /usr/share/nginx/html/index.html→ change "Your Name" → save (Ctrl+O, Enter, Ctrl+X) →sudo systemctl reload nginx→ refresh browser
S3 vs EC2 for static sites — both can do it. S3 is cheaper, simpler, scales automatically. EC2 is more flexible (run any code) but you manage the server yourself. The exam asks "which would you recommend for a static site with no backend?" — answer: S3.
✅ Check before moving on:
BONUS — Run a tiny Node.js API
Prove your server can do more than static files
This is optional but worth doing
So far you served static HTML — same as S3 could do. Let's prove the EC2 advantage by running an actual program: a tiny Node.js HTTP server that returns a different message every time you reload.
# Install Node.js (Amazon Linux 2023 has it as a package) sudo dnf install -y nodejs # Verify version node --version
cat > ~/app.js <<'EOF' const http = require('http'); const facts = [ "S3 stands for Simple Storage Service.", "EC2 stands for Elastic Compute Cloud.", "AWS has 30+ regions globally.", "CloudFront has 400+ edge locations.", "t2.micro has 1 vCPU and 1 GB RAM.", "IAM means Identity and Access Management." ]; http.createServer((req, res) => { const fact = facts[Math.floor(Math.random() * facts.length)]; res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: "Hello from Node.js on EC2!", random_aws_fact: fact, timestamp: new Date().toISOString() })); }).listen(3000); console.log('API listening on port 3000'); EOF # Run it (will block your terminal — that's fine) node ~/app.js
-
1In another browser tab, go to your security group
-
2Add a new rule Type: Custom TCP · Port: 3000 · Source: Anywhere-IPv4 (0.0.0.0/0) · Save
-
3Visit http://<your-IP>:3000 in your browser You should see JSON with a random AWS fact. Refresh — different fact. THAT'S a dynamic API.
✅ What you should see
- JSON response:
{"message":"Hello from Node.js on EC2!","random_aws_fact":"...","timestamp":"..."} - Each refresh shows a different fact
- Your terminal in SSH is showing the running Node process
You ran custom code on a server you control, listening on a port you exposed, returning data you generated. This is what an API is. Backend developers build apps exactly like this — just bigger. Frameworks like Express, Flask, FastAPI all do the same thing under the hood.
Press Ctrl+C in the SSH session where it's running. To run it in the background even after you log out, use nohup node ~/app.js & — but for learning, just keep the terminal open.
✅ Check:
Unlike S3 (which is basically free at small sizes), a running EC2 instance accumulates hours every minute. Free tier covers 750 hrs/month — but if you launch a second instance for any lab, you double-count. Safest: stop or terminate when done.
You can restart it later. Note: stopping gives a NEW public IP on restart.
Permanent. The instance is destroyed and you can't get it back. Most thorough cleanup.
You'll reuse
cloudwithshad-key in future labs. No charge for storing key pairs in AWS.After cleanup, go to EC2 → Instances and confirm: either (a) instance shows "Stopped" or "Terminated", and (b) no other running instances exist. If anything is "running", it's accumulating hours.
What's next — Lab 3 + Week 2
Lab 3 — Build the Architecture
Interactive quiz where you assemble Week 1's architecture by answering questions correctly
Week 2 — IAM Deep Dive
Users, roles, policies — the security backbone of every AWS account
Week 2 — Networking
VPCs, subnets, route tables — wire up a real production-style network