Tarek Cheikh
Founder & AWS Cloud Architect
In the previous articles, we covered IAM, credentials, and Vault. All of that infrastructure exists to secure something — and that something is usually compute. Amazon EC2 (Elastic Compute Cloud) is where most AWS journeys begin: it lets you launch virtual servers in minutes and pay only for what you use.
This article walks through the complete process of launching your first EC2 instance, connecting to it, running a web server, and understanding the operational basics: security groups, key pairs, monitoring, and cost control.
EC2 provides virtual servers (called instances) running in AWS data centers. Instead of buying and maintaining physical hardware, you rent compute capacity on demand. An instance can be launched in under a minute, run for as long as you need, and be terminated when you are done. You are billed per second of usage, with a 60-second minimum.
Each instance runs a full operating system (Linux, Windows, or macOS) and behaves like a standalone server. You get root or administrator access and can install any software. The difference from a physical server is that EC2 instances are created from templates, can be duplicated instantly, and their capacity can be changed without physical intervention.
Every EC2 instance is built from four components:
AWS offers hundreds of instance types organized into families. The naming convention encodes the family, generation, and size:
m5.xlarge = m (family: general purpose) + 5 (generation) + xlarge (size)
Higher generations offer better performance per dollar. Always prefer the latest generation available in your region.
General Purpose (T, M series) — balanced compute, memory, and networking. Use for web servers, small databases, development environments.
t3.nano 1 vCPU 0.5 GB RAM ~$3.80/month
t3.micro 1 vCPU 1 GB RAM ~$7.59/month Free tier eligible
t3.small 1 vCPU 2 GB RAM ~$15.18/month
t3.medium 2 vCPU 4 GB RAM ~$30.37/month
m5.large 2 vCPU 8 GB RAM ~$70/month
T-series instances are burstable: they accumulate CPU credits during idle periods and spend them during traffic spikes. This makes them cost-effective for workloads with variable CPU demand.
Compute Optimized (C series) — high-performance CPUs for compute-intensive tasks. Use for batch processing, scientific modeling, high-performance web servers, gaming servers.
Memory Optimized (R, X series) — large memory capacity for data-intensive workloads. Use for in-memory databases (Redis, Memcached), real-time analytics, data processing.
Storage Optimized (I, D series) — high sequential read/write access to large datasets on local storage. Use for distributed file systems, data warehousing, log processing.
PENDING --> RUNNING --> STOPPING --> STOPPED --> TERMINATING --> TERMINATED
(billed) (not billed) (gone)
Important: data on instance store volumes is lost when an instance stops or terminates. Only EBS-backed data persists across stops.
New AWS accounts get 750 hours per month of t3.micro (or t2.micro, depending on region) for the first 12 months. That is enough to run one instance continuously for free. The 750 hours are shared across all t3.micro/t2.micro instances in the account — running two instances simultaneously uses 2 hours per hour of wall clock time.
After the free tier expires, a t3.micro costs approximately $0.0104/hour ($7.59/month for 24/7 operation).
ec2:RunInstances, ec2:DescribeInstances, ec2:CreateKeyPair, ec2:CreateSecurityGroup, ec2:AuthorizeSecurityGroupIngress, ec2:StartInstances, ec2:StopInstances, ec2:TerminateInstances, ec2:CreateTags)An AMI (Amazon Machine Image) is a template that contains the operating system and pre-installed software. When you launch an instance, AWS creates a copy of the AMI on the instance's storage volume.
Common choices:
For this tutorial, use Amazon Linux 2023 (free tier eligible, AWS-optimized).
Select t3.micro (1 vCPU, 1 GB RAM). It is free tier eligible and sufficient for learning and small applications.
Storage types available:
Tags are key-value pairs for organizing and tracking resources. At minimum, add:
Name MyFirstInstance
Environment Learning
Owner your-name
Tags are critical for cost tracking (filter your AWS bill by tag), automation (scripts that stop all "Development" instances at night), and organizational compliance.
A security group is a stateful firewall that controls inbound and outbound traffic to your instance. By default, all inbound traffic is blocked and all outbound traffic is allowed.
Create a new security group with these rules:
Inbound Rules:
SSH TCP 22 Your IP (/32) "SSH access from my location"
HTTP TCP 80 Your IP (/32) "HTTP access for testing"
Never open SSH (port 22) to 0.0.0.0/0. Automated bots continuously scan the internet for open SSH ports and will begin brute-force attempts within minutes. Always restrict SSH to your specific IP address or a known range.
Security group properties:
A typical multi-tier architecture uses chained security groups:
Web-Tier-SG: Port 80/443 from internet (via load balancer)
App-Tier-SG: Port 8080 from Web-Tier-SG only
Database-Tier-SG: Port 3306 from App-Tier-SG only
Each tier can only communicate with its adjacent tier, limiting the blast radius of a compromise.
Key pairs provide SSH access to your instance using public-key cryptography instead of passwords.
Create a key pair named MyFirstKeyPair (RSA, .pem format). The private key file downloads immediately and cannot be downloaded again. If you lose it, you lose SSH access to the instance.
# Move the key to your SSH directory
mv ~/Downloads/MyFirstKeyPair.pem ~/.ssh/
# Set permissions - SSH refuses keys with loose permissions
chmod 400 ~/.ssh/MyFirstKeyPair.pem
The chmod 400 sets the file to read-only by the owner. SSH will reject key files that are readable by group or others, as a security measure against accidental exposure.
Review the configuration and click Launch. The instance transitions from Pending to Running in 30-60 seconds.
# Connect to your instance
ssh -i ~/.ssh/MyFirstKeyPair.pem ec2-user@YOUR-INSTANCE-PUBLIC-IP
# First connection asks to verify the host fingerprint - type "yes"
Find the public IP in the EC2 console: select your instance and look for "Public IPv4 address" in the details panel.
Use Windows Subsystem for Linux (WSL) with the same SSH command above, or use PuTTY with the key converted to .ppk format via PuTTYgen.
Each AMI type has a different default username:
Amazon Linux 2 / 2023: ec2-user
Ubuntu: ubuntu
Debian: admin
CentOS: centos
RHEL: ec2-user
SUSE: ec2-user
Windows: Administrator (via RDP, not SSH)
Using the wrong username is the most common SSH connection failure.
If you cannot connect:
chmod 400 ~/.ssh/MyFirstKeyPair.pemOnce connected, explore your instance:
# System information
uname -a # Kernel and architecture
cat /proc/cpuinfo | grep processor | wc -l # Number of vCPUs
free -h # Memory usage
df -h # Disk usage
# Package management (Amazon Linux 2023 uses dnf)
sudo dnf update -y # Update all packages
sudo dnf install -y git wget curl htop # Install common tools
# Package management (Amazon Linux 2 uses yum)
sudo yum update -y
sudo yum install -y git wget curl htop
Let us deploy a simple web server to verify your instance is accessible from the network.
# Amazon Linux 2023
sudo dnf install -y httpd
sudo systemctl start httpd
sudo systemctl enable httpd
# Amazon Linux 2
sudo yum install -y httpd
sudo systemctl start httpd
sudo systemctl enable httpd
# Verify Apache is running
sudo systemctl status httpd
sudo tee /var/www/html/index.html > /dev/null <<'EOF'
<!DOCTYPE html>
<html>
<head>
<title>My First EC2 Web Server</title>
<style>
body { font-family: sans-serif; text-align: center; padding: 50px; }
h1 { color: #232f3e; }
</style>
</head>
<body>
<h1>My First EC2 Instance</h1>
<p>This page is served from an Amazon EC2 instance.</p>
</body>
</html>
EOF
Open your browser and navigate to http://YOUR-INSTANCE-PUBLIC-IP. If you see your page, the instance, Apache, and security group are all working correctly.
If the page does not load:
# Verify Apache is listening
sudo ss -tlnp | grep :80
# Test locally on the instance
curl http://localhost
# Check the security group allows HTTP from your IP
aws ec2 describe-security-groups --group-ids sg-your-id
# Regular updates
sudo dnf update -y
# Enable automatic security updates (Amazon Linux 2023)
sudo dnf install -y dnf-automatic
sudo systemctl enable --now dnf-automatic-install.timer
# Disable password authentication (key-only access)
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
# Disable root login
sudo sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
# Restart SSH
sudo systemctl restart sshd
Fail2ban monitors log files for repeated authentication failures and temporarily blocks the offending IP addresses:
# Amazon Linux 2023
sudo dnf install -y fail2ban
# Amazon Linux 2
sudo amazon-linux-extras install epel -y
sudo yum install -y fail2ban
# Configure
sudo tee /etc/fail2ban/jail.local > /dev/null <<'EOF'
[DEFAULT]
bantime = 600
findtime = 600
maxretry = 3
[sshd]
enabled = true
port = ssh
logpath = /var/log/secure
EOF
sudo systemctl enable --now fail2ban
With this configuration, any IP address that fails SSH authentication 3 times within 10 minutes is blocked for 10 minutes.
# List running instances
aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" \
--query "Reservations[].Instances[].{ID:InstanceId,Type:InstanceType,IP:PublicIpAddress,Name:Tags[?Key=='Name']|[0].Value}" \
--output table
# Stop an instance (preserves EBS, stops billing for compute)
aws ec2 stop-instances --instance-ids i-1234567890abcdef0
# Start a stopped instance
aws ec2 start-instances --instance-ids i-1234567890abcdef0
# Reboot
aws ec2 reboot-instances --instance-ids i-1234567890abcdef0
# Terminate (permanent deletion)
aws ec2 terminate-instances --instance-ids i-1234567890abcdef0
CloudWatch automatically collects metrics from every EC2 instance at no additional cost (basic monitoring at 5-minute intervals).
View metrics in the EC2 console by selecting your instance and clicking the Monitoring tab.
# Create an alarm that triggers when CPU exceeds 80% for 10 minutes
aws cloudwatch put-metric-alarm \
--alarm-name "High-CPU-MyFirstInstance" \
--alarm-description "CPU exceeds 80 percent" \
--metric-name CPUUtilization \
--namespace AWS/EC2 \
--statistic Average \
--period 300 \
--threshold 80 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 2 \
--dimensions Name=InstanceId,Value=i-1234567890abcdef0 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:my-alerts
This alarm checks CPU utilization every 5 minutes. If the average exceeds 80% for two consecutive periods (10 minutes), it sends a notification via SNS.
If your instance consistently uses less than 40% CPU, consider a smaller instance type. If it regularly exceeds 80%, consider a larger one. CloudWatch metrics provide this data.
If you are not using an instance, stop it to preserve the EBS volume while halting compute charges. Terminate only when you no longer need the data.
Set up billing alerts to avoid surprises:
# Create SNS topic for billing notifications
aws sns create-topic --name billing-alerts
# Subscribe your email
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:123456789012:billing-alerts \
--protocol email \
--notification-endpoint your-email@example.com
# Create billing alarm at $10
aws cloudwatch put-metric-alarm \
--alarm-name "Monthly-Billing-Alert" \
--alarm-description "Monthly bill exceeds 10 USD" \
--metric-name EstimatedCharges \
--namespace AWS/Billing \
--statistic Maximum \
--period 86400 \
--threshold 10 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:billing-alerts
For learning accounts, set the threshold at $5-10. Create multiple alarms at 50%, 80%, and 100% of your budget for progressive warnings.
An AMI (Amazon Machine Image) captures the complete state of your instance, including the operating system, installed software, and configuration. You can launch new instances from an AMI to create exact copies.
# Create an AMI from a running instance
aws ec2 create-image \
--instance-id i-1234567890abcdef0 \
--name "MyFirstInstance-Backup-$(date +%Y%m%d)" \
--description "Backup of my first EC2 instance"
Use cases: disaster recovery (launch a replacement if the original fails), horizontal scaling (launch multiple copies behind a load balancer), and environment replication (create staging from production).
sudo systemctl status httpd)# Check disk usage
df -h
# Find large files
sudo find / -type f -size +100M 2>/dev/null
# Resize EBS volume (requires extending the filesystem after)
aws ec2 modify-volume --volume-id vol-12345678 --size 20
# Then on the instance:
sudo growpart /dev/xvda 1
sudo xfs_growfs /
In the next article, we will explore EC2 instance types in depth — how to evaluate workload requirements and choose the right instance family, generation, and size for any use case.
This article is just the start. Get the full picture with our free whitepaper - 8 chapters covering IAM, S3, VPC, monitoring, agentic AI security, compliance, and a prioritized action plan with 50+ CLI commands.
A practical guide to configuring AWS CLI v2, Boto3, and secure credential management for professional cloud development.
Stop sending your IAM policies, CloudTrail logs, and infrastructure code to third-party APIs. Run LLMs locally with Ollama on Apple Silicon — private, offline, fast. Complete setup guide with AWS security use cases.
We obtained the actual compromised litellm packages, set up a disposable EC2 instance with honeypot credentials and mitmproxy, and detonated the malware. Full evidence: fork bomb, credential theft in under 2 seconds, IMDS queries, AWS API calls, and C2 exfiltration.