Course outline

3.4

Bastion Hosts, NAT Instances and NAT Gateways

Reaching private servers safely, and giving them outbound internet without exposing them: NAT step by step, high availability and cost.

30 min Reading + animations

Tip: tap or hover any dotted-underlined term to see what it means.

By the end of this lesson you will be able to

  • Give administrators a shell on private servers with a bastion host, and configure its security groups correctly.
  • Explain why Session Manager (and EC2 Instance Connect Endpoint) is the modern answer when the exam says “no inbound ports”.
  • Describe how NAT lets private servers reach the internet while staying unreachable from it.
  • Build a NAT instance correctly (source/destination check, Elastic IP, routes) and know why it is legacy.
  • Configure NAT gateways for high availability, compare zonal and Regional NAT gateways, and know when a private NAT gateway is the answer.

#Two problems that private subnets create

Lesson 3.3 made the database and application servers private: no public address, no route to the internet gateway. That is the right default, but it creates two everyday problems:

  1. Getting in to administer them. How does an engineer open a shell on a server that cannot be reached from the internet?
  2. Letting them out. How does a private server download security patches, pull a container image or call a payment API, without becoming reachable from the internet?

The first is solved by a bastion host (or, better, by Session Manager). The second by NAT: a NAT gateway or, in older designs, a NAT instance.

#Bastion hosts

A bastion host (also called a jump box) is a small EC2 instance in a public subnet with a public or Elastic IP. Administrators SSH (port 22, Linux) or RDP (port 3389, Windows) to the bastion first, and from there to the private servers.

The whole design lives or dies on two security groups:

Security groupInbound ruleWhy
Bastion hostPort 22 (or 3389) only from the admins’ address, for example 203.0.113.25/32 or the office rangeNever 0.0.0.0/0: an SSH port open to the world is attacked within minutes
Private serversPort 22 (or 3389) only from the bastion’s security group (sg-…)Referencing the group, not an IP, keeps working when the bastion is replaced, and excludes every other machine in the public subnet

Getting a shell on a private server

The database server has no public address. Compare the classic bastion host with Session Manager, which needs no open port at all.

Scenario

Bastion SG: SSH from 203.0.113.25/32. Database SG: SSH from the bastion’s security group.

VPC10.0.0.0/16
Public subnet
Private subnet
Admin203.0.113.25
Scanner192.0.2.66
Bastion host10.0.0.20source 203.0.113.25/32 ✓
Web server10.0.0.30
Database server10.0.10.40
SSH :22
Step 1 of 3
1

The admin connects with SSH (port 22) to the bastion’s public address. The bastion’s security group allows port 22 only from the admin’s own address, 203.0.113.25/32.

#The modern answer: no inbound port at all

A bastion is still a server: you must patch it, protect its SSH keys, keep its allowed IP list current, and pay for it. AWS offers two ways to avoid it entirely:

  • AWS Systems Manager Session Manager. An agent on the instance (pre-installed on Amazon Linux and recent Windows AMIs) makes an outbound HTTPS connection to the Systems Manager service. You open a shell from the console or CLI, and IAM decides who may connect to which instance. No inbound rules, no port 22, no SSH keys, no public IP, and every session can be logged to S3 or CloudWatch Logs. The instance needs an IAM role with Systems Manager permissions and a path out (a NAT gateway or VPC endpoints for Systems Manager).
  • EC2 Instance Connect Endpoint. An endpoint you create in the VPC that tunnels SSH or RDP from the console or CLI to private instances, authenticated by IAM, without a bastion or public IP.

#NAT: out yes, in no

NAT (Network Address Translation) rewrites the source address of outgoing packets. A NAT device in a public subnet takes a packet from a private server (10.0.10.10), replaces the source with its own public address, remembers the pairing in a translation table, and when the reply comes back, reverses the swap.

The crucial property is in the table: an entry exists only because a server inside started the conversation. A packet that arrives from the internet without a matching entry has nowhere to go and is dropped. That gives private servers outbound access with no inbound exposure.

A NAT gateway lets private servers out, never in

An app server in a private subnet downloads security patches. Follow the addresses as they are rewritten.

Scenario

