NAT Instance vs NAT Gateway: Self-Managed or Let AWS Handle It?
You’re reviewing this month’s AWS bill. The NAT Gateway line item stands out — $0.045/hour plus $0.045/GB of data processed. With dev/staging environments running 24/7, NAT Gateway costs can reach hundreds of dollars per month just for EC2 instances in private subnets to download packages or call external APIs.
A colleague suggests: “Why not use a NAT Instance for dev? Run it on a t3.micro — costs just a few bucks.”
NAT Instance — sounds familiar but you’ve never set one up. It’s also NAT, but runs on EC2 instead of using a managed service. So how does it work? Why do you need to disable Source/Destination Check? And when should you use it instead of NAT Gateway?
This post assumes you already understand the basics of VPC, subnets, and route tables. If not, read AWS VPC: Understanding VPC Through a Packet’s Journey first.
1. What Is NAT? (Quick Recap)
In a VPC, resources in a private subnet only have private IPs. The internet doesn’t know how to route to private IPs (which belong to the RFC 1918 range — IP addresses reserved for internal networks), so these resources cannot communicate directly with the internet.
NAT solves this by placing a “proxy” in the public subnet: when an EC2 instance in a private subnet wants to reach the internet, the packet is sent to the NAT, which replaces the source IP with its own public IP and forwards the packet out. When the response comes back, the NAT looks up its mapping table and routes it back to the original EC2.
AWS provides 2 ways to do NAT:
- NAT Gateway: a fully managed service by AWS — you create it, attach an Elastic IP, done. AWS handles everything else.
- NAT Instance: an EC2 instance you configure yourself to act as NAT — you manage everything from A to Z.
This post focuses on NAT Instance — how it works internally, then compares it with NAT Gateway.
2. NAT Instance — How It Works
2.1. Under the Hood: An EC2 Acting as NAT
A NAT Instance is essentially an EC2 instance running an AMI configured with IP forwarding and iptables. When a packet arrives, instead of processing it at the application layer, the OS changes the source IP and forwards the packet out through another interface — just like a traditional NAT router.
To function, a NAT Instance requires 4 things:
- Placed in a public subnet — because it needs a route to the internet via the Internet Gateway (IGW)
- Elastic IP attached — so the internet can send responses back
- Source/Destination Check disabled — details in the next section
- Route table configured — the private subnet must have a route
0.0.0.0/0 → eni-xxxpointing to the NAT Instance’s ENI
AWS used to provide a pre-configured Amazon Linux AMI for NAT Instances, but this AMI reached the end of standard support on December 31, 2020. If you want to use a NAT Instance today, you have 2 options: find an alternative AMI on the AWS Marketplace (several community AMIs come pre-configured for NAT, such as fck-nat — an open-source alternative optimized for cost), or configure it yourself from a standard Linux AMI — enable IP forwarding in the kernel (net.ipv4.ip_forward = 1) and set up iptables NAT rules.
2.2. Source/Destination Check — Why Must It Be Disabled?
This is the most interesting part of understanding NAT Instance. Before understanding why it must be disabled, let’s understand why AWS enables it by default.
What Is Source/Destination Check?
By default, AWS enables Source/Destination Check on every EC2 instance. This feature checks: does the packet arriving at or leaving this instance have a source or destination IP that matches the instance’s own IP? If not — the packet is dropped immediately.
Why Does AWS Enable It by Default?
This is a defense in depth mechanism that works independently of Security Groups and NACLs — meaning even if your SG or NACL is misconfigured, this layer still prevents dangerous behavior:
1. Preventing IP Spoofing — IP Spoofing is a technique where an attacker forges the source IP in a packet to deceive the target system. Attackers can use this in DDoS reflection attacks (spoofing the victim’s IP so servers send responses to the victim), or bypass firewalls by impersonating a trusted IP. Source/Dest Check ensures that outgoing packets must have the instance’s own IP as the source, preventing all forms of IP forgery.
2. Blocking Unauthorized Traffic Forwarding — Without this mechanism, a compromised EC2 instance could become a proxy to forward malicious traffic to other instances in the VPC, or perform lateral movement. By dropping all packets that don’t belong to the instance, AWS blocks this behavior at the infrastructure level.
3. Ensuring Routing Consistency — In a multi-tenant cloud environment, AWS needs to ensure traffic follows the defined route tables. If instances could freely forward traffic, any instance could “become a router” on its own and break the network topology, making troubleshooting and network auditing extremely difficult.
4. Secure by Default — AWS follows the Least Privilege principle: dangerous behaviors are blocked by default, and those who need them must explicitly opt in. Most workloads (web servers, app servers, databases) don’t need to forward traffic, so enabling Source/Dest Check by default makes sense.
| Purpose | Protects Against |
|---|---|
| Anti IP Spoofing | IP forgery attacks, DDoS reflection |
| Block unauthorized forwarding | Compromised instance as proxy, lateral movement |
| Ensure routing consistency | Misrouted traffic, audit difficulties |
| Secure by default | Accidental vulnerabilities from misconfiguration |
Why Must NAT Instance Disable It?
But NAT Instance is different — its entire job is to forward packets on behalf of others. Look at the packet it receives from an EC2 in a private subnet:
src: 10.0.2.20 ← IP of the EC2 in the private subnet
dst: 3.5.6.7 ← IP of an external serverThe NAT Instance has IP 10.0.1.30. Neither the source nor the destination matches the NAT Instance’s IP. With Source/Destination Check enabled, this packet gets dropped — the NAT Instance never even sees it.
Similarly, when the NAT Instance forwards a packet outbound, the original source IP (10.0.2.20) doesn’t match the NAT Instance’s IP → check fails → packet dropped again.
That’s why you must disable Source/Destination Check on the NAT Instance. Without this step, the NAT Instance is completely useless — it cannot forward a single packet. Besides NAT Instance, other use cases that require disabling this mechanism include: VPN instances, software firewalls, or any instance acting as a router.
With NAT Gateway, you don’t need to worry about this — AWS handles it automatically since it’s a managed service, not running on a regular EC2 instance.
2.3. Detailed Packet Flow
Let’s trace a packet from an EC2 in a private subnet to the internet via a NAT Instance:
| Entity | IP |
|---|---|
| EC2 (private subnet) | 10.0.2.20 |
| NAT Instance (public subnet) | Private: 10.0.1.30, EIP: 12.34.56.78 |
| External server | 3.5.6.7 |
Outbound:
| # | Location | src | dst | Action |
|---|---|---|---|---|
| 1 | EC2 | 10.0.2.20 | 3.5.6.7 | Create packet |
| 2 | Route Table (private) | 10.0.2.20 | 3.5.6.7 | Match 0.0.0.0/0 → eni-xxx → forward to NAT Instance |
| 3 | NAT Instance | 10.0.2.20 → 10.0.1.30 | 3.5.6.7 | iptables replaces src IP, saves mapping in conntrack |
| 4 | Route Table (public) | 10.0.1.30 | 3.5.6.7 | Match 0.0.0.0/0 → igw-xxx → forward to IGW |
| 5 | IGW | 10.0.1.30 → 12.34.56.78 | 3.5.6.7 | 1:1 NAT private → EIP |
| 6 | Internet | 12.34.56.78 | 3.5.6.7 | Server receives request |
Return:
| # | Location | src | dst | Action |
|---|---|---|---|---|
| 1 | Server | 3.5.6.7 | 12.34.56.78 | Send response |
| 2 | IGW | 3.5.6.7 | 12.34.56.78 → 10.0.1.30 | 1:1 NAT EIP → private |
| 3 | Route Table (public) | 3.5.6.7 | 10.0.1.30 | Match 10.0.0.0/16 → local |
| 4 | NAT Instance | 3.5.6.7 | 10.0.1.30 → 10.0.2.20 | Look up conntrack, replace dst IP |
| 5 | Route Table (private) | 3.5.6.7 | 10.0.2.20 | Match 10.0.0.0/16 → local |
| 6 | EC2 | 3.5.6.7 | 10.0.2.20 | Receive response |
Just like NAT Gateway, there are 2 NAT translations on the outbound path:
- NAT Instance (hop 3): many-to-1 — multiple EC2s share the NAT Instance’s private IP, differentiated by source port
- IGW (hop 5): 1-to-1 — fixed EIP mapping
2.4. Security Group for NAT Instance
Since a NAT Instance is an EC2, you can (and must) attach a Security Group. This is both an advantage (granular traffic control) and a responsibility (misconfigure it and you lose connectivity).
Minimum Security Group rules for a NAT Instance:
Inbound Rules:
| Type | Port | Source | Purpose |
|---|---|---|---|
| HTTP | 80 | 10.0.2.0/24 (Private Subnet CIDR) | Allow HTTP traffic from private subnet |
| HTTPS | 443 | 10.0.2.0/24 (Private Subnet CIDR) | Allow HTTPS traffic from private subnet |
| SSH | 22 | Your IP | Administer the NAT Instance (via IGW) |
Outbound Rules:
| Type | Port | Destination | Purpose |
|---|---|---|---|
| HTTP | 80 | 0.0.0.0/0 | Forward HTTP traffic to the internet |
| HTTPS | 443 | 0.0.0.0/0 | Forward HTTPS traffic to the internet |
For comparison: NAT Gateway does not support Security Groups — you can only control traffic using NACLs. With a NAT Instance, you get both layers of protection: Security Group (stateful, instance-level) + NACL (stateless, subnet-level).
3. Zonal NAT Gateway — Managed Service (Recap)
NAT Gateway is the option AWS recommends by default. Instead of managing your own EC2, you simply:
- Create a NAT Gateway in a public subnet
- Attach an Elastic IP
- Configure the route table:
0.0.0.0/0 → nat-xxx
AWS handles everything else: patching, monitoring, scaling, and high availability within the AZ. Bandwidth auto-scales from 5 Gbps up to 100 Gbps — no need to choose an instance type or worry about bottlenecks.
Note: EC2 instances in the same subnet as the NAT Gateway cannot use it — only EC2 instances from other subnets can route traffic through the NAT Gateway. This is why the NAT Gateway is always placed in a public subnet, while EC2 instances that need NAT reside in a private subnet.
For details on how NAT Gateway works (PAT, double NAT, connection tracking), see AWS VPC: Understanding VPC Through a Packet’s Journey — Flow 3: EC2 → Stripe.
Important note about High Availability: NAT Gateway has automatic HA within a single AZ. If that AZ goes down, the NAT Gateway goes down too. For region-wide HA, you need to deploy one NAT Gateway per AZ and configure route tables accordingly for each AZ’s private subnets. You don’t need to worry about cross-AZ failover — because when an AZ goes down, the EC2 instances in that AZ go down as well, so they no longer need NAT.
The section above describes Zonal NAT Gateway — the traditional type that operates within a single AZ. Since late 2025, AWS has launched Regional NAT Gateway with significant improvements — details in the next section.
4. Regional NAT Gateway — A Major Step Forward (2025)
In November 2025, AWS announced regional availability mode for NAT Gateway. With this feature, you can create a single NAT Gateway that automatically scales up and down across Availability Zones (AZs) in your VPC based on workload presence, maintaining high availability while simplifying setup and management.
This is a major step forward because previously, achieving region-wide HA required manually deploying a NAT Gateway in each AZ. Now AWS handles that for you.
4.1. Key Differences from Zonal NAT Gateway
1. No Public Subnet Required
Traditionally, NAT Gateway required a public subnet (a subnet with a direct route to the internet via IGW) for internet access. Security-conscious customers had to ensure no one created other resources in that public subnet. With Regional NAT Gateway, your VPC doesn’t need a public subnet — this is a major security improvement because VPCs can now be completely private while still having internet egress.
2. Automatic AZ Scaling
Regional NAT Gateway operates at the VPC level. When you expand your workload to a new AZ, you can reuse the same Regional NAT Gateway ID. Regional NAT Gateway automatically scales and maintains zonal affinity to ensure high availability.
3. Simplified Routing
With Zonal NAT Gateway, you need to create separate route tables for each AZ — each pointing to that AZ’s NAT Gateway. With Regional NAT Gateway, you only need a single route table with the entry 0.0.0.0/0 → rnat-xxx, shared across all private subnets in every AZ.
4. Two Operating Modes
Regional NAT Gateway operates in 2 modes:
- Automatic mode: AWS manages IP addresses and handles AZ changes automatically — you don’t need to intervene when adding or removing AZs
- Manual mode: you manage IP addresses yourself and are responsible for adjusting the gateway per AZ — suitable when you need tight control over egress IP addresses (e.g., for allowlisting)
4.2. Limitations
1. No Private NAT Support: Regional NAT Gateway requires an Internet Gateway (IGW) in the VPC — meaning it only supports public NAT (traffic to the internet). If you need private NAT (internal traffic between VPCs or to on-premise), you must still use Zonal Private NAT Gateway.
2. No Significant Cost Reduction: Regional NAT Gateway simplifies operations, but costs are comparable to deploying multi-AZ Zonal NAT Gateways. If your goal is to reduce costs, consider migrating to IPv6 instead of using NAT.
4.3. AWS Recommendation
AWS recommends migrating to Regional NAT Gateway for all use cases except those requiring private connectivity — a feature that Regional NAT Gateway does not yet support.
5. Detailed Comparison
| Criteria | NAT Instance | Zonal NAT Gateway | Regional NAT Gateway |
|---|---|---|---|
| Nature | EC2 instance running a NAT-configured AMI | Managed service, operates in 1 AZ | Managed service, operates at region level |
| Management | You manage everything (patch, update, monitor) | AWS manages everything | AWS manages everything |
| High Availability | Not built-in — must self-setup with ASG multi-AZ | Automatic HA within 1 AZ | Automatic HA across region (multi-AZ) |
| Bandwidth | Depends on instance type | Auto-scales 5–100 Gbps | Auto-scales 5–100 Gbps |
| Cost | Pay for EC2 instance (cheaper with small instances) | $0.045/hr + $0.045/GB | Comparable to Zonal NAT Gateway |
| Security Group | Can attach | Not supported (NACL only) | Not supported (NACL only) |
| Use as Bastion host | Possible | Not possible | Not possible |
| Port forwarding | Supported | Not supported | Not supported |
| Source/Destination Check | Must disable manually | Not needed (AWS handles it) | Not needed (AWS handles it) |
| Elastic IP | Can use EIP or public IP | EIP required | Supported (depends on mode) |
| Public subnet | Required | Required | Not required |
| Private NAT | Can be configured | Supported | Not yet supported |
| Routing for multi-AZ | Separate route table per AZ | Separate route table per AZ | Single shared route table |
6. When Should You Use Which?
Use Regional NAT Gateway when:
- Production environments requiring multi-AZ HA without managing multiple NAT Gateways
- You want to simplify routing — just 1 shared route table for all AZs
- You want enhanced security — no public subnet needed in the VPC
- When adding new AZs, you don’t want to create additional NAT Gateways and modify route tables
- This is the new AWS recommendation, replacing Zonal NAT Gateway for most use cases
Use Zonal NAT Gateway when:
- You need private NAT (internal traffic between VPCs or to on-premise) — Regional NAT Gateway doesn’t support this yet
- You already have stable multi-AZ NAT Gateway infrastructure and don’t need to migrate yet
Use NAT Instance when:
- You want to save costs for dev/test environments — a
t3.microcosts only ~$7–8/month compared to ~$32/month for NAT Gateway (excluding data processing fees) - You need port forwarding — for example, forwarding port 3306 from the internet to an RDS in a private subnet (NAT Gateway doesn’t support this)
- You want to also use it as a bastion host
- You need granular traffic control via Security Groups instead of just NACLs
- Internet traffic is low and predictable
SAA exam tip: Regional NAT Gateway is a new topic that will appear in exams. Remember the key concepts: Source/Destination Check (must be disabled for NAT Instance), Security Group (only NAT Instance supports it), Regional NAT Gateway (automatic multi-AZ HA, no public subnet needed, single shared route table), and Private NAT (only Zonal NAT Gateway supports it, Regional does not yet).
Conclusion
Back to the original question: your colleague suggested using a NAT Instance for the dev environment — this is a reasonable choice if you accept the trade-off of manual management and no built-in HA in exchange for lower costs.
Key takeaways:
- NAT Instance is a self-managed EC2 that requires disabling Source/Destination Check because it forwards packets that don’t belong to it — neither the source nor destination IP matches its own
- Zonal NAT Gateway is a managed service operating within a single AZ — AWS handles HA, scaling, and patching — but requires deployment in each AZ for region-wide HA
- Regional NAT Gateway is a major step forward (2025) — automatic multi-AZ HA, no public subnet needed, and only 1 route table shared across all AZs
- NAT Instance supports Security Groups, can serve as a bastion host, and enables port forwarding — capabilities that NAT Gateway lacks
- Regional NAT Gateway is the new AWS recommendation for most use cases; NAT Instance is suitable for dev/test or when you need special features
- Regional NAT Gateway does not yet support private NAT — if you need private connectivity, you must still use Zonal NAT Gateway