What You'll Learn
A hands-on guide to launching, connecting, and managing AWS EC2 instances — covering instance types, security groups, key pairs, user data scripts, AMIs, and Auto Scaling.
What is EC2?
Amazon EC2 (Elastic Compute Cloud) provides resizable virtual servers — called instances — in the cloud. You can launch Linux or Windows instances in minutes, choose from hundreds of instance types, and pay only for what you use.
Launching EC2 via AWS CLI
# 1. Configure AWS CLI
aws configure
# AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
# AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Default region: us-east-1
# 2. Launch an EC2 instance
aws ec2 run-instances \
--image-id ami-0c02fb55956c7d316 \ # Amazon Linux 2023
--instance-type t3.micro \
--key-name my-keypair \
--security-group-ids sg-0123456789abcdef0 \
--subnet-id subnet-0123456789abcdef0 \
--iam-instance-profile Name=EC2-S3-Role \
--user-data file://userdata.sh \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=my-webserver},{Key=Env,Value=prod}]' \
--count 1
# 3. Check instance status
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=my-webserver" \
--query 'Reservations[*].Instances[*].[InstanceId,State.Name,PublicIpAddress]' \
--output table
# 4. Stop / Start / Terminate
aws ec2 stop-instances --instance-ids i-1234567890abcdef0
aws ec2 start-instances --instance-ids i-1234567890abcdef0
aws ec2 terminate-instances --instance-ids i-1234567890abcdef0
User Data Script — Bootstrap Your Instance
User data is a script that runs automatically when an instance first starts. Use it to install software, configure the system, and start services.
#!/bin/bash
set -e # Exit on error
# Update system packages
yum update -y
# Install Nginx
yum install -y nginx
systemctl enable nginx
systemctl start nginx
# Install Node.js 20 via NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
export NVM_DIR="/root/.nvm"
source "$NVM_DIR/nvm.sh"
nvm install 20
nvm use 20
# Install Docker
yum install -y docker
systemctl enable docker
systemctl start docker
usermod -aG docker ec2-user
# Pull and run the application
docker pull my-registry/myapp:latest
docker run -d \
--name myapp \
--restart unless-stopped \
-p 3000:3000 \
-e NODE_ENV=production \
my-registry/myapp:latest
# Configure Nginx as reverse proxy
cat > /etc/nginx/conf.d/myapp.conf << 'EOF'
server {
listen 80;
server_name _;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
EOF
systemctl reload nginx
# Signal success
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
echo "Bootstrap complete!" >> /var/log/userdata.log
Security Groups — Instance Firewall
# Create security group
aws ec2 create-security-group \
--group-name web-servers-sg \
--description "Security group for web servers" \
--vpc-id vpc-0123456789abcdef0
# Add inbound rules
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--ip-permissions \
'IpProtocol=tcp,FromPort=80,ToPort=80,IpRanges=[{CidrIp=0.0.0.0/0,Description="HTTP from internet"}]' \
'IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges=[{CidrIp=0.0.0.0/0,Description="HTTPS from internet"}]' \
'IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp=10.0.0.0/8,Description="SSH from VPN only"}]'
# Best Practice: Restrict SSH to your IP ONLY, never 0.0.0.0/0!
Auto Scaling Group + Launch Template
# 1. Create Launch Template
aws ec2 create-launch-template \
--launch-template-name MyAppTemplate \
--version-description "v1" \
--launch-template-data '{
"ImageId": "ami-0c02fb55956c7d316",
"InstanceType": "t3.micro",
"KeyName": "my-keypair",
"SecurityGroupIds": ["sg-0123456789abcdef0"],
"UserData": "'"$(base64 -w 0 userdata.sh)"'",
"IamInstanceProfile": {"Name": "EC2-Role"},
"Monitoring": {"Enabled": true}
}'
# 2. Create Auto Scaling Group
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name MyAppASG \
--launch-template LaunchTemplateName=MyAppTemplate,Version='$Latest' \
--min-size 2 \
--max-size 10 \
--desired-capacity 3 \
--availability-zones us-east-1a us-east-1b us-east-1c \
--target-group-arns arn:aws:elasticloadbalancing:...
# 3. Create scaling policy (scale up when CPU > 70%)
aws autoscaling put-scaling-policy \
--policy-name scale-up \
--auto-scaling-group-name MyAppASG \
--policy-type TargetTrackingScaling \
--target-tracking-configuration '{
"TargetValue": 70.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageCPUUtilization"
}
}'
Cost Optimization Tips
- Spot Instances — Up to 90% cheaper. Use for non-critical, fault-tolerant workloads.
- Reserved Instances — 40-60% savings for predictable workloads (1 or 3 year commitment).
- Savings Plans — Flexible alternative to RIs — commit to $/hour, apply across instance types.
- Right-sizing — Use AWS Compute Optimizer to find oversized instances.
- Stop dev instances — Schedule non-production instances to stop at night with AWS Instance Scheduler.
- S3 for static assets — Don't serve static files from EC2; use S3 + CloudFront instead.