AWS Public & Private Subnets with NAT Instance — Free-Tier Tutorial

AWS Public & Private Subnets with NAT Instance — Free-Tier Tutorial

When learning AWS networking, one of the first hurdles is understanding public vs. private subnets — AWS doesn’t label them for you; it all depends on how outbound traffic is routed.
In this blog, we’ll build a full VPC from scratch… buckle up, this is going to be fun! We’ll create:

  • public subnet for instances reachable from the Internet
  • private subnet for instances isolated from the Internet
  • NAT instance to allow private instances to securely access the Internet

We’ll keep it free-tier friendly by using t3.micro instances. By the end, you’ll know not just the steps, but why each component matters.

Preparation: Setting Up for Success

AWS permissions, regions, and limits matter before starting a lab — no one likes surprise bills 😅. Using the root account is risky and unnecessary; an IAM user with admin permissions is sufficient. Selecting the right region ensures the services you need are available and helps stay within free-tier limits. With these steps, our environment is ready for a smooth lab experience without permission headaches or unexpected charges.

What We Need to Do

  • Use an IAM user with AdministratorAccess
    Prevents “permission denied” errors when creating VPCs, subnets, route tables, or EC2 instances.
  • Avoid using the root account for routine labs
    Root access is safer to reserve for account-level changes.
  • Select an appropriate AWS region
    • I’m using us-east-1 (N. Virginia), but any region with free-tier coverage works.

Steps

  • Log in with your IAM user
  • Confirm the AWS region is set (us-east-1 for this walkthrough)

💡 Tip: Checking your AWS usage regularly prevents unexpected charges, especially when working with EC2, NAT instances, or leftover resources from labs.

Create a VPC

VPC (Virtual Private Cloud) is your isolated network in AWS. It defines the IP address range for all subnets and resources. Planning your CIDR wisely ensures you have enough IPs and avoids conflicts later. All subnets, route tables, and gateways live inside a VPC. Without it, resources cannot communicate or be properly segmented.

💡 Tip: Use a /16 CIDR (e.g., 10.0.0.0/16). This provides over 65,000 IP addresses — more than enough for multiple subnets.

Steps

  1. Go to VPC → Your VPCs → Create VPC
  2. Set the following:
  • Name: LabVPC
  • IPv4 CIDR: 10.0.0.0/16
  • Tenancy: Default
  1. Click Create VPC

Pro Insight: The VPC defines the routing scope for all subnets, route tables, and gateways. Choosing it thoughtfully simplifies the rest of the lab.

Create Subnets

A subnet is considered public when its route table sends Internet-bound traffic to an Internet Gateway (IGW). If it lacks that route and instead uses a NAT device for outbound traffic, it is treated as private.

  • Public subnet: Direct Internet access through an Internet Gateway (IGW).
  • Private subnet: No direct Internet access; traffic flows through a NAT instance for controlled access.

💡 Tip: Assign subnets to specific Availability Zones (AZs) for consistency.

2a. Create a Public Subnet

Steps

  1. Go to VPC → Subnets → Create Subnet
  2. Set the following:
    • Name: PublicSubnet
    • VPC: LabVPC
    • AZ: Choose preferred
    • IPv4 CIDR: 10.0.1.0/24
  1. Click Create Subnet

Pro Insight: Resources that need direct Internet access, like web servers, go here. Subnets themselves do not incur charges.

2b. Create a Private Subnet

Steps:

  1. Go to VPC → Subnets → Create Subnet
  2. Set the following:
    • Name: PrivateSubnet
    • VPC: LabVPC
    • AZ: Choose as preferred
    • IPv4 CIDR: 10.0.2.0/24
  1. Click Create Subnet

Pro Insight: Private subnets isolate resources from direct Internet access. Any outbound traffic will pass through a NAT instance, ensuring controlled connectivity.

Create an Internet Gateway (IGW)

An Internet Gateway (IGW) provides your VPC with Internet connectivity. Only subnets whose route tables point to the IGW can send or receive Internet traffic.

Steps to create IGW:

  1. Go to VPC → Internet Gateways → Create IGW
  2. Name it LabIGW
  3. Attach to LabVPC

Pro Insight: Attaching the IGW enables Internet access for public resources. This is a free-tier safe operation; charges only occur when instances send traffic.

Configure Route Tables

Route tables control the flow of traffic within the VPC. A public route table sends 0.0.0.0/0 to the IGW, while a private route table sends it to a NAT device. This routing behavior is what determines whether a subnet is public or private.

Routing Overview

  • Public subnet: 0.0.0.0/0 → IGW
  • Private subnet: 0.0.0.0/0 → NAT instance (we will configure it later)

4a. Public Route Table

Steps:

  1. Go to VPC → Route Tables → Create Route Table
  2. Name: PublicRT
  3. VPC: LabVPC
  4. Add route: 0.0.0.0/0 → LabIGW
  5. Associate with PublicSubnet

Pro Insight: Public resources now have Internet access.

4b. Private Route Table

Steps:

  1. Go to VPC → Route Tables → Create Route Table
  2. Name: PrivateRT
  3. VPC: LabVPC
  4. Do not add an Internet route yet, we will add after NAT instance is configured
  5. Associate with PrivateSubnet

Pro Insight: Private resources remain isolated until the NAT instance is configured.

Launch EC2 Instances

We will use one public EC2 as a bastion/NAT instance and one private EC2 to test connectivity. Security groups define who can connect to whom. Public instance can access Internet directly; private instance can only access Internet via NAT.

5a. Public EC2

