Launch and manage virtual servers: pick an instance type and an AMI, attach EBS storage, give a server a fixed Elastic IP, bootstrap it with user-data scripts, and choose pricing options that cut costs.
EC2 (Elastic Compute Cloud) rents you virtual servers — "instances" — by the second. Why: it is the workhorse of AWS; you launch one, it boots in under a minute, and you pay only while it runs. Note: all the commands below assume the VPC, subnet, and security group from the VPC lesson.
List the AWS regions available to you (servers are launched per-region)
aws ec2 describe-regions --query 'Regions[].RegionName' --output textA Region is a geographic area (e.g. us-east-1 = N. Virginia); inside it are Availability Zones (AZs) — separate data centers. Why it matters: pick a region close to your users for low latency, and spread servers across AZs so one data-center failure does not take you down.
See the AZs in your current region
aws ec2 describe-availability-zones \
--query 'AvailabilityZones[].ZoneName' --output textSet your default region for this session (overrides aws configure)
export AWS_DEFAULT_REGION=us-east-1An instance type (e.g. t3.micro, m5.large) sets the CPU, memory, and network of a server. The letter is the family (t = burstable/cheap, m = balanced, c = compute, r = memory). Why: match the workload — a small website fits t3.micro; a database needs an r-family. Bigger = more capable and more expensive.
List general-purpose instance types and their vCPU / memory
aws ec2 describe-instance-types \
--filters Name=instance-type,Values=t3.* \
--query 'InstanceTypes[].[InstanceType,VCpuInfo.DefaultVCpus,MemoryInfo.SizeInMiB]' \
--output tableThe cheap t3/t2 instances run on "CPU credits": they earn credits while idle and spend them to burst above a baseline when busy. Why know this: if a t3 server is constantly maxed out it runs out of credits and slows down — a sign you need a bigger type, or "unlimited" mode (which bills for the overage).
Watch the CPU credit balance of a running t-family instance (CloudWatch metric — covered fully in the CloudWatch lesson)
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 --metric-name CPUCreditBalance \
--dimensions Name=InstanceId,Value=i-0123456789abcdef0 \
--start-time 2024-01-01T00:00:00Z --end-time 2024-01-01T01:00:00Z \
--period 300 --statistics AverageAn AMI (Amazon Machine Image) is a snapshot of an operating system (plus any pre-installed software) that a new instance copies onto its disk at launch. Why: choose an AMI to decide what your server starts as — plain Amazon Linux, Ubuntu, or your own customized image.
Find the latest Amazon Linux 2023 AMI id (kept in a public parameter)
AMI_ID=$(aws ssm get-parameter \
--name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
--query 'Parameter.Value' --output text)echo "Will boot from $AMI_ID"Now combine everything: an AMI, an instance type, a subnet, a security group, and a key pair (for SSH). Why a key pair: AWS injects your public key so you can log in; you keep the private half. This single command creates a real, billable server.
Create an SSH key pair and save the private key locally
aws ec2 create-key-pair --key-name my-key \
--query 'KeyMaterial' --output text > my-key.pemchmod 400 my-key.pemLaunch one t3.micro in the public subnet with the web security group
aws ec2 run-instances \
--image-id $AMI_ID \
--instance-type t3.micro \
--key-name my-key \
--subnet-id $PUBLIC_SUBNET \
--security-group-ids $SG_ID \
--count 1User data is a script AWS runs once, the first time an instance boots — the standard way to install software automatically. Why: instead of SSHing in to set up each server by hand, you bake the setup into the launch so servers come up ready to serve.
Save this as bootstrap.sh — it installs and starts a web server on first boot
#!/bin/bash
dnf install -y httpd
systemctl enable --now httpd
echo "Hello from $(hostname)" > /var/www/html/index.htmlPass it at launch with --user-data
aws ec2 run-instances --image-id $AMI_ID --instance-type t3.micro \
--subnet-id $PUBLIC_SUBNET --security-group-ids $SG_ID --count 1 \
--user-data file://bootstrap.shEBS (Elastic Block Store) is durable disk that attaches to an instance — your data survives a reboot. Why separate from the instance: you can detach a volume and reattach it elsewhere, resize it, or snapshot it for backup. gp3 is the modern general-purpose type.
Create a 20 GB gp3 volume in the same AZ as your instance
VOL_ID=$(aws ec2 create-volume --size 20 --volume-type gp3 \
--availability-zone us-east-1a --query 'VolumeId' --output text)Attach it to an instance as /dev/sdf
aws ec2 attach-volume --volume-id $VOL_ID \
--instance-id i-0123456789abcdef0 --device /dev/sdfBack it up with a point-in-time snapshot
aws ec2 create-snapshot --volume-id $VOL_ID \
--description "nightly backup"A normal public IP changes every time you stop and start an instance. An Elastic IP (EIP) is a static address you own and can re-point. Why: so DNS records and firewall allow-lists keep working across restarts. Note: an EIP is free while attached to a running instance, but billed while unattached.
Allocate an Elastic IP
ALLOC=$(aws ec2 allocate-address --domain vpc \
--query 'AllocationId' --output text)Point it at your instance
aws ec2 associate-address \
--instance-id i-0123456789abcdef0 --allocation-id $ALLOCWhen done, release it so you stop paying for an idle address aws ec2 release-address --allocation-id $ALLOC
How you pay changes the price a lot. On-Demand: pay per second, no commitment (default). Spot: spare capacity up to ~90% cheaper, but AWS can reclaim it with 2 minutes' notice (great for batch jobs). Savings Plans / Reserved: commit to 1–3 years for a big discount on steady workloads.
Launch a Spot instance — same as on-demand plus a market option
aws ec2 run-instances --image-id $AMI_ID --instance-type t3.micro \
--subnet-id $PUBLIC_SUBNET --security-group-ids $SG_ID --count 1 \
--instance-market-options 'MarketType=spot'Check the current Spot price history for a type
aws ec2 describe-spot-price-history --instance-types t3.micro \
--product-descriptions "Linux/UNIX" --max-results 1