Back to blogs

AWS Bastion Host: What It Is, Why It Matters, and How to Set It Up

AWS Bastion Host: What It Is, Why It Matters, and How to Set It Up

When you work with AWS, one of the first security principles you learn is that not every server should be directly accessible from the Internet.

Your application servers, internal services, and databases often live inside private subnets. But sooner or later, a DevOps engineer needs to connect to those private resources for troubleshooting, maintenance, deployments, or system administration.

So how do you access a private EC2 instance without giving it a public IP?

One traditional solution is a Bastion Host.

In this guide, we'll understand what a bastion host is, why it is important, how it works in AWS, how to configure one, and why AWS Systems Manager Session Manager is increasingly becoming a better alternative.

What Is a Bastion Host?

A Bastion Host is a specially configured server that acts as a controlled entry point into a private network.

In AWS, a bastion host is typically an EC2 instance deployed in a public subnet. Administrators connect to the bastion from a trusted network and then use it to access servers located in private subnets.

A simple architecture looks like this:

                         Internet
                            |
                            |
                    Trusted Admin IP
                            |
                            | SSH :22
                            v
                   +------------------+
                   |  Bastion Host    |
                   |  Public Subnet   |
                   +------------------+
                            |
                            | SSH :22
                            |
                            v
                   +------------------+
                   | Private EC2      |
                   | Private Subnet   |
                   +------------------+
                            |
                            v
                         RDS

The important point is that the private EC2 instance does not need a public IP address.

Why Do We Need a Bastion Host?

Imagine you have an application server running in a private subnet:

Private EC2
10.0.2.10

The server doesn't have a public IP because you don't want it directly exposed to the Internet.

However, your DevOps team needs SSH access to troubleshoot an issue.

One approach would be to assign the EC2 instance a public IP and allow SSH:

Internet
   |
   | SSH :22
   v
Private/Application Server

This is not a good architecture for most production environments.

You are unnecessarily exposing your application server to the public Internet.

A bastion host provides a controlled access path:

Administrator
      |
      | SSH
      v
Bastion Host
      |
      | SSH through private network
      v
Private EC2

Only the bastion needs to be reachable from the administrator's network.

Bastion Host Architecture in AWS

A typical AWS architecture might look like this:

                         Internet
                            |
                     Internet Gateway
                            |
             +--------------+--------------+
             |                             |
        Public Subnet                 Private Subnet
        10.0.1.0/24                   10.0.2.0/24
             |                             |
             |                             |
      +--------------+              +--------------+
      | Bastion EC2  |--------------| Application  |
      |              |     SSH      | EC2          |
      | Public IP    |              | No Public IP |
      +--------------+              +--------------+
                                           |
                                           |
                                      +----------+
                                      |   RDS    |
                                      | Private  |
                                      +----------+

The public subnet contains the bastion.

The application server and database remain inside private subnets.

Public Subnet vs Private Subnet

Before setting up a bastion, it's important to understand the difference.

Public Subnet

A subnet is considered public when its route table has a route to an Internet Gateway.

For example:

Destination       Target

10.0.0.0/16       local
0.0.0.0/0         Internet Gateway

A bastion host can be placed here.

Private Subnet

A private subnet does not have a direct route to an Internet Gateway for inbound Internet access.

For example:

Destination       Target

10.0.0.0/16       local

Your application servers and databases should generally be placed here.

How a Bastion Host Works

The connection flow is simple.

Step 1

The administrator connects to the bastion:

ssh -i bastion.pem ec2-user@<BASTION_PUBLIC_IP>

Step 2

From the bastion, the administrator connects to the private EC2:

ssh ec2-user@10.0.2.10

The traffic flow is:

Laptop
   |
   | SSH
   v
Bastion
   |
   | SSH through private VPC
   v
Private EC2

The private EC2 never needs to accept SSH traffic directly from the Internet.

Setting Up a Bastion Host in AWS

Let's walk through a basic AWS setup.

We'll assume the following network:

VPC
10.0.0.0/16

Public Subnet
10.0.1.0/24

Private Subnet
10.0.2.0/24

Step 1: Create a VPC

Go to:

AWS Console → VPC → Your VPCs → Create VPC

Example:

Name: production-vpc
CIDR: 10.0.0.0/16

This gives us the overall private network.

Step 2: Create the Public Subnet

Create a subnet:

Name: public-subnet
CIDR: 10.0.1.0/24
Availability Zone: ap-south-1a

This subnet will contain the bastion host.

Step 3: Create the Private Subnet

Create another subnet:

Name: private-subnet
CIDR: 10.0.2.0/24
Availability Zone: ap-south-1a

Your application EC2 instance can live here.

Step 4: Create an Internet Gateway

Create an Internet Gateway:

production-igw

Attach it to:

production-vpc

Then configure the public subnet's route table:

Destination       Target

10.0.0.0/16       local
0.0.0.0/0         production-igw

Now the public subnet has Internet connectivity.

Step 5: Launch the Bastion EC2 Instance

Go to:

EC2 → Launch Instance

For example:

