03 · EC2 Compute¶
EC2 (Elastic Compute Cloud) rents you virtual machines — "instances" — by the second. It's the most fundamental compute building block in AWS: almost every other compute service (ECS, EKS, even parts of Lambda's underlying infrastructure) ultimately runs on EC2 under the hood. This module covers launching an instance from the CLI, the security concepts that gate access to it (key pairs, security groups), choosing an image and size, and connecting to it over SSH.
Core concepts¶
| Concept | What it is |
|---|---|
| AMI (Amazon Machine Image) | A template: OS + pre-installed software an instance boots from. |
| Instance type | The hardware profile (vCPUs, memory, network) — e.g. t3.micro, m5.large. |
| Key pair | An SSH public/private key pair; AWS installs the public half on the instance, you keep the private half. |
| Security group | A virtual firewall attached to the instance, controlling inbound/outbound traffic by port and source. |
| Elastic IP | An optional static public IP you can attach to an instance (regular public IPs change if you stop/start). |
Create a key pair¶
aws ec2 create-key-pair \
--key-name training-key \
--query "KeyMaterial" \
--output text > training-key.pem
chmod 400 training-key.pem
chmod 400 is required — SSH refuses to use a private key file that other
users on your machine could read. There is no way to retrieve this private
key again from AWS if you lose it; you'd need to create a new key pair.
Create a security group¶
aws ec2 create-security-group \
--group-name training-sg \
--description "SSH and HTTP for training instances"
# {
# "GroupId": "sg-0123456789abcdef0"
# }
# Allow SSH only from your own IP (safer than 0.0.0.0/0)
MY_IP=$(curl -s https://checkip.amazonaws.com)
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp --port 22 \
--cidr "${MY_IP}/32"
# Allow HTTP from anywhere (for a web server you'll test in this module)
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp --port 80 \
--cidr 0.0.0.0/0
Security groups are stateful: a response to an allowed inbound request is automatically allowed back out, so you rarely need explicit outbound rules for typical request/response traffic.
Choose an AMI and instance type¶
# Find the latest Amazon Linux 2023 AMI for your region
aws ssm get-parameters \
--names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
--query "Parameters[0].Value" --output text
# ami-0abcdef1234567890
Common instance types you'll see in this course:
| Type | vCPU | Memory | Typical use | Free tier? |
|---|---|---|---|---|
t3.micro |
2 | 1 GiB | Small dev/test workloads | Yes (750 hrs/month for 12 months on new accounts) |
t3.small |
2 | 2 GiB | Light web apps | No |
m5.large |
2 | 8 GiB | General-purpose production | No |
Stick to t3.micro for this course to stay in the free tier.
Launch an instance¶
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t3.micro \
--key-name training-key \
--security-group-ids sg-0123456789abcdef0 \
--count 1 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=training-instance}]'
The response includes an InstanceId (e.g. i-0123456789abcdef0). Poll its
state:
aws ec2 describe-instances \
--instance-ids i-0123456789abcdef0 \
--query "Reservations[0].Instances[0].[State.Name,PublicIpAddress]" \
--output table
# Or wait for it to become reachable before connecting
aws ec2 wait instance-status-ok --instance-ids i-0123456789abcdef0
Connect via SSH¶
ec2-user is the default login for Amazon Linux AMIs (Ubuntu AMIs use
ubuntu). Once connected, this is a normal Linux box:
# On the instance
sudo yum install -y httpd
sudo systemctl start httpd
sudo systemctl enable httpd
echo "<h1>Hello from EC2</h1>" | sudo tee /var/www/html/index.html
Visit http://<PublicIpAddress> in a browser — you should see "Hello from
EC2" (this works because you opened port 80 to 0.0.0.0/0 in the security
group above).
Attaching an IAM role to an instance¶
Instead of putting access keys on the instance, attach the instance profile you created in the previous module so it can talk to AWS APIs (e.g. read S3) using temporary, auto-rotating credentials:
aws ec2 associate-iam-instance-profile \
--instance-id i-0123456789abcdef0 \
--iam-instance-profile Name=EC2-S3-ReadOnly-Profile
From inside the instance, any AWS SDK or the CLI automatically picks up
these credentials via the instance metadata service — no aws configure
needed on the instance itself.
Stopping vs. terminating¶
| Action | What happens | Billing |
|---|---|---|
| Stop | Instance shuts down, disk (EBS) persists, public IP is released (unless Elastic IP) | Stops compute billing; storage still billed |
| Terminate | Instance and its root EBS volume are deleted (unless "delete on termination" is disabled) | Stops all billing for this instance |
aws ec2 stop-instances --instance-ids i-0123456789abcdef0
aws ec2 start-instances --instance-ids i-0123456789abcdef0
aws ec2 terminate-instances --instance-ids i-0123456789abcdef0
Clean up when you're done
A running t3.micro is free-tier-eligible for the first 12 months on a
new account, but only up to 750 hours/month total — and only while
within that window. Terminate instances you're not actively using so you
don't get a surprise bill.
Cheat sheet¶
| Command | Purpose |
|---|---|
aws ec2 create-key-pair --key-name NAME |
Create an SSH key pair, prints the private key. |
aws ec2 create-security-group --group-name NAME --description DESC |
Create a firewall group. |
aws ec2 authorize-security-group-ingress --group-id ID --protocol tcp --port N --cidr CIDR |
Open a port. |
aws ec2 run-instances --image-id AMI --instance-type TYPE --key-name KEY --security-group-ids SG |
Launch an instance. |
aws ec2 describe-instances |
List/describe instances. |
aws ec2 wait instance-status-ok --instance-ids ID |
Block until an instance passes health checks. |
ssh -i KEY.pem ec2-user@IP |
Connect to an Amazon Linux instance. |
aws ec2 stop-instances / start-instances |
Pause/resume billing for compute (storage still billed). |
aws ec2 terminate-instances |
Delete the instance and its root volume. |
How It Actually Works¶
Launching an EC2 instance is not "starting a server" in the traditional
sense — it's a scheduling decision made by AWS's fleet-wide placement
system. When you call RunInstances, the request goes to a control-plane
service that picks a physical host in the target Availability Zone with
enough spare capacity for your instance type, taking into account the
underlying Nitro hypervisor architecture: on Nitro-based instance types,
nearly all traditional hypervisor work (networking, storage virtualization,
security) is offloaded to dedicated Nitro Cards (custom silicon), leaving a
minimal, stripped-down hypervisor on the host CPU. That's why modern instance
types get near bare-metal performance — your guest OS isn't sharing CPU
cycles with a heavyweight software hypervisor doing packet processing.
A security group is not a firewall rule list evaluated top-to-bottom like a traditional ACL — it's a stateful, allow-only object evaluated by the Nitro Card itself at the network-interface level, before packets ever reach your instance's virtual NIC. "Stateful" means only the initiating direction needs an explicit rule; return traffic is tracked and auto-permitted via connection tracking, which is also why security groups have no explicit-deny rules — only allows, unioned across every group attached to the interface.
Instance metadata (the 169.254.169.254 link-local address) is served by a
per-instance metadata service running on the Nitro hypervisor, not fetched
over the internet or even your VPC's routing — it's why IMDS calls work even
with no internet gateway, and why IMDSv2's session-token requirement (a
PUT before any GET) closes off SSRF attacks that could otherwise trick a
vulnerable app into leaking that role's temporary credentials.
Exercise¶
- Launch a
t3.microAmazon Linux instance with a security group that allows SSH from your IP and HTTP from anywhere. - SSH in, install and start
httpd, and serve a custom HTML page with your name on it. - Confirm you can view the page from your browser via the instance's public IP.
- Attach the
EC2-S3-ReadOnly-Profileinstance profile you created in the IAM module, then from inside the instance runaws s3 ls(install the CLI on the instance first if needed) — confirm it works with noaws configurestep, proving the role is supplying credentials. - Terminate the instance when you're done so it stops counting against your free-tier hours.