Fix AWS EC2 Instance Unreachable: Can't SSH In

Quick answer
EC2 instance unreachable via SSH? The cause is almost always Security Group rules, Network ACLs, a missing public IP, or a failed boot. Here's a systematic diagnostic flow — including SSM Session Manager as a fallback when SSH isn't an option.
- Step 1: Check the Instance State
- Step 3: Check the Network ACL — Stateless Firewall
- Step 5: Check the Instance Has a Public IP
- Step 6: Check the Instance System Log for Boot Errors
- Step 7: SSH Key Mismatch
6 min read · Platform Engineering
Fix AWS EC2 Instance Unreachable: Can't SSH In
ssh: connect to host 54.x.x.x port 22: Connection timed out
or:
ssh: connect to host 54.x.x.x port 22: Connection refused
EC2 connectivity failures are almost always a network configuration problem, not an application problem. Work through these checks in order — each step rules out one layer.
Step 1: Check the Instance State
aws ec2 describe-instances \
--instance-ids i-0abc1234567890 \
--query 'Reservations[0].Instances[0].State.Name' \
--output textMust be running. If it's stopped, pending, or shutting-down:
# Start a stopped instance
aws ec2 start-instances --instance-ids i-0abc1234567890
# Wait for it to be running
aws ec2 wait instance-running --instance-ids i-0abc1234567890If the instance is stuck in pending for more than a few minutes, check the system log (Step 5).
Step 2: Check the Security Group — Port 22 Open?
Security Groups are stateful firewalls applied per instance. Inbound rules must explicitly allow SSH.
In the AWS Console: EC2 → Instances → select instance → Security tab → Security groups → Inbound rules.
Via CLI:
1# Get the security group IDs for the instance
2SG_IDS=$(aws ec2 describe-instances \
3 --instance-ids i-0abc1234567890 \
4 --query 'Reservations[0].Instances[0].SecurityGroups[*].GroupId' \
5 --output text)
6
7# Check inbound rules
8aws ec2 describe-security-groups \
9 --group-ids $SG_IDS \
10 --query 'SecurityGroups[*].IpPermissions'You need an inbound rule for:
- Protocol: TCP
- Port range: 22
- Source: Your IP (
x.x.x.x/32) or0.0.0.0/0(open to all — not recommended for production)
Fix: Add the rule:
# Replace 203.0.113.1 with your actual public IP
aws ec2 authorize-security-group-ingress \
--group-id sg-0abc1234 \
--protocol tcp \
--port 22 \
--cidr 203.0.113.1/32To find your current public IP:
curl -s https://checkip.amazonaws.comStep 3: Check the Network ACL — Stateless Firewall
Network ACLs (NACLs) are stateless and applied at the subnet level. Unlike Security Groups, they apply rules separately to inbound AND outbound traffic, and you must explicitly allow return traffic.
Inbound rule needed: TCP port 22 from your IP Outbound rule needed: TCP ports 1024–65535 to your IP (ephemeral ports — the SSH response)
In the Console: VPC → Subnets → select subnet → Network ACL tab.
1# Find the subnet for your instance
2SUBNET_ID=$(aws ec2 describe-instances \
3 --instance-ids i-0abc1234567890 \
4 --query 'Reservations[0].Instances[0].SubnetId' \
5 --output text)
6
7# Get the NACL for that subnet
8aws ec2 describe-network-acls \
9 --filters "Name=association.subnet-id,Values=$SUBNET_ID" \
10 --query 'NetworkAcls[0].Entries'Check for DENY rules on port 22 inbound or ephemeral ports outbound. NACLs evaluate rules in order by rule number — a lower-numbered DENY rule overrides a higher-numbered ALLOW.
Default NACL: Allows all traffic in both directions. If you're using a custom NACL, verify these rules exist.
Step 4: Check the Route Table — Is There a Route to the Internet?
For a public subnet, the route table must have a route sending 0.0.0.0/0 to an Internet Gateway.
# Get the route table for the subnet
aws ec2 describe-route-tables \
--filters "Name=association.subnet-id,Values=$SUBNET_ID" \
--query 'RouteTables[0].Routes'Look for: "DestinationCidrBlock": "0.0.0.0/0" with "GatewayId": "igw-xxx".
If the route sends 0.0.0.0/0 to a NAT Gateway instead of an Internet Gateway, this is a private subnet — the instance has no public inbound route. You need to connect via a bastion host, VPN, or SSM.
Stuck on this in production?
We debug exactly this kind of issue for platform teams — usually in a single working session.
Step 5: Check the Instance Has a Public IP
aws ec2 describe-instances \
--instance-ids i-0abc1234567890 \
--query 'Reservations[0].Instances[0].{PublicIP:PublicIpAddress,PublicDNS:PublicDnsName}'If PublicIpAddress is None, the instance has no public IP. Possible causes:
- The subnet doesn't auto-assign public IPs
- The instance was launched without "Auto-assign public IP" enabled
Fix option 1: Allocate and associate an Elastic IP:
1# Allocate an Elastic IP
2ALLOC_ID=$(aws ec2 allocate-address --domain vpc --query AllocationId --output text)
3
4# Associate it with the instance
5aws ec2 associate-address \
6 --instance-id i-0abc1234567890 \
7 --allocation-id $ALLOC_IDFix option 2: Enable auto-assign on the subnet for future instances:
aws ec2 modify-subnet-attribute \
--subnet-id $SUBNET_ID \
--map-public-ip-on-launchStep 6: Check the Instance System Log for Boot Errors
If the instance is running but completely unreachable (not just port 22), it may have failed to boot correctly.
aws ec2 get-console-output \
--instance-id i-0abc1234567890\
--latest \
--output textLook for:
FAILEDorERRORlines in the boot sequencefsckfilesystem errors (especially after an unclean shutdown)- Out of disk space errors during cloud-init
sshdfailed to start
Step 7: SSH Key Mismatch
Permission denied (publickey)
This means the network path is fine but the SSH key is wrong.
1# Specify the key explicitly
2ssh -i ~/.ssh/my-ec2-key.pem [email protected]
3
4# Common default usernames by AMI
5# Amazon Linux 2/2023: ec2-user
6# Ubuntu: ubuntu
7# Debian: admin
8# CentOS/RHEL: ec2-user or centos
9# Fedora: fedora
10# SUSE: ec2-user
11# Bitnami: bitnamiIf you lost the key, the instance is inaccessible via SSH. Use SSM Session Manager instead (Step 8), then add a new key.
Step 8: SSM Session Manager — SSH-Free Access
If SSH is blocked or the key is lost, AWS Systems Manager Session Manager provides browser-based or CLI terminal access with no inbound ports required.
Requirements: SSM Agent running on the instance (pre-installed on Amazon Linux 2/2023 and many marketplace AMIs) and an IAM role with AmazonSSMManagedInstanceCore policy attached.
# Start a session (opens a shell on the instance)
aws ssm start-session --target i-0abc1234567890
# Or via the Console: EC2 → Connect → Session ManagerOnce connected, you can:
- Add your SSH public key to
~/.ssh/authorized_keys - Fix Security Group rules via AWS CLI
- Investigate failed services
Enabling SSM on an existing instance
1# Attach the SSM policy to the instance's IAM role
2aws iam attach-role-policy \
3 --role-name my-ec2-role \
4 --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
5
6# Restart SSM Agent on the instance if it's running but not connected
7sudo systemctl restart amazon-ssm-agentEC2 Instance Connect — Browser SSH
For Amazon Linux 2/2023 and Ubuntu, EC2 Instance Connect pushes a temporary SSH key to the instance for a 60-second window:
1aws ec2-instance-connect send-ssh-public-key \
2 --instance-id i-0abc1234567890 \
3 --availability-zone us-east-1a \
4 --instance-os-user ubuntu \
5 --ssh-public-key file://~/.ssh/temp-key.pub
6
7# Then SSH within 60 seconds
8ssh -i ~/.ssh/temp-key [email protected]Requires port 22 open in the Security Group, but removes the long-term key management problem.
Diagnostic Checklist
| Check | Expected | How to fix |
|---|---|---|
| Instance state | running | aws ec2 start-instances |
| Security Group inbound | TCP 22 from your IP | Add inbound rule |
| Network ACL inbound | Allow TCP 22 | Add allow rule |
| Network ACL outbound | Allow TCP 1024–65535 | Add ephemeral port rule |
| Route table | 0.0.0.0/0 → igw-xxx | Add IGW route |
| Public IP | Not null | Allocate and associate EIP |
| SSH key | Correct key file | Use SSM to add new key |
See Also
- Linux Commands for Advanced Engineers —
systemctl,journalctl, andsshdconfiguration - Ubuntu Zero-Downtime Patching — keep EC2 instances patched without SSH outages
Official References
- Debug Pods — reading pod status, events and container states
- kubectl reference — command syntax, output formats and selectors
Was this article helpful?
Be the first to rate this article
Related Topics
Found this useful? Share it.