Private route table: 0.0.0.0/0 → nat-… · Public route table: 0.0.0.0/0 → igw-…

VPC10.0.0.0/16
Public subnet10.0.0.0/24
Private subnet10.0.10.0/24
App server10.0.10.10
NAT gateway10.0.0.50 · EIP 203.0.113.80
Internet gateway
Patch server198.51.100.30
HTTPS

Leaving the app server

Source

10.0.10.10:43122

Destination

198.51.100.30:443

Step 1 of 4
1

The app server connects to the patch server. Its subnet’s route table sends 0.0.0.0/0 to the NAT gateway, which sits in a public subnet.

#NAT instances (the old way)

Before NAT gateways existed, you ran NAT yourself on an EC2 instance. It still appears in exam questions as the “older option”, mostly around one setting.

How to build one:

  1. Launch it in a public subnet with an Elastic IP (or public IP).
  2. Disable the source/destination check on the instance. By default every EC2 instance drops traffic that is not addressed from or to itself; a NAT instance forwards other machines’ traffic, so with the check on it silently discards everything. This is the exam’s favourite NAT question.
  3. In the private subnets’ route table, send 0.0.0.0/0 to the NAT instance (i-…).
  4. Its security group must allow HTTP/HTTPS from the private subnets inbound, and HTTP/HTTPS to the internet outbound (plus SSH from your network if you administer it).

Why nobody builds them for new systems:

  • You manage everything: OS patches, the NAT software, monitoring.
  • No built-in high availability: if the instance or its AZ fails, private servers lose the internet until a script or Auto Scaling group replaces it.
  • Bandwidth is limited by the instance type.
  • The ready-made NAT AMI that AWS used to provide was based on Amazon Linux 1, which reached end of support; today you would have to configure NAT on a current image yourself.

#NAT gateways

A NAT gateway is NAT as a managed service, and the default choice today.

PropertyDetail
Fully managedNo OS, no patching, no software to run.
BandwidthStarts at 5 Gbps and scales automatically up to 100 Gbps.
PlacementA (zonal, public) NAT gateway is created in a specific AZ, in a public subnet, and uses an Elastic IP. It is redundant within its AZ.
Needs an internet gatewayIts subnet must route 0.0.0.0/0 to the VPC’s IGW; without one, it cannot reach the internet.
No security groupsYou cannot attach a security group to a NAT gateway; control traffic with security groups on the instances and network ACLs on the subnets.
Who can use itInstances in other subnets route to it; it is not used by instances in its own (public) subnet, which go to the IGW directly.
IPv4 only for internet egressFor IPv6 servers that need outbound-only internet, use an egress-only internet gateway instead (Chapter 22).
CostCharged per hour and per GB processed, on top of normal data transfer.

#NAT instance vs NAT gateway

NAT instanceNAT gateway
ManagementYou (OS, software, patches)AWS
AvailabilityBuild it yourself (scripts, Auto Scaling)Redundant within its AZ; one per AZ for full HA
BandwidthDepends on instance type5 Gbps, scaling to 100 Gbps
Security groupsYes, plus network ACLsNo; network ACLs only
Source/destination checkYou must disable itNot applicable
Use as a bastion tooPossible (not recommended)No
CostInstance hours + data transferGateway hours + per-GB processing + data transfer

Self-checkQuestion

Private instances must download updates from the internet but must never be reachable from it. The solution must be highly available and need no maintenance. What should you use?

#NAT gateways and high availability

A zonal NAT gateway is highly available inside one AZ, but if that whole AZ fails, the gateway fails with it. The rule for production:

  • Create one NAT gateway in each AZ you use, each in that AZ’s public subnet.
  • Give each AZ’s private subnets their own route table, pointing 0.0.0.0/0 at the NAT gateway in the same AZ.

This avoids two problems with sharing a single NAT gateway:

  1. Single point of failure: if the AZ with the only NAT gateway fails, the surviving AZs lose internet access too.
  2. Cross-AZ data charges: every gigabyte from AZ B to a NAT gateway in AZ A is billed as inter-AZ transfer, on top of NAT processing.

You do not need cross-AZ failover for NAT: if an AZ fails, the servers that used its NAT gateway are gone too.

