AWS EC2 — Instances, AMIs, Storage, Auto Scaling, and Pricing
A full walkthrough of AWS EC2 — instance types, AMIs, pricing models, storage options, Auto Scaling Groups, placement groups, and Systems Manager
What is EC2?
Amazon EC2 (Elastic Compute Cloud) is AWS’s virtual machine service. It gives you resizable compute capacity in the cloud — you can launch a server in minutes, scale it up or down, and pay only for what you use.
EC2 is the foundation of most AWS workloads. Even when you use higher-level services like ECS, EKS, or Elastic Beanstalk, EC2 instances are often running underneath.
Instance Types
EC2 instances come in families, each optimised for a different workload. The naming convention is: family + generation + size — e.g. t3.medium, c6i.xlarge, r5.2xlarge.
| Family | Purpose | Example Use Case |
|---|---|---|
| T (Burstable) | Low-cost general purpose, burstable CPU | Dev/test, small websites |
| M (General) | Balanced CPU and memory | Web servers, app servers |
| C (Compute) | High CPU-to-memory ratio | Batch processing, gaming, ML inference |
| R (Memory) | High memory-to-CPU ratio | Databases, in-memory caches |
| X (Memory Extreme) | Largest memory instances | SAP HANA, in-memory databases |
| I (Storage) | High I/O NVMe SSD | NoSQL databases, data warehousing |
| D (Dense Storage) | High HDD storage capacity | Hadoop, HDFS, data lakes |
| P / G (Accelerated) | GPU | ML training, video rendering, HPC |
| Inf (Inferentia) | AWS custom ML chip | ML inference at low cost |
Sizes go from nano → micro → small → medium → large → xlarge → 2xlarge → … → 48xlarge (and metal for bare-metal access).
📸 SCREENSHOT: EC2 → Launch Instance → Instance Type. Show the instance type selector with filter by family (e.g. “t3”) and the columns showing vCPUs, Memory, and On-Demand price.
1
2
3
4
5
# List available instance types in your region
aws ec2 describe-instance-types \
--filters "Name=instance-type,Values=t3.*" \
--query 'InstanceTypes[*].[InstanceType,VCpuInfo.DefaultVCpus,MemoryInfo.SizeInMiB]' \
--output table
T-type Burstable Instances — CPU Credits
T2 and T3 instances earn CPU credits when CPU usage is below baseline. They spend credits when CPU spikes above baseline. If credits run out, the instance throttles to baseline CPU. Use t3.unlimited mode to burst beyond credits (you pay extra) — useful for unpredictable workloads.
AMIs — Amazon Machine Images
An AMI is a template for launching EC2 instances. It contains the operating system, software, and configuration. Every EC2 instance is launched from an AMI.
AMI types
| Type | Description |
|---|---|
| AWS-provided | Amazon Linux 2/2023, Ubuntu, Windows Server, Red Hat |
| AWS Marketplace | Third-party software (pre-configured databases, firewalls, etc.) |
| Community | Public AMIs shared by other users (use with caution) |
| Custom (private) | AMIs you create from your own running instances |
📸 SCREENSHOT: EC2 → AMIs → My AMIs. Show a list of your own AMIs with status (available), creation date, and the source instance ID.
1
2
3
4
5
6
7
8
9
10
11
12
# List your own AMIs
aws ec2 describe-images --owners self --output table
# Find the latest Amazon Linux 2023 AMI in your region
aws ec2 describe-images \
--owners amazon \
--filters \
"Name=name,Values=al2023-ami-*" \
"Name=architecture,Values=x86_64" \
"Name=state,Values=available" \
--query 'sort_by(Images, &CreationDate)[-1].[ImageId,Name,CreationDate]' \
--output table
Creating a Custom AMI
Creating an AMI from a running instance snapshots the root volume and all attached EBS volumes. Use this to bake your application dependencies into a base image — faster launch times and consistent environments.
1
2
3
4
5
6
7
8
9
10
11
12
13
# Create an AMI from a running instance
aws ec2 create-image \
--instance-id i-0abc123def456 \
--name "my-app-v1.2-$(date +%Y%m%d)" \
--description "Application server with dependencies pre-installed" \
--no-reboot
# Copy AMI to another region (for multi-region deployment)
aws ec2 copy-image \
--source-region eu-west-1 \
--source-image-id ami-0abc123 \
--name "my-app-v1.2-us-east-1" \
--region us-east-1
Instance Lifecycle
1
2
3
4
5
Launch → Pending → Running → Stopping → Stopped → Terminated
│ │
└── Rebooting ───────┘
│
└── Hibernating → Stopped (RAM preserved)
| State | Billing |
|---|---|
| Pending | Not billed |
| Running | Billed |
| Stopping | Not billed |
| Stopped | Not billed for compute (EBS still billed) |
| Terminated | Not billed — instance deleted permanently |
Stopping and starting moves the instance to a new physical host. The instance gets a new public IP (unless you use an Elastic IP). Rebooting keeps the same host and public IP.
Launching an Instance
📸 SCREENSHOT: EC2 → Launch Instance wizard. Show the Name field, AMI selector, instance type dropdown, key pair selector, and the Network Settings section with VPC/subnet/SG configuration.
1
2
3
4
5
6
7
8
9
10
# Launch an instance
aws ec2 run-instances \
--image-id ami-0abc123 \
--instance-type t3.micro \
--key-name my-key-pair \
--security-group-ids sg-0abc123 \
--subnet-id subnet-private-1a \
--iam-instance-profile Name=my-instance-profile \
--user-data file://bootstrap.sh \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-server},{Key=Environment,Value=prod}]'
Key Pairs
A key pair is used for SSH access to Linux instances (or to decrypt the Windows admin password). AWS stores the public key; you keep the private key. If you lose the private key, you lose SSH access.
1
2
3
4
5
6
7
8
9
10
11
# Create a key pair
aws ec2 create-key-pair \
--key-name my-key-pair \
--query 'KeyMaterial' \
--output text > my-key-pair.pem
chmod 400 my-key-pair.pem
# SSH to an instance
ssh -i my-key-pair.pem ec2-user@<public-ip> # Amazon Linux
ssh -i my-key-pair.pem ubuntu@<public-ip> # Ubuntu
User Data — Bootstrap Scripts
User data runs once on first launch (as root). Use it to install packages, configure the instance, pull application code.
1
2
3
4
5
6
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "Hello from $(hostname)" > /var/www/html/index.html
Pricing Models
| Model | Use Case | Savings vs On-Demand |
|---|---|---|
| On-Demand | Unpredictable workloads, short-term | Baseline |
| Reserved (1yr) | Steady-state, 1-year commitment | ~40% |
| Reserved (3yr) | Long-term workloads | ~60% |
| Savings Plans | Flexible — commit to $/hr spend | ~66% |
| Spot | Fault-tolerant, interruptible workloads | ~90% |
| Dedicated Host | Compliance, bring-your-own-license | — |
| Dedicated Instance | Isolation on dedicated hardware | — |
Spot Instances
Spot instances use unused EC2 capacity at up to 90% discount. AWS can interrupt them with 2 minutes notice when capacity is needed. Design for interruption: use Spot for stateless workers, batch jobs, and CI/CD runners. Never use Spot for databases or stateful workloads.
1
2
3
4
5
# Request a Spot instance
aws ec2 run-instances \
--instance-type c5.xlarge \
--image-id ami-0abc123 \
--instance-market-options 'MarketType=spot,SpotOptions={MaxPrice=0.05,SpotInstanceType=one-time}'
Storage
Instance Store
Local NVMe SSD directly attached to the physical host. Ephemeral — data is lost when the instance stops, terminates, or fails. Very fast — hundreds of thousands of IOPS. Use for temporary data, buffers, caches, scratch space.
EBS (Elastic Block Store)
Network-attached block storage that persists independently of the instance. Can be detached from one instance and attached to another. Supports snapshots to S3.
| Volume Type | Use Case | IOPS | Throughput |
|---|---|---|---|
| gp3 | General purpose SSD (default) | Up to 16,000 | Up to 1,000 MB/s |
| gp2 | Older general purpose (avoid for new) | Up to 16,000 | Up to 250 MB/s |
| io2 Block Express | Critical databases (SAP, Oracle) | Up to 256,000 | Up to 4,000 MB/s |
| io1 | Databases needing consistent IOPS | Up to 64,000 | Up to 1,000 MB/s |
| st1 | Sequential read HDD (logs, ETL) | 500 max | 500 MB/s |
| sc1 | Cold HDD (infrequent access) | 250 max | 250 MB/s |
gp3 is the current default — choose it over gp2 for new workloads. You can set IOPS and throughput independently on gp3 (not the case with gp2 where IOPS scales with size).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Create a gp3 volume
aws ec2 create-volume \
--volume-type gp3 \
--size 100 \
--iops 6000 \
--throughput 300 \
--availability-zone eu-west-1a
# Attach to an instance
aws ec2 attach-volume \
--volume-id vol-0abc123 \
--instance-id i-0abc123 \
--device /dev/sdf
# Create a snapshot (backup)
aws ec2 create-snapshot \
--volume-id vol-0abc123 \
--description "Daily backup $(date +%Y-%m-%d)"
Elastic IP
A public IPv4 address that you own until you release it. Survives instance stop/start. Free while attached to a running instance — you are charged when it is allocated but not attached.
1
2
3
4
5
6
7
# Allocate an Elastic IP
aws ec2 allocate-address --domain vpc
# Associate with an instance
aws ec2 associate-address \
--instance-id i-0abc123 \
--allocation-id eipalloc-0abc123
Placement Groups
Placement groups control how EC2 instances are physically placed.
| Type | Purpose | Use Case |
|---|---|---|
| Cluster | All instances on same rack — low latency, high bandwidth | HPC, tightly coupled apps |
| Spread | Instances on different racks — max fault isolation | Small critical instances (max 7 per AZ) |
| Partition | Groups of instances on separate racks | Kafka, HDFS, Cassandra |
1
2
3
4
# Create a cluster placement group
aws ec2 create-placement-group \
--group-name hpc-cluster \
--strategy cluster
Auto Scaling Groups
An Auto Scaling Group (ASG) automatically adjusts the number of EC2 instances based on demand. You define a minimum, desired, and maximum count. The ASG replaces unhealthy instances automatically.
📸 SCREENSHOT: EC2 → Auto Scaling Groups → Create Auto Scaling Group. Show the launch template selection, VPC/subnet selection, and the capacity settings (Min, Desired, Max).
Launch Template
A launch template defines the configuration for each instance the ASG launches — AMI, instance type, key pair, security groups, user data.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Create a launch template
aws ec2 create-launch-template \
--launch-template-name web-server-lt \
--launch-template-data '{
"ImageId": "ami-0abc123",
"InstanceType": "t3.medium",
"KeyName": "my-key-pair",
"SecurityGroupIds": ["sg-0abc123"],
"IamInstanceProfile": {"Name": "web-instance-profile"},
"UserData": "base64-encoded-script"
}'
# Create the ASG
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name web-asg \
--launch-template LaunchTemplateName=web-server-lt,Version='$Latest' \
--min-size 2 \
--desired-capacity 4 \
--max-size 10 \
--vpc-zone-identifier "subnet-1a,subnet-1b,subnet-1c" \
--target-group-arns arn:aws:elasticloadbalancing:...:targetgroup/web-tg/abc123
Scaling Policies
| Policy Type | How It Works |
|---|---|
| Target Tracking | Maintain a target metric (e.g. 60% CPU) — ASG adjusts automatically |
| Step Scaling | Scale by fixed amounts when metric crosses thresholds |
| Scheduled Scaling | Scale at specific times (e.g. scale up at 8am, down at 8pm) |
| Predictive Scaling | ML-based, forecasts load and scales proactively |
1
2
3
4
5
6
7
8
9
10
11
# Target tracking — keep average CPU at 60%
aws autoscaling put-scaling-policy \
--auto-scaling-group-name web-asg \
--policy-name cpu-target-tracking \
--policy-type TargetTrackingScaling \
--target-tracking-configuration '{
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageCPUUtilization"
},
"TargetValue": 60.0
}'
📸 SCREENSHOT: EC2 → Auto Scaling Groups → select ASG → Automatic Scaling tab. Show the scaling policies listed with type (Target tracking), metric, and target value.
Systems Manager Session Manager
Session Manager gives you shell access to EC2 instances without opening port 22, without key pairs, and without a bastion host. The instance runs the SSM Agent and communicates outbound to the SSM service. All sessions are logged to CloudWatch or S3.
Requirements:
- SSM Agent installed (pre-installed on Amazon Linux 2/2023, Ubuntu 20.04+)
- IAM instance profile with
AmazonSSMManagedInstanceCorepolicy - No inbound security group rules needed
1
2
3
4
5
6
7
8
# Start a session
aws ssm start-session --target i-0abc123
# Run a command on all instances with a tag
aws ssm send-command \
--targets "Key=tag:Environment,Values=prod" \
--document-name "AWS-RunShellScript" \
--parameters 'commands=["yum update -y"]'
📸 SCREENSHOT: EC2 → Instances → select instance → Connect → Session Manager tab. Show the “Connect” button and the browser-based terminal session that opens.
Quick Reference
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Instances
aws ec2 describe-instances --output table
aws ec2 describe-instances --filters "Name=tag:Environment,Values=prod" --output table
# Start / stop / terminate
aws ec2 start-instances --instance-ids i-0abc123
aws ec2 stop-instances --instance-ids i-0abc123
aws ec2 terminate-instances --instance-ids i-0abc123
# Get instance public IP
aws ec2 describe-instances \
--instance-ids i-0abc123 \
--query 'Reservations[0].Instances[0].PublicIpAddress' \
--output text
# List running instances with Name tag
aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" \
--query 'Reservations[*].Instances[*].[InstanceId,InstanceType,Tags[?Key==`Name`].Value|[0],PublicIpAddress]' \
--output table
# ASG operations
aws autoscaling describe-auto-scaling-groups --output table
aws autoscaling set-desired-capacity --auto-scaling-group-name web-asg --desired-capacity 6