How to SSH into a Private EC2 Instance Through a Bastion Host (Jump Server)
To SSH into a private EC2 instance, go through a bastion host (jump server) in a public subnet, without copying your key to it. On your laptop run ssh-add mykey.pem, then ssh -J ec2-user@<BASTION_PUBLIC_IP> ec2-user@<PRIVATE_IP>. The private instance's security group must allow SSH (22) only from the bastion's security group, and the bastion's security group allows 22 only from your IP.
Come on, friends! To go into the society's inner wing, you first register your name at the reception near the main gate, then go in. The bastion host is that reception: first SSH to it, then from there to the private server. And note: keep the key (private key) in your own pocket; don't leave it at reception.
चला मित्रांनो! Society च्या आतल्या wing मध्ये जायचं असेल तर आधी main gate च्या reception ला नाव नोंदवा, मग आत जा. Bastion host तेच reception आहे: आधी त्याच्यावर SSH, तिथून private server वर. आणि लक्ष द्या: चावी (private key) आपल्या खिशातच ठेवा, reception वर सोडून जाऊ नका.
चलो दोस्तों! Society की अंदर वाली wing में जाना हो तो पहले main gate के reception पर नाम लिखवाओ, फिर अंदर जाओ. Bastion host वही reception है: पहले उस पर SSH, वहाँ से private server पर. और ध्यान दो: चाबी (private key) अपनी जेब में ही रखो, reception पर छोड़कर मत जाओ.
Quick answer
On your laptop (macOS, Linux, or Windows 10/11 with OpenSSH):
chmod 400 mykey.pem # macOS/Linux: the key must be private
ssh-add mykey.pem # load the key into the SSH agent
ssh -J ec2-user@<BASTION_PUBLIC_IP> ec2-user@10.0.2.10
Or with agent forwarding, in two hops:
ssh -A ec2-user@<BASTION_PUBLIC_IP>
ssh ec2-user@10.0.2.10 # run on the bastion; the key stays on your laptop
What do I need before connecting?
- A VPC with a public subnet (bastion) and a private subnet (target). See Public vs private subnet.
- Two instances launched with the same key pair (or two keys, both loaded with
ssh-add). - Security groups:
| Security group | Inbound rule | Attached to |
|---|---|---|
bastion-sg |
SSH 22 from My IP (e.g. 198.51.100.7/32) |
Bastion |
app-sg |
SSH 22 from bastion-sg |
Private instance |
- The SSH agent running: on macOS and Linux it usually is; on Windows start it once in an admin PowerShell with
Get-Service ssh-agent | Set-Service -StartupType Automatic; Start-Service ssh-agent.
How does SSH through a bastion work?
ssh -J first connects to the bastion in the public subnet on port 22, then jumps to the private server 10.0.2.10, which allows SSH only from bastion-sg. The private key stays on your laptop.
ssh -J आधी public subnet मधल्या bastion ला port 22 वर connect होतो, मग private server 10.0.2.10 वर jump करतो, जो SSH फक्त bastion-sg कडून allow करतो. Private key तुमच्या laptop वरच राहते.
ssh -J पहले public subnet के bastion से port 22 पर connect होता है, फिर private server 10.0.2.10 पर jump करता है, जो SSH सिर्फ bastion-sg से allow करता है. Private key आपके laptop पर ही रहती है.
With ssh -J, your laptop opens an SSH connection to the bastion and, through it, a second SSH connection to the private server. Both logins are checked with the key held by the agent on your laptop; the bastion only passes the encrypted traffic along. Nothing secret is stored on the bastion.
How do I SSH into a private instance step by step?
Step 1 — Load your key into the agent
chmod 400 mykey.pem
ssh-add mykey.pem
ssh-add -l # lists the loaded key
Step 2 — Jump with ProxyJump (-J)
ssh -J ec2-user@192.0.2.10 ec2-user@10.0.2.10
Replace 192.0.2.10 (a documentation example address) with the bastion's public IP and 10.0.2.10 with the private instance's private IP (EC2 console → instance → Private IPv4 addresses). Note: an -i mykey.pem option on this command applies only to the final server, not to the bastion — that is why we use ssh-add.
Step 3 — Save it in ~/.ssh/config (optional, recommended)
Host bastion
HostName 192.0.2.10
User ec2-user
IdentityFile ~/keys/mykey.pem
Host app
HostName 10.0.2.10
User ec2-user
IdentityFile ~/keys/mykey.pem
ProxyJump bastion
Now ssh app is enough. Each host uses its own IdentityFile, so here ssh-add is not even required.
Step 4 — Or use agent forwarding (-A)
ssh -A ec2-user@192.0.2.10
ssh ec2-user@10.0.2.10
-A forwards the agent, so the second ssh on the bastion can use the key that lives on your laptop. Use it only with bastions you trust, because an administrator of the bastion could use your forwarded agent while you are connected. ssh -J avoids that and is the better default. 🔑
Ravindra Bagale's Tip
Some tutorials say: scp the .pem file to the bastion. Never! If the bastion is hacked, the key to your private servers is gone too. Keep the key on your laptop and use ssh-add and ssh -J. And on your laptop give the key chmod 400, otherwise SSH refuses to use it.
Ravindra Bagale's Tip – मराठी
काही tutorials सांगतात: .pem file bastion वर scp करा. कधीच नाही! Bastion hack झाला तर private servers ची चावी पण गेली. Key laptop वरच ठेवा, ssh-add आणि ssh -J वापरा. आणि laptop वर key ला chmod 400, नाहीतर SSH ती key वापरायला नकार देतो.
Ravindra Bagale's Tip – हिंदी
कुछ tutorials बताते हैं: .pem file को bastion पर scp करो. कभी नहीं! Bastion hack हुआ तो private servers की चाबी भी गई. Key laptop पर ही रखो, ssh-add और ssh -J इस्तेमाल करो. और laptop पर key को chmod 400 दो, वरना SSH उस key को इस्तेमाल करने से मना कर देता है.
How do I fix bastion SSH errors?
Ghabru naka 😅 — these are the usual ones:
| Symptom | Likely cause | Fix |
|---|---|---|
Permission denied (publickey) on the private server |
Key not loaded in the agent, or you used -i (applies only to the final hop) |
ssh-add mykey.pem, check ssh-add -l, retry |
Could not open a connection to your authentication agent |
Agent not running | macOS/Linux: eval "$(ssh-agent -s)"; Windows: start the ssh-agent service |
| Timeout to the bastion | bastion-sg does not allow 22 from your current IP |
Edit the rule to My IP again (home IPs change) |
| Timeout from bastion to the private IP | app-sg has no SSH rule from bastion-sg |
Add inbound SSH 22, source bastion-sg |
WARNING: UNPROTECTED PRIVATE KEY FILE! |
Key permissions too open | chmod 400 mykey.pem |
Host key verification failed after rebuilding servers |
Old host key cached | ssh-keygen -R 10.0.2.10 (and the bastion IP) |
Is there a way without a bastion?
Yes: AWS Systems Manager Session Manager opens a shell from the console or CLI with no inbound port 22 at all. The instance needs the SSM Agent (preinstalled on Amazon Linux 2023), an IAM role with the AmazonSSMManagedInstanceCore policy, and a way to reach the SSM service (a NAT gateway or VPC endpoints). EC2 Instance Connect Endpoint is another bastion-free option. The bastion is still worth learning: it is common in companies and in interviews.
Try it at home
Connect to a private instance with ssh -J, then write the ~/.ssh/config above and connect with just ssh app. Finally, try ssh -i mykey.pem -J ... without ssh-add after running ssh-add -D, read the error, and explain why it happens.
Learn it properly
Got it? ssh-add, then ssh -J through the bastion to the private IP. The private server's port 22 only for bastion-sg. The key stays on the laptop – never on the bastion.
समजलं का? ssh-add, मग ssh -J bastion वरून private IP वर. Private server चा port 22 फक्त bastion-sg साठी. Key laptop वरच – bastion वर कधीच नाही.
समझ आया? ssh-add, फिर ssh -J bastion से होकर private IP पर. Private server का port 22 सिर्फ bastion-sg के लिए. Key laptop पर ही – bastion पर कभी नहीं.
Frequently asked questions
How do I SSH into a private EC2 instance?
Through a bastion host in a public subnet: load the key with ssh-add mykey.pem and run ssh -J ec2-user@BASTION_IP ec2-user@PRIVATE_IP. The private instance must allow port 22 from the bastion security group.
Should I copy my .pem key to the bastion host?
No. If the bastion is compromised, the key to every private server is stolen. Keep the key on your laptop and use ssh -J or agent forwarding.
What is the difference between ssh -J and ssh -A?
-J (ProxyJump) tunnels the second connection through the bastion from your laptop. -A forwards your agent to the bastion so you can run ssh there. -J exposes less and is the better default.
Why do I get Permission denied (publickey) on the private server?
The key is not in your SSH agent, or you passed -i, which applies only to the final hop. Run ssh-add mykey.pem and check with ssh-add -l.
Which security group rules does a bastion setup need?
The bastion allows SSH 22 from your IP only. Private instances allow SSH 22 only from the bastion security group, not from 0.0.0.0/0.
Can I connect without a bastion?
Yes, with AWS Systems Manager Session Manager (IAM role, SSM Agent, no port 22) or EC2 Instance Connect Endpoint.