NAT gateways and Availability Zones

A zonal NAT gateway lives in one AZ. Compare one per AZ, one shared, and the Regional NAT gateway when an AZ fails.

Scenario

Route table AZ A: 0.0.0.0/0 → NAT A · Route table AZ B: 0.0.0.0/0 → NAT B

VPC
AZ A
AZ B
Public
Private
Public
Private
Internet gateway
NAT gateway A
NAT gateway B
App servers
App servers
Step 1 of 2
1

Each AZ has its own NAT gateway in its public subnet, and each AZ’s private route table points to the NAT gateway in the same AZ. Traffic never crosses AZs.

#Regional NAT gateways

Since November 2025, a NAT gateway can also be created in regional availability mode. A Regional NAT gateway is attached to the VPC rather than to a public subnet:

  • one NAT gateway (one ID, one route target) serves every AZ of the VPC;
  • AWS adds and removes AZs automatically as your workloads appear in them (expanding to a new AZ can take some time, up to about an hour);
  • no public subnets are needed to host it, and it has its own route table;
  • in automatic mode AWS also manages its public IP addresses; in manual mode you supply Elastic IPs.

It gives the resilience of “one per AZ” with far less to manage. Zonal NAT gateways remain available, and are still required for private NAT (below).

#Public vs private NAT gateways

NAT gateways have two connectivity types:

Public NAT gatewayPrivate NAT gateway
Created inA public subnetA private subnet
Elastic IPRequiredNone
Internet gatewayRequiredNot used
Used forPrivate subnets → internetPrivate subnets → other VPCs or on-premises networks through a transit gateway or virtual private gateway
Typical reasonOutbound internetThe other network overlaps your range, or only accepts traffic from a small allowed range

A private NAT gateway translates your servers’ addresses into an address from a small, non-overlapping “routable” range of your VPC, so traffic can reach a network that overlaps with the rest of your range. It never touches the internet.

#Try it yourself

Create a NAT gateway for a private subnet (replace the IDs with yours; a NAT gateway costs money per hour, so delete it afterwards):

EIP=$(aws ec2 allocate-address --domain vpc --query AllocationId --output text)
NAT=$(aws ec2 create-nat-gateway --subnet-id $PUBLIC_SUBNET --allocation-id $EIP \
      --query NatGateway.NatGatewayId --output text)
aws ec2 wait nat-gateway-available --nat-gateway-ids $NAT

aws ec2 create-route --route-table-id $PRIVATE_RT --destination-cidr-block 0.0.0.0/0 --nat-gateway-id $NAT

# From a private instance (via Session Manager), check which public address the internet sees:
curl -s https://checkip.amazonaws.com      # prints the NAT gateway's Elastic IP

# Session Manager instead of SSH (needs the AWS CLI Session Manager plugin):
aws ssm start-session --target i-0123456789abcdef0

# Clean up
aws ec2 delete-nat-gateway --nat-gateway-id $NAT
aws ec2 release-address --allocation-id $EIP   # after the NAT gateway is deleted

#Summary

Key takeaways

  • A bastion host is an EC2 instance in a public subnet used to reach private servers. Its security group allows SSH/RDP only from admin IPs; the private servers allow SSH/RDP only from the bastion’s security group.
  • Session Manager (or EC2 Instance Connect Endpoint) gives shell access with no inbound ports, no SSH keys and no bastion, controlled by IAM and fully logged: the exam’s answer to “without opening port 22”.
  • NAT rewrites the source address outbound and keeps a translation table, so private servers can start connections out but nothing can start a connection in.
  • A NAT instance needs a public subnet, an Elastic IP, routes pointing to it, and source/destination check disabled; it is self-managed and legacy.
  • A NAT gateway is managed, 5–100 Gbps, in a public subnet with an Elastic IP, needs an internet gateway, has no security groups, and handles IPv4 only (IPv6 uses an egress-only internet gateway).
  • For high availability use one NAT gateway per AZ with per-AZ route tables, or a Regional NAT gateway. One shared NAT gateway is a single point of failure and adds cross-AZ charges.
  • A private NAT gateway (no EIP, no IGW) connects to other VPCs or on-premises networks with overlapping ranges.