Name:
production-bastion

AMI:
Amazon Linux 2023

Instance Type:
t3.micro

Select:

VPC:
production-vpc

Subnet:
public-subnet

Enable a public IPv4 address.

For production environments, you may also use an Elastic IP if a stable public IP is required.

Step 6: Create the Bastion Security Group

Create:

bastion-sg

The most important rule is the inbound SSH rule.

For example:

Type: SSH
Protocol: TCP
Port: 22
Source: YOUR_PUBLIC_IP/32

If your office's public IP is:

203.0.113.50

then:

SSH
TCP 22
203.0.113.50/32

This means only that IP address can attempt SSH access.

Avoid this:

SSH
TCP 22
0.0.0.0/0

That allows SSH connections from anywhere on the Internet.

Step 7: Configure the Private EC2 Security Group

Now suppose your application server has:

private-ec2-sg

Instead of allowing SSH from the Internet, configure:

Type: SSH
Port: 22
Source: bastion-sg

This is an important AWS security practice.

You are effectively saying:

Only instances associated with the bastion security group can SSH into this private EC2 instance.

The architecture becomes:

Internet
   |
   | SSH :22
   | YOUR_IP ONLY
   v
Bastion
   |
   | SSH :22
   | Source = bastion-sg
   v
Private EC2

Why Security Group References Are Better

You could allow SSH from the entire VPC:

10.0.0.0/16

But using the bastion's Security Group as the source is more precise:

Source:
bastion-sg

This creates a logical relationship between the two servers.

If the bastion's private IP changes, the rule doesn't need to be updated.

Connecting to the Bastion

Once the instance is running:

ssh -i bastion.pem ec2-user@<BASTION_PUBLIC_IP>

For Ubuntu:

ssh -i bastion.pem ubuntu@<BASTION_PUBLIC_IP>

After connecting:

ssh ec2-user@10.0.2.10

You are now connected to the private EC2 instance.

Don't Copy Your Private Key to the Bastion

A common beginner mistake is:

Laptop
   |
   | Copy private key
   v
Bastion
   |
   v
Private EC2

For example, copying:

production.pem

to the bastion.

This is generally not a good security practice.

If someone compromises the bastion and your private key is stored there, they may be able to use that key to access additional infrastructure.

A better approach is to use SSH Agent Forwarding or ProxyJump, or preferably use AWS Systems Manager where possible.

Using SSH Agent Forwarding

On your local machine:

ssh-add ~/.ssh/private-key.pem

Then connect:

ssh -A ec2-user@<BASTION_PUBLIC_IP>

From the bastion:

ssh ec2-user@10.0.2.10

Your private key remains on your local machine.

Using SSH ProxyJump

An even cleaner SSH workflow is ProxyJump.

Configure:

~/.ssh/config

Example:

Host bastion
    HostName <BASTION_PUBLIC_IP>
    User ec2-user
    IdentityFile ~/.ssh/bastion.pem

Host private-server
    HostName 10.0.2.10
    User ec2-user
    IdentityFile ~/.ssh/private.pem
    ProxyJump bastion

Now you can simply run:

ssh private-server

SSH automatically creates the connection:

Laptop
   |
   | SSH
   v
Bastion
   |
   | SSH
   v
Private EC2

Bastion Host Security Best Practices

A bastion host is itself a security-sensitive system.

It should therefore be hardened properly.

1. Restrict SSH access

Don't use:

0.0.0.0/0

Instead:

YOUR_OFFICE_IP/32

or your VPN CIDR.

2. Use SSH keys

Disable password-based SSH authentication where possible.

Use:

SSH Key Authentication

instead of:

Username + Password

3. Keep the operating system patched

Regularly apply security updates:

sudo dnf update -y

For Ubuntu:

sudo apt update
sudo apt upgrade -y

4. Keep the bastion minimal

Don't install unnecessary applications.

A bastion should primarily provide controlled administrative access.

Avoid turning it into:

Bastion
+ Jenkins
+ Docker
+ Database
+ Application
+ Monitoring
+ Random tools

The more software you install, the larger the attack surface becomes.

5. Monitor the bastion

Monitor:

  • SSH login attempts
  • Failed authentication
  • CPU
  • Memory
  • Disk
  • Network traffic
  • System logs

AWS services such as CloudWatch, CloudTrail, GuardDuty, and VPC Flow Logs can contribute to the monitoring and security architecture.

Bastion Host and High Availability

A single bastion can become a single point of failure.

For example:

               Internet
                  |
             Bastion-1
                  |
             Private VPC

If Bastion-1 goes down, administrators lose their normal SSH access path.

For larger environments, you may consider:

                  Internet
                     |
             +-------+-------+
             |               |
         Bastion-1       Bastion-2
             |               |
             +-------+-------+
                     |
               Private VPC

However, before deploying multiple bastions, consider whether AWS Systems Manager Session Manager would solve the access requirement more simply.

Bastion Host vs NAT Gateway

These two services are often confused.

They serve completely different purposes.

Bastion Host

Used for:

Inbound administrative access

Example:

