[AWS] Cutting NAT Gateway Costs — Building a NAT Instance with iptables (Part 2)

Overview

  1. Why a NAT Instance (instead of a NAT Gateway) — Part 1
  2. Using the AWS community NAT Instance AMI — Part 1
  3. Building a NAT Instance manually with iptables — Part 2 (this post)

In Part 1 we built a NAT Instance from the AWS community AMI. But that AMI runs an end-of-service, old OS that's hard to maintain. So in Part 2 we configure a NAT Instance directly on the latest AWS OS by manipulating iptables.

[AWS] Cutting NAT Gateway Costs — Building a NAT Instance from an AMI (Part 1)
A NAT Gateway costs $32+/month in fixed fees. To save money, I connected a Private Subnet to the internet using an EC2 NAT Instance — Part 1 uses the AWS community AMI (amzn-ami-vpc-nat).
taystudios.com/blog

The network diagram was covered in Part 1, so I'll just recap the situation. For a Private Subnet to reach the internet, you need a NAT Gateway (translating Public IP ↔ Private IP — essentially a router's role). Instead of an AWS NAT Gateway, we build a NAT Instance ourselves to cut costs. Part 2 covers how to make a plain OS with no NAT setup act as a NAT.

Configuring the NAT Instance (EC2)

1. Launch a basic EC2

Launching the NAT Instance EC2 from the latest Ubuntu LTS AMI Figure 1. Launching the NAT Instance EC2

Use the LTS AMI provided directly by AWS (not the community one). I prefer Ubuntu, so I chose Ubuntu 24.04 LTS. The NAT Instance must of course sit in the Public Subnet; all other route table examples are in Part 1.

2. Manipulating iptables on the EC2 OS (NAT setup)

What is iptables? A powerful Linux firewall / packet-filtering tool that can manage network traffic, act as a firewall, and implement Network Address Translation (NAT). Here we use its NAT feature, not the firewall.

SSH into the NAT Instance you created, then follow these steps.

2-1. Enable IP forwarding

echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

A NAT Instance must route traffic elsewhere (not to itself), so it must allow packets to come in and go out.

  • net.ipv4.ip_forward = 1 — enables IP forwarding.
  • /etc/sysctl.conf — applies the setting permanently.
  • sudo sysctl -p — applies the setting immediately.

/etc/sysctl.conf editor — net.ipv4.ip_forward commented out Figure 2. The /etc/sysctl.conf editor

Alternatively, opening sysctl.conf shows net.ipv4.ip_forward=1 disabled with a # as in Figure 2. You can just uncomment it and apply.

2-2. Add an iptables NAT rule

sudo iptables -t nat -A POSTROUTING -o {your-eth} -j MASQUERADE

This NAT rule translates the private network's IP to the NAT Instance's public IP. The options:

  • -t nat — add the rule to the NAT table.
  • -A POSTROUTING — apply NAT after routing (just before departure).
  • -o {eth} — apply to traffic leaving via your interface (e.g., eth0, ens5 — the public network interface).
  • -j MASQUERADE — translate the source IP to the NAT Instance's public IP (private network → internet).

Find your interface name with ip addr (or ifconfig after installing net-tools).

ip addr

Running ip addr — checking the network interface name (ens5) Figure 3. Checking the network interface name (e.g., ens5)

In the figure above, ens5 is the interface name.

2-3. Verify the result

sudo iptables -t nat -L -n -v

This command lists the NAT table applied to iptables.

  • -t nat — inspect the NAT table.
  • -L — list all chains and rules.
  • -n — show IPs instead of hostnames.
  • -v — show verbose info (packet/byte counts, etc.).

Comparing iptables before/after adding the NAT rule — POSTROUTING MASQUERADE Figure 4. Result after adding the iptables rule (top: before / bottom: after)

As in Figure 4, the MASQUERADE rule should be added to the POSTROUTING chain. Once confirmed, save it so the setting persists across reboots.

2-4. Persist the rules (survive reboots)

sudo apt-get update
sudo apt-get install -y iptables-persistent

iptables-persistent saves iptables rules and auto-loads them on boot. If it asks whether to save the current rules during install, answer yes to all.

sudo netfilter-persistent save

This saves the current rules to /etc/iptables/rules.v4 etc. Saved rules survive reboots.

sudo systemctl enable netfilter-persistent

Enable the service so the rules auto-load after a reboot.

That completes the OS-side NAT Instance setup. The rest (Private Subnet route table, disabling the EC2 source/destination check) is identical to Part 1.

3. Result — verifying Private Subnet internet connectivity

After building the NAT Instance, apt-get update succeeds on the Private Subnet EC2 Figure 5. NAT Instance result — internet connectivity confirmed

After finishing the setup, running sudo apt-get update on the Private Subnet EC2 confirms it now has internet.

Wrap-up

In Part 2 we configured the NAT Instance directly on the OS instead of using a community AMI. The remaining route table and console settings are identical to Part 1. Because I needed one instance to serve all three roles — Reverse Proxy / NAT Instance / Bastion — I run it configured manually on a version-managed, up-to-date OS as shown above.


📦 Migrated from my own Korean blog (my own writing). Original: taehyuklee.tistory.com/28

Share𝕏f

Comments