Home / AWS

AWSHow-toBeginner

Launch your first EC2 instance with the CLI: key pair, security group and SSH

Launch a small Amazon Linux instance from the CLI. Create a key pair, allow SSH only from your own IP, find the latest AMI safely, connect, then stop or terminate it so you don't keep paying.

Deepak Bhardwaj5 min read

Applies toAmazon EC2, Amazon Linux 2023, AWS CLI v2

The goal

You need a Linux server on AWS for a quick test. By the end, you'll have one running, you'll be connected over SSH and you'll know how to shut it down properly.

Check your identity and region before you create anything:

aws sts get-caller-identity
aws configure get region

An instance you can't find is often one you launched in another region.

Step 1: create a key pair

The key pair is how you log in. AWS stores the public half, and you keep the private half.

aws ec2 create-key-pair \
  --key-name lab-key \
  --key-type ed25519 \
  --query KeyMaterial \
  --output text > lab-key.pem

chmod 400 lab-key.pem

AWS doesn't keep a copy of your private key. If you lose it, you'll have to use one of the recovery methods. Never commit it to Git.

On Windows PowerShell, skip chmod and restrict the file through its security properties, or ssh may refuse to use it.

Step 2: choose a network

For a lab, the default VPC works:

VPC_ID=$(aws ec2 describe-vpcs --filters Name=is-default,Values=true \
  --query 'Vpcs[0].VpcId' --output text)
echo "$VPC_ID"

SUBNET_ID=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" \
  --query 'Subnets[0].SubnetId' --output text)

If VPC_ID prints None, your region has no default VPC. Build a VPC first, and use its public subnet.

Step 3: allow SSH from your IP only

A security group is a firewall around the instance. By default it blocks all inbound traffic and allows all outbound.

SG_ID=$(aws ec2 create-security-group \
  --group-name lab-ssh \
  --description "SSH from my IP" \
  --vpc-id "$VPC_ID" \
  --query GroupId --output text)

MYIP=$(curl -s https://checkip.amazonaws.com)

aws ec2 authorize-security-group-ingress \
  --group-id "$SG_ID" \
  --protocol tcp --port 22 \
  --cidr "$MYIP/32"

Warning

Don't open port 22 to 0.0.0.0/0. Automated scanners will find it fast. For long-lived servers, skip SSH altogether and use Session Manager.

Step 4: launch the instance

AMI IDs differ by region and change over time, so don't hard-code one. AWS publishes the latest ID as a public parameter:

INSTANCE_ID=$(aws ec2 run-instances \
  --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --instance-type t3.micro \
  --key-name lab-key \
  --security-group-ids "$SG_ID" \
  --subnet-id "$SUBNET_ID" \
  --associate-public-ip-address \
  --metadata-options HttpTokens=required \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=lab-01},{Key=Owner,Value=me}]' \
  --query 'Instances[0].InstanceId' --output text)

aws ec2 wait instance-status-ok --instance-ids "$INSTANCE_ID"

The waiter takes a few minutes, and returns once the status checks pass. HttpTokens=required turns on IMDSv2, a safer form of instance metadata, and it's a good habit to start with.

Step 5: connect

IP=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" \
  --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)

ssh -i lab-key.pem ec2-user@"$IP"

The login user depends on the image:

Image User
Amazon Linux ec2-user
Ubuntu ubuntu
Debian admin
RHEL ec2-user

Once connected, look around:

cat /etc/os-release
df -h /
TOKEN=$(curl -s -X PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-type

The last two lines read the instance's metadata using IMDSv2.

Common problems

Problem Likely cause Fix
InvalidKeyPair.NotFound Key is in another region Create the key in this region
Connection timed out Security group, your IP changed, or no route to the internet Update the rule, and see SSH timeout
Permission denied (publickey) Wrong user or key Check the table above, and use -i
UNPROTECTED PRIVATE KEY FILE Key file is readable by others chmod 400 lab-key.pem
InsufficientInstanceCapacity No capacity in that zone Try another type or subnet. See launch failures
VcpuLimitExceeded Account vCPU quota reached Use a smaller type or request an increase

Step 6: stop or terminate

Action Compute charge Disk charge Data on EBS
Stop Stops Continues Kept
Start again Resumes Continues Kept, but the public IP usually changes
Terminate Stops Root volume usually deleted Usually gone
# Stop it for now
aws ec2 stop-instances --instance-ids "$INSTANCE_ID"

# Finished with it
aws ec2 terminate-instances --instance-ids "$INSTANCE_ID"
aws ec2 wait instance-terminated --instance-ids "$INSTANCE_ID"

Caution

Terminating can't be undone. By default, it also deletes the root volume. Check the instance ID and the Name tag first. For real servers, turn on termination protection with aws ec2 modify-instance-attribute --instance-id <id> --disable-api-termination.

Now remove the rest of the lab:

aws ec2 delete-security-group --group-id "$SG_ID"
aws ec2 delete-key-pair --key-name lab-key
rm -f lab-key.pem

Before you finish, list what's still running in this region:

aws ec2 describe-instances \
  --filters Name=instance-state-name,Values=pending,running,stopped \
  --query 'Reservations[].Instances[].{id:InstanceId, state:State.Name, name:Tags[?Key==`Name`]|[0].Value}' \
  --output table

Prevent it next time

  • Tag each instance with an owner, so that you can tell what's safe to remove.
  • Set a budget alert to catch forgotten instances.
  • Use user data to automate setup.
  • For production, use the EC2 production build guide instead of these lab settings.