Administrator
      |
      v
Bastion
      |
      v
Private EC2

NAT Gateway

Used primarily for:

Outbound Internet access from private resources

Example:

Private EC2
     |
     v
NAT Gateway
     |
     v
Internet Gateway
     |
     v
Internet

The private EC2 can download updates or access external APIs without accepting inbound Internet connections.

So:

Bastion = controlled administrative access INTO private infrastructure

NAT Gateway = outbound Internet access FROM private infrastructure

Bastion Host vs Internet Gateway

An Internet Gateway connects your VPC to the Internet.

It doesn't provide the same functionality as a bastion.

For example:

Internet
   |
   v
Internet Gateway
   |
   v
Public Subnet
   |
   v
Bastion

The Internet Gateway provides network connectivity.

The bastion provides a controlled administrative endpoint.

Bastion Host vs AWS Systems Manager

This is one of the most important modern AWS considerations.

Traditionally:

Administrator
     |
     | SSH
     v
Bastion
     |
     | SSH
     v
Private EC2

With Systems Manager Session Manager:

Administrator
     |
     | AWS API
     v
Systems Manager
     |
     v
Private EC2

You don't necessarily need:

  • Public IP
  • Port 22
  • SSH keys
  • Bastion server

What Is AWS Systems Manager Session Manager?

Session Manager allows administrators to access managed EC2 instances through AWS Systems Manager.

For example:

aws ssm start-session \
    --target i-xxxxxxxxxxxxxxxxx

You can get a shell on the EC2 instance without exposing SSH to the Internet.

This can significantly reduce the attack surface.

Bastion vs SSM Session Manager

FeatureBastion HostSSM Session ManagerPublic IPUsually requiredNoSSH port 22Usually requiredNoSSH private keysUsuallyNoIAM integrationIndirectStrongCloudTrail integrationLimitedStrongSession loggingAdditional configurationSupportedOperational overheadHigherLowerAWS-nativeYesYesRecommended for new AWS environmentsSituationalOften preferred

This doesn't mean bastions are obsolete.

They are still useful for environments that require traditional SSH, third-party tooling, legacy systems, or specific network access patterns.

Production-Grade AWS Access Architecture

For a modern AWS production environment, you could use:

                         AWS Account
                              |
                    +---------+---------+
                    |                   |
               IAM / SSO           CloudTrail
                    |
                    v
             Systems Manager
                    |
                    v
              Private Subnets
                    |
          +---------+---------+
          |                   |
          v                   v
      Application EC2       EKS
          |
          v
         RDS

There is no need to expose every server to the Internet.

If traditional SSH is required:

                    Trusted Network
                          |
                          v
                   Bastion Host
                          |
             +------------+------------+
             |            |            |
             v            v            v
          EC2 App       EKS        Internal VM
             |
             v
            RDS

Common Bastion Host Mistakes

Mistake 1: Allowing SSH from anywhere

0.0.0.0/0

Avoid this wherever possible.

Mistake 2: Storing private keys on the bastion

Don't casually copy production SSH keys onto the bastion.

Mistake 3: Giving the bastion excessive IAM permissions

Follow the principle of least privilege.

Mistake 4: Running unnecessary applications

Keep the bastion hardened and minimal.

Mistake 5: Giving private servers public IPs anyway

If the entire purpose of the architecture is controlled private access, don't undermine it by exposing the application servers.

Mistake 6: Ignoring logging

Administrative access should be auditable.

Bastion Host: Interview Explanation

If you're preparing for an AWS DevOps interview, you can explain it like this:

A bastion host is a hardened EC2 instance deployed in a public subnet that provides controlled administrative access to resources in private subnets. Instead of exposing private EC2 instances directly to the Internet, administrators connect to the bastion from trusted IP addresses and then access private resources through the VPC. The bastion Security Group should restrict SSH access to trusted sources, while private instances should allow SSH only from the bastion Security Group. In modern AWS architectures, Systems Manager Session Manager can often replace a traditional bastion by providing auditable access without exposing SSH or requiring public IP addresses.

Final Takeaway

A bastion host is essentially a controlled gateway into your private infrastructure.

The traditional architecture is:

                 Internet
                    |
             Trusted IP only
                    |
                    | SSH
                    v
             +-------------+
             |   Bastion   |
             | Public      |
             +-------------+
                    |
                    | SSH
                    v
             +-------------+
             | Private EC2 |
             +-------------+
                    |
                    v
                   RDS

The key security principle is:

Expose as little infrastructure as possible to the public Internet.

A bastion helps achieve this by providing a single, controlled administrative entry point.

However, for new AWS environments, don't automatically deploy a bastion just because it's a common architecture. Evaluate AWS Systems Manager Session Manager first. If it satisfies your operational requirements, it can provide a more secure and lower-maintenance way to access private EC2 instances.

For a DevOps engineer, understanding both patterns is important:

Traditional:
Administrator → Bastion → Private EC2

Modern AWS:
Administrator → SSM → Private EC2

The best architecture depends on your security requirements, operational model, compliance needs, and the tooling used by your team.