Steps:

  1. Launch t3.micro (free-tier)
  2. Set
    • Name: PublicEc2
    • Key pair: Create/download Public-Ec2-keypair.pem
    • VPC: LabVPC, Subnet: PublicSubnet, Auto-assign Public IP: Enable
    • Security group: Allow SSH (22) from public IP only
  1. Click Launch

5b. Private EC2

Steps:

  1. Launch t3.micro (free-tier)
  2. Set
    • Name: PrivateEc2
    • Key pair: Create/download Private-Ec2-keypair.pem
    • VPC: LabVPC, Subnet: PrivateSubnet, Auto-assign Public IP: Disable
    • Security group: Allow SSH (22) only from PublicEc2 security group
  1. Click Launch

Pro Insight: Private EC2 has no direct Internet access. All outbound traffic will pass through the NAT instance. SSH access is restricted to the public EC2 for controlled administration.

Auto-assigning a public IP determines whether an EC2 instance can be reached directly from the Internet. For public instances, like a web server or bastion host, this option is enabled so the instance receives a public IP automatically. This allows direct access for SSH or HTTP traffic without needing to manually attach an Elastic IP.

In contrast, private instances, such as databases or internal servers, have auto-assign public IP disabled to keep them isolated from the Internet. These instances communicate externally only through a NAT instance or NAT Gateway, which allows controlled Internet access while keeping the instance’s private IP hidden.

Configure NAT on Public EC2

A NAT (Network Address Translation) instance allows private instances to access the Internet without exposing their private IPs. Next we will:

Disable Source/Destination Check on PublicEc2. By default, an EC2 instance is only allowed to send and receive traffic for itself — AWS blocks any packets not sourced from or destined to that instance. Disabling Source/Dest Check allows the instance to forward traffic on behalf of private hosts, which is required for it to function as a NAT instance.

Steps to disable:

  1. Go to EC2 → Instances
  2. Select PublicEc2
  3. Click Actions → Networking → Change source/dest check
  4. Select Stop and save

Enable IP forwarding and configure NAT using nftables. Enabling IP forwarding and configuring nftables turns the instance into a functional router that can translate private IP addresses for outbound Internet access.

Steps:

# Fix key permissions 
chmod 400 Public-Ec2-keypair.pem

# SSH into public EC2 and 
ssh -i Public-Ec2-keypair.pem ec2-user@44.203.210.91

# Install nftables
sudo dnf install -y nftables

# Enable IPv4 forwarding
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf

# Configure NAT
sudo nft add table ip nat
sudo nft 'add chain ip nat POSTROUTING { type nat hook postrouting priority 100 ; }'
sudo nft add rule ip nat POSTROUTING oif ens5 masquerade

# Save rules
sudo sh -c 'nft list ruleset > /etc/nftables.conf'

# Enable service
sudo systemctl enable nftables

Pro Insight: NAT Gateways are the recommended option in production because they are fully managed, highly available, and scale automatically. However, they are not free-tier and incur additional costs. For labs and learning environments, a NAT instance is the better choice — free-tier friendly and perfect for understanding how NAT works.

Add Private Subnet Route to NAT

Private subnet needs a default route to the NAT instance. This tells private EC2s where to send traffic destined for the Internet.

Steps:

  1. Go to VPC → Route Tables → PrivateRT → Edit routes
  2. Add route: 0.0.0.0/0 → NAT instance ID

Pro Insight: Private EC2 can now access the Internet securely through the NAT instance.

Testing Internet Connectivity

“I’ve summarized both scenarios in the tables below — now it’s time for some hands-on testing… yayyy! 🎉 Let’s see our public and private instances in action.”

Instance

Method

Test

Result 

Public Ec2

Direct

Ping 8.8.8.8/curl http://checkip.amazonaws.com 

Shows public Ip

Private Ec2

Via NAT

Ping 8.8.8.8/curl http://checkip.amazonaws.com 

Shows NAT/public Ip

  1. Public EC2:

We can log into the public instance using the assigned key pair. Once logged in, we can verify internet connectivity:

ssh -i Public-Ec2-keypair.pem ec2-user@44.203.210.91
ping 8.8.8.8
curl http://checkip.amazonaws.com

A successful response from ping and curl confirms that the instance has proper internet connectivity.

  1. Private Ec2:

We can log into the private instance via the public instance using key forwarding. This allows access while keeping the private key secure. After login, internet connectivity can be tested:

eval "$(ssh-agent -s)"
ssh-add Private-Ec2-keypair.pem
ssh -A -i Public-Ec2-keypair.pem ec2-user@44.203.210.91
ssh ec2-user@10.0.2.64
ping 8.8.8.8
curl http://checkip.amazonaws.com

A successful ping and curl indicates that the private instance can reach the internet through the NAT instance.

Pro Insight: Public EC2 accesses the Internet directly. Private EC2 accesses the Internet via NAT with its private IP hidden.

Summary & Key Takeaways

  • Public and private subnets are now correctly configured.
  • NAT instance enables secure Internet access for private resources.
  • Route tables and security groups control traffic flow.
  • SSH access to private instances is secure via public EC2 (bastion host).

If you’ve followed along without your head exploding, give yourself a pat on the back 🎉.

💡 Optional: For a stable public IP on the NAT instance, attach an Elastic IP.An Elastic IP is a static, never-changing public IP address that stays with your instance even if it stops, starts, or reboots. Using an Elastic IP ensures your NAT instance always has the same public IP for consistent outbound traffic.

Cleanup Tip: Terminate EC2 instances, remove NAT configuration, delete route tables, subnets, IGW, and check for leftover Elastic IPs to avoid charges.