AWS EC2 Complete Guide — Launch, Scale, and Secure Virtual Machines

Sanjeev SharmaSanjeev Sharma
5 min read

Advertisement

Introduction

Why This Matters

Amazon EC2 (Elastic Compute Cloud) is the backbone of AWS compute infrastructure. Whether you are running a small web application or orchestrating hundreds of microservices, understanding EC2 instance types, networking, storage, and scaling directly impacts application performance, reliability, and cloud costs. EC2 underpins ECS, EKS, and many other AWS services.

Launching Instances

# Launch an EC2 instance
aws ec2 run-instances \
  --image-id ami-0c55b159cbfafe1f0 \
  --instance-type t3.micro \
  --key-name my-key-pair \
  --security-group-ids sg-0123456789abcdef0 \
  --subnet-id subnet-0123456789abcdef0 \
  --associate-public-ip-address \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-server},{Key=Environment,Value=production}]'
 
# List running instances
aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  --query 'Reservations[*].Instances[*].[InstanceId,PublicIpAddress,InstanceType,Tags[?Key==`Name`].Value|[0]]' \
  --output table
 
# Connect via SSH
ssh -i ~/.ssh/my-key.pem ec2-user@<public-ip>
 
# Stop and start (preserves EBS, releases public IP unless Elastic IP)
aws ec2 stop-instances --instance-ids i-1234567890abcdef0
aws ec2 start-instances --instance-ids i-1234567890abcdef0
 
# Terminate (permanent deletion)
aws ec2 terminate-instances --instance-ids i-1234567890abcdef0

Instance Types

FamilyUse CaseExample
t3/t4gGeneral purpose, burstableWeb apps, dev environments
m7iGeneral purpose, sustainedApplication servers
c7iCompute-optimizedCPU-intensive workloads
r7iMemory-optimizedIn-memory databases, caching
g4dnGPUML inference, graphics
i4iStorage-optimizedHigh IOPS databases
# Get current generation pricing
aws ec2 describe-instance-types \
  --instance-types t3.micro t3.small t3.medium \
  --query 'InstanceTypes[*].[InstanceType,VCpuInfo.DefaultVCpus,MemoryInfo.SizeInMiB]' \
  --output table

Security Groups

# Create security group
aws ec2 create-security-group \
  --group-name web-sg \
  --description "Web server security group" \
  --vpc-id vpc-0123456789abcdef0
 
# Allow HTTPS from internet
aws ec2 authorize-security-group-ingress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp \
  --port 443 \
  --cidr 0.0.0.0/0
 
# Allow SSH only from your IP
aws ec2 authorize-security-group-ingress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp \
  --port 22 \
  --cidr $(curl -s ifconfig.me)/32
 
# Allow app port from load balancer SG only
aws ec2 authorize-security-group-ingress \
  --group-id sg-app \
  --protocol tcp \
  --port 3000 \
  --source-group sg-alb

Elastic IPs and Networking

# Allocate Elastic IP (static public IP)
EIP=$(aws ec2 allocate-address --domain vpc --query 'AllocationId' --output text)
 
# Associate with instance
aws ec2 associate-address \
  --instance-id i-1234567890abcdef0 \
  --allocation-id $EIP
 
# Release Elastic IP (stop paying for unused EIPs)
aws ec2 release-address --allocation-id $EIP

User Data — Boot Scripts

# Launch with user data script (runs on first boot)
aws ec2 run-instances \
  --image-id ami-0c55b159cbfafe1f0 \
  --instance-type t3.small \
  --user-data file://user-data.sh
 
# user-data.sh
#!/bin/bash
yum update -y
yum install -y nodejs npm nginx
npm install -g pm2
 
mkdir -p /opt/app
cd /opt/app
git clone https://github.com/my-org/my-app .
npm ci --production
pm2 start server.js --name my-app
pm2 startup systemd -u ec2-user --hp /home/ec2-user
systemctl enable nginx
systemctl start nginx

Auto Scaling Groups

# Create launch template
aws ec2 create-launch-template \
  --launch-template-name web-lt \
  --version-description "v1" \
  --launch-template-data '{
    "ImageId": "ami-0c55b159cbfafe1f0",
    "InstanceType": "t3.small",
    "KeyName": "my-key",
    "SecurityGroupIds": ["sg-0123456789abcdef0"],
    "UserData": "'"$(base64 -w0 user-data.sh)"'"
  }'
 
# Create Auto Scaling Group
aws autoscaling create-auto-scaling-group \
  --auto-scaling-group-name web-asg \
  --launch-template LaunchTemplateName=web-lt,Version='$Latest' \
  --min-size 2 \
  --max-size 10 \
  --desired-capacity 3 \
  --vpc-zone-identifier "subnet-1,subnet-2,subnet-3" \
  --target-group-arns arn:aws:elasticloadbalancing:... \
  --health-check-type ELB \
  --health-check-grace-period 300
 
# Scale based on CPU
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'

Common Mistakes

  • Opening SSH (port 22) to 0.0.0.0/0 — restrict SSH to your IP or use AWS Systems Manager Session Manager instead
  • Not enabling detailed CloudWatch monitoring for Auto Scaling — default 5-minute intervals cause slow scale reactions
  • Using public subnets for application servers — place them in private subnets behind a load balancer
  • Forgetting to release Elastic IPs after instance termination — AWS charges for unassociated Elastic IPs
  • Using On-Demand pricing for long-running workloads — Reserved Instances or Savings Plans save 40-60%

Best Practices

  • Use Launch Templates (not Launch Configurations) for Auto Scaling Groups — they support versioning
  • Attach IAM roles to EC2 instances instead of embedding AWS credentials in user data or environment variables
  • Enable termination protection on critical instances to prevent accidental deletion
  • Use multiple Availability Zones for Auto Scaling Groups to survive AZ failures
  • Tag all instances consistently with project, environment, owner, and cost-center for billing visibility
  • Use AWS Systems Manager Patch Manager to automate OS patching without SSH access

Key Takeaways

  • EC2 instances are virtual machines — choose instance families based on CPU, memory, GPU, or storage workload characteristics
  • Security Groups act as stateful firewalls at the instance level — always apply least-privilege rules
  • Elastic IPs provide static public addresses — release them when unused to avoid charges
  • User data scripts run at first boot — ideal for bootstrapping software installation and configuration
  • Auto Scaling Groups automatically adjust instance count based on demand, health checks, or custom metrics
  • Spot Instances can reduce costs by up to 90% for fault-tolerant, interruptible workloads
  • Always place application servers in private subnets with a load balancer in public subnets for production
  • IAM instance profiles are the correct way to give EC2 instances permissions to call other AWS services

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading