An EC2 instance is a Linux box listening on TCP/22 (or, optionally, no port at all if you use Session Manager). AWS gives you four sanctioned ways in. The fundamental method is plain ssh -i key.pem user@public_ip against a key pair, which works from any machine with OpenSSH and a route to the instance. The other three (EC2 Instance Connect, Session Manager, and EC2 Instance Connect Endpoint) trade local key management for IAM-driven access and, in the case of the last two, no public IP at all. The cluster of problems people hit is always the same: wrong username for the AMI, security group does not allow your IP, key file permissions too loose, or the instance has no public IP and they did not realize SSH would never work from the open internet. Below: the exact procedure for each method, default usernames by AMI, the ~/.ssh/config block I use for AWS, and a troubleshooting matrix that pins down Permission denied (publickey) versus Connection timed out in under a minute.
How do I SSH into an AWS EC2 instance?
Download your key pair .pem file when you launch the instance, then set permissions: chmod 400 ~/.ssh/aws_keypair.pem. From the EC2 console, grab the instance's public IPv4 address or public DNS name. Confirm the security group attached to the instance allows inbound TCP/22 from your source IP (ideally your /32, not 0.0.0.0/0). Connect with ssh -i ~/.ssh/aws_keypair.pem ec2-user@PUBLIC_IP. The username depends on the AMI: ec2-user for Amazon Linux 2 and Amazon Linux 2023, ubuntu for Ubuntu, admin for Debian, centos for CentOS, fedora for Fedora, ec2-user or rocky for Rocky Linux. If you cannot expose port 22 or assign a public IP, use AWS Systems Manager Session Manager (aws ssm start-session --target i-XXX) or an EC2 Instance Connect Endpoint. Both work via IAM, no inbound rules required.
Jump to:
- The four ways to connect
- Default usernames by AMI
- Method A: Plain SSH with a key pair
- Method B: EC2 Instance Connect
- Method C: Session Manager (no SSH, no public IP)
- Method D: EC2 Instance Connect Endpoint
- Security group rules for SSH
- The ~/.ssh/config block I use for EC2
- Comparison: Console vs CLI vs Session Manager vs IAP
- Troubleshooting: Permission denied, Connection timed out
- What to do next
- FAQ
The four ways to connect
AWS supports four sanctioned paths to a shell on an EC2 instance. Pick by network model and operational requirements.
| Method | Network requirement | Auth | When to use |
|---|---|---|---|
| A. Plain SSH (key pair) | Public IP plus inbound TCP/22 | Key pair | The default, scriptable, works everywhere |
| B. EC2 Instance Connect | Public IP plus inbound TCP/22 from AWS service range | IAM, ephemeral key | Ad-hoc browser or CLI access without managing keys |
| C. Session Manager | No inbound rules; outbound HTTPS to SSM endpoints | IAM | Private instances, audit-logged sessions, no SSH at all |
| D. EC2 Instance Connect Endpoint | No public IP; endpoint in same VPC | IAM | SSH or SCP to private instances without a bastion |
Methods C and D are how a hardened production environment looks. Method A is how a fresh AWS account looks. Method B is what the console's "Connect" button does behind the scenes.
Default usernames by AMI
The single most common reason ssh fails on a freshly launched EC2 instance is the wrong username. AWS does not let you log in as root over SSH on stock public AMIs; you log in as a sudo-capable initial user that is hardcoded into the AMI.
| AMI | Default username |
|---|---|
| Amazon Linux 2023 | ec2-user |
| Amazon Linux 2 | ec2-user |
| Amazon Linux (legacy) | ec2-user |
| Ubuntu | ubuntu |
| Debian | admin |
| CentOS | centos |
| Rocky Linux | rocky or ec2-user (depends on AMI) |
| Fedora | fedora or ec2-user |
| RHEL | ec2-user (RHEL 7.4+) or root (older) |
| SUSE | ec2-user or root |
| Bitnami AMIs | bitnami |
If you launched a community AMI or a marketplace image, the username is in the AMI description on the launch page. Save it somewhere; you will need it again.
Method A: Plain SSH with a key pair
The original, the default, and still the workhorse.
Step 1: Get the key pair .pem file.
You either downloaded it at instance launch (the only chance to download a key pair that AWS generates for you) or you imported a public key you generated yourself with ssh-keygen. AWS stores only the public half; the private half is on your machine.
chmod 400 :key_fileOpenSSH refuses to use a key file with looser permissions than 0400 (read-only by owner). Skipping this step yields WARNING: UNPROTECTED PRIVATE KEY FILE! and the connection aborts.
Step 2: Find the instance's public IPv4 or DNS name.
EC2 console, Instances, click the instance, the Public IPv4 address or Public IPv4 DNS field. The CLI version:
aws ec2 describe-instances \
--region :region \
--instance-ids :instance_id \
--query "Reservations[].Instances[].PublicIpAddress" \
--output textFor workloads that need a stable IP, allocate an Elastic IP and associate it with the instance. Otherwise the IP changes every stop/start cycle.
Step 3: Confirm the security group allows your IP on port 22.
This is the second-most-common reason connections fail. Inbound rule needs to be TCP, port 22, source either 0.0.0.0/0 (open internet, bad) or your IP as a /32 (good).
aws ec2 authorize-security-group-ingress \
--region :region \
--group-id sg-0abc123 \
--protocol tcp \
--port 22 \
--cidr "$(curl -s https://checkip.amazonaws.com)/32"That one-liner adds an inbound rule for your current public IP. Re-run it whenever your home or office IP changes. For a fuller treatment of the curl tricks above, the curl cheat sheet covers the option reference.
Step 4: Connect.
ssh -i :key_file :user@:hostThat is the whole thing. On first connection OpenSSH prompts to accept the host key; type yes. After that, ssh-keygen records the fingerprint in ~/.ssh/known_hosts and subsequent connects are silent.
To copy a file in either direction, swap ssh for scp or rsync:
scp -i :key_file ./localfile.tar.gz :user@:host:/tmp/
rsync -avz -e "ssh -i :key_file" ./data/ :user@:host:/srv/data/Method B: EC2 Instance Connect
EC2 Instance Connect (launched June 2019) sidesteps key-pair management for ad-hoc access. AWS pushes a short-lived public key into the instance's authorized_keys for 60 seconds, you connect, the key disappears.
Browser path: EC2 console, Instances, select the instance, Connect button, EC2 Instance Connect tab, Connect. A browser-based terminal opens. This is the easiest path for a one-off poke at an instance from a machine that does not have the .pem file.
CLI path: with AWS CLI v2.13.0+, a single command does everything:
aws ec2-instance-connect ssh --region :region --instance-id :instance_idThe CLI generates a one-shot key pair, pushes the public half via the EC2 Instance Connect API, then invokes ssh for you. The connecting IAM principal needs ec2-instance-connect:SendSSHPublicKey on the instance.
Requirements:
- The instance must have the
ec2-instance-connectpackage installed (default on Amazon Linux 2, Amazon Linux 2023, Ubuntu 16.04+). - Inbound TCP/22 in the security group, either from your IP or from the AWS service IP range for EC2 Instance Connect in the instance's region (look up
EC2_INSTANCE_CONNECTin the AWS IP ranges JSON). - Public IP on the instance (or use Method D for private instances).
EC2 Instance Connect is the "I lost the key pair and need in right now" escape hatch.
Method C: Session Manager (no SSH, no public IP)
AWS Systems Manager Session Manager (launched September 2018) is the most production-friendly option. The instance runs the SSM agent, which polls SSM endpoints over outbound HTTPS. There is no inbound rule on port 22, no public IP required, no key pair. Every session is logged to CloudWatch Logs or S3 if you want it.
Prerequisites:
- SSM agent installed on the instance. Amazon Linux 2, Amazon Linux 2023, and Ubuntu AMIs from 2017 onward ship it pre-installed.
- An IAM instance profile attached with the
AmazonSSMManagedInstanceCoremanaged policy. - Network reachability to SSM endpoints. Either a public IP plus internet gateway, a NAT gateway, or VPC endpoints for
ssm,ssmmessages, andec2messages. - The Session Manager plugin installed on your local CLI.
Connect:
aws ssm start-session --region :region --target :instance_idThat is it. A shell opens. Behind the scenes the SSM agent on the instance opens a bidirectional channel to AWS, your CLI joins the same channel, and the two sides pipe stdin/stdout. No port 22, no public IP, no SSH client involved.
SSH-over-SSM is also possible if you want to keep using ssh and scp against private instances:
# in ~/.ssh/config
Host i-* mi-*
ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
With that block, ssh ec2-user@i-0abc123def456 works for any instance the IAM principal can ssm:StartSession against. The instance still needs a key pair if you go this route, since SSH does the actual auth; SSM only provides the tunnel.
Audit log: every command in a Session Manager session can be logged. For regulated environments this beats SSH, where session contents are not centrally captured.
Method D: EC2 Instance Connect Endpoint
EC2 Instance Connect Endpoint (GA June 2023) is the bastion-host replacement. It is a VPC-attached service that brokers SSH and RDP traffic from your IAM-authenticated CLI to private instances without those instances ever needing a public IP.
Setup is one-time:
aws ec2 create-instance-connect-endpoint \
--region :region \
--subnet-id subnet-0abc123 \
--security-group-ids sg-0abc123The endpoint's security group must allow outbound TCP/22 to the target instance's security group. The target instance's security group must allow inbound TCP/22 from the endpoint's security group.
Connect:
aws ec2-instance-connect ssh \
--region :region \
--instance-id :instance_id \
--connection-type eiceOr for the long-form SSH-with-tunnel pattern:
aws ec2-instance-connect open-tunnel \
--region :region \
--instance-id :instance_id \
--local-port 2222 &
ssh -i :key_file -p 2222 :user@127.0.0.1The tunnel routes through Instance Connect Endpoint, the instance's private IP is reached over VPC routing, and your IAM identity gates access.
This is the modern replacement for a Linux bastion EC2 instance running 24/7. No bastion to patch, no inbound rules on internet-facing security groups.
Security group rules for SSH
The single inbound rule that lets you SSH:
| Type | Protocol | Port range | Source |
|---|---|---|---|
| SSH | TCP | 22 | 0.0.0.0/0 (open internet) or YOUR_IP/32 (locked down) |
Use your /32 whenever possible. A wide-open port 22 on the internet receives tens of thousands of brute-force attempts per day, which fail against key-based auth but fill the logs and waste CPU.
For a fleet, the cleanest pattern is a "bastion" security group with the inbound /32 rule, attached to any instance you want SSH access to. The bastion mental model is now mostly obsolete (Session Manager and EC2 Instance Connect Endpoint do it better) but the security group pattern persists.
To lock down an existing SG to your current IP, revoke the open rule and add the narrow one:
MY_IP=$(curl -s https://checkip.amazonaws.com)
aws ec2 revoke-security-group-ingress --region :region \
--group-id sg-0abc123 --protocol tcp --port 22 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --region :region \
--group-id sg-0abc123 --protocol tcp --port 22 --cidr "${MY_IP}/32"The ~/.ssh/config block I use for EC2
Host ec2-bastion
HostName 54.197.100.42
User ec2-user
IdentityFile ~/.ssh/aws_keypair.pem
IdentitiesOnly yes
ServerAliveInterval 60
ServerAliveCountMax 3
Host ec2-*
User ec2-user
IdentityFile ~/.ssh/aws_keypair.pem
IdentitiesOnly yes
StrictHostKeyChecking accept-new
Host i-* mi-*
ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
User ec2-user
IdentityFile ~/.ssh/aws_keypair.pem
IdentitiesOnly yes
The first block names a specific bastion. The second handles anything I name with an ec2-* prefix in my local DNS or /etc/hosts. The third makes ssh ec2-user@i-0abc123def456 work via Session Manager for private instances.
IdentitiesOnly yes matters when you have multiple keys in ssh-agent. Without it, OpenSSH offers every key in agent before falling back to the right one, and AWS will start rejecting attempts before you reach the correct key. For the broader SSH option reference, the SSH cheat sheet has the flag list.
Comparison: Console vs CLI vs Session Manager vs IAP
The same job, multiple interfaces.
| Tool | Best for | Key management | Network model | Audit log |
|---|---|---|---|---|
| EC2 console Connect button | One-off browser access | None (Instance Connect) | Public IP plus SG inbound | CloudTrail only |
| Plain OpenSSH (Method A) | Scripted automation, CI | Manual .pem | Public IP plus SG inbound | None on the session itself |
aws ec2-instance-connect ssh | Ad-hoc CLI access, no .pem | Ephemeral, IAM-issued | Public IP plus SG inbound | CloudTrail per push |
| Session Manager | Production, regulated environments | None | Outbound HTTPS to SSM | Full session log (optional) |
| EC2 Instance Connect Endpoint | Private instances, replace bastion | .pem or ephemeral | No public IP needed | CloudTrail per session |
For a single-person AWS account doing personal infrastructure, plain SSH with a key pair plus a tight security group is fine. For a team, lean on Session Manager or Instance Connect Endpoint and stop managing .pem files across laptops.
For batch operations against many instances, a Bash for loop over an instance-ID list with ssh or aws ssm start-session is the simplest pattern. Pair with a Bash while loop and aws ec2 describe-instance-status if you need to wait for instances to reach running before connecting.
Troubleshooting: Permission denied, Connection timed out
The five errors that cover 99% of real failures.
Permission denied (publickey)
Network works, authentication does not. In order of frequency:
- Wrong username. This is almost always the cause.
ec2-userfor Amazon Linux,ubuntufor Ubuntu,adminfor Debian. See the Default usernames by AMI section. Runssh -vand look forAuthenticated to ... using "publickey"versusPermission denied (publickey). - Wrong key.
ssh -vshowsOffering public key:lines. Confirm the key matches the key pair name attached to the instance (aws ec2 describe-instances ... --query "...KeyName"). - Key permissions too loose.
chmod 400 ~/.ssh/aws_keypair.pem. - Custom AMI with sshd locked down.
PasswordAuthentication noplus a missing~/.ssh/authorized_keysfor the user. Recover via Session Manager.
ssh: connect to host X.X.X.X port 22: Connection timed out (or Operation timed out)
Network-layer problem. The TCP SYN is being dropped before reaching sshd.
- Security group does not allow TCP/22 from your IP. Most common cause. Add the rule (see the security group section).
- NACL on the subnet blocks the port. Rare on default VPCs; check the network ACL.
- The instance is in a private subnet with no public IP. Internet cannot reach it. Switch to Session Manager or Instance Connect Endpoint.
- The instance is stopped. Check the instance state in the console.
ssh: connect to host X.X.X.X port 22: Connection refused
Network works, but nothing is listening on port 22.
- sshd crashed or was disabled. Use Session Manager to recover, or use the EC2 Serial Console.
- The instance just rebooted and sshd has not finished starting. Wait 30 seconds.
Host key verification failed
The instance's host key changed since you last connected. Usually because the instance was terminated and a new one was launched with the same IP.
ssh-keygen -R :hostRemoves the stale entry from ~/.ssh/known_hosts. Then reconnect and accept the new key.
WARNING: UNPROTECTED PRIVATE KEY FILE!
OpenSSH refusing to use a key with too-loose permissions.
chmod 400 :key_fileThe full debug recipe: ssh -vvv -i ~/.ssh/aws_keypair.pem ec2-user@PUBLIC_IP 2>&1 | less. The Offering public key: and Server accepts key: lines pin down auth issues; the connect to address line pins down network issues.
What to do next
- The companion EC2 articles in this cluster: How to Add an EBS Volume to an EC2 Instance and How to Extend an AWS EBS Volume Without a Restart.
- The GCP equivalent for cross-cloud sysadmin work: How to SSH into a Google Cloud VM Without gcloud. Same idea, different metadata model.
- SSH Cheat Sheet: the flag and option reference covering everything above.
- How to Export and Import PuTTY Sessions and Settings: the Windows-side workflow for migrating saved sessions across machines.
- Bash For Loops and Bash While Loops: the patterns I use for scripted ssh against many instances.
- Curl Cheat Sheet: for hitting the EC2 REST API directly when scripting access checks.
- The canonical reference: AWS's Connect to your Linux instance using SSH guide.
- AWS Systems Manager Session Manager documentation for the no-SSH-port path.





