Create AWS VPC NAT Gateways

The aws:ec2/natGateway:NatGateway resource, part of the Pulumi AWS provider, provisions NAT gateways that enable outbound internet access or inter-subnet routing for instances in private subnets. This guide focuses on four capabilities: public NAT for internet access, private NAT for subnet-to-subnet routing, regional multi-AZ deployment, and secondary IP allocation.

NAT gateways belong to a VPC and require subnets, Elastic IPs for public connectivity, and an Internet Gateway for public NAT configurations. The examples are intentionally small. Combine them with your own route tables and security groups.

Enable internet access for private subnets

Most VPCs allow instances in private subnets to reach the internet for software updates and API calls while preventing inbound connections.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.ec2.NatGateway("example", {
    allocationId: exampleAwsEip.id,
    subnetId: exampleAwsSubnet.id,
    tags: {
        Name: "gw NAT",
    },
}, {
    dependsOn: [exampleAwsInternetGateway],
});
import pulumi
import pulumi_aws as aws

example = aws.ec2.NatGateway("example",
    allocation_id=example_aws_eip["id"],
    subnet_id=example_aws_subnet["id"],
    tags={
        "Name": "gw NAT",
    },
    opts = pulumi.ResourceOptions(depends_on=[example_aws_internet_gateway]))
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := ec2.NewNatGateway(ctx, "example", &ec2.NatGatewayArgs{
			AllocationId: pulumi.Any(exampleAwsEip.Id),
			SubnetId:     pulumi.Any(exampleAwsSubnet.Id),
			Tags: pulumi.StringMap{
				"Name": pulumi.String("gw NAT"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			exampleAwsInternetGateway,
		}))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.Ec2.NatGateway("example", new()
    {
        AllocationId = exampleAwsEip.Id,
        SubnetId = exampleAwsSubnet.Id,
        Tags = 
        {
            { "Name", "gw NAT" },
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            exampleAwsInternetGateway,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.NatGateway;
import com.pulumi.aws.ec2.NatGatewayArgs;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new NatGateway("example", NatGatewayArgs.builder()
            .allocationId(exampleAwsEip.id())
            .subnetId(exampleAwsSubnet.id())
            .tags(Map.of("Name", "gw NAT"))
            .build(), CustomResourceOptions.builder()
                .dependsOn(exampleAwsInternetGateway)
                .build());

    }
}
resources:
  example:
    type: aws:ec2:NatGateway
    properties:
      allocationId: ${exampleAwsEip.id}
      subnetId: ${exampleAwsSubnet.id}
      tags:
        Name: gw NAT
    options:
      dependsOn:
        - ${exampleAwsInternetGateway}

The NAT gateway sits in a public subnet and uses an Elastic IP for its public address. The allocationId references the EIP, and subnetId places the gateway in a public subnet with internet gateway routing. The dependsOn ensures the internet gateway exists before creating the NAT gateway, which is required for public connectivity.

Route traffic between private subnets without internet access

Some architectures need NAT functionality for inter-subnet routing without exposing traffic to the internet, such as connecting isolated workload subnets to shared service subnets.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.ec2.NatGateway("example", {
    connectivityType: "private",
    subnetId: exampleAwsSubnet.id,
});
import pulumi
import pulumi_aws as aws

example = aws.ec2.NatGateway("example",
    connectivity_type="private",
    subnet_id=example_aws_subnet["id"])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := ec2.NewNatGateway(ctx, "example", &ec2.NatGatewayArgs{
			ConnectivityType: pulumi.String("private"),
			SubnetId:         pulumi.Any(exampleAwsSubnet.Id),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.Ec2.NatGateway("example", new()
    {
        ConnectivityType = "private",
        SubnetId = exampleAwsSubnet.Id,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.NatGateway;
import com.pulumi.aws.ec2.NatGatewayArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new NatGateway("example", NatGatewayArgs.builder()
            .connectivityType("private")
            .subnetId(exampleAwsSubnet.id())
            .build());

    }
}
resources:
  example:
    type: aws:ec2:NatGateway
    properties:
      connectivityType: private
      subnetId: ${exampleAwsSubnet.id}

Setting connectivityType to “private” creates a NAT gateway without a public IP address. This configuration enables routing between private subnets within your VPC without internet access. The gateway still requires a subnet placement but doesn’t need an Elastic IP allocation.

Add secondary IPs for increased connection capacity

High-throughput workloads can exhaust the port range of a single IP address when making many concurrent connections to the same destination.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.ec2.NatGateway("example", {
    allocationId: exampleAwsEip.id,
    subnetId: exampleAwsSubnet.id,
    secondaryAllocationIds: [secondary.id],
    secondaryPrivateIpAddresses: ["10.0.1.5"],
});
import pulumi
import pulumi_aws as aws

example = aws.ec2.NatGateway("example",
    allocation_id=example_aws_eip["id"],
    subnet_id=example_aws_subnet["id"],
    secondary_allocation_ids=[secondary["id"]],
    secondary_private_ip_addresses=["10.0.1.5"])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := ec2.NewNatGateway(ctx, "example", &ec2.NatGatewayArgs{
			AllocationId: pulumi.Any(exampleAwsEip.Id),
			SubnetId:     pulumi.Any(exampleAwsSubnet.Id),
			SecondaryAllocationIds: pulumi.StringArray{
				secondary.Id,
			},
			SecondaryPrivateIpAddresses: pulumi.StringArray{
				pulumi.String("10.0.1.5"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.Ec2.NatGateway("example", new()
    {
        AllocationId = exampleAwsEip.Id,
        SubnetId = exampleAwsSubnet.Id,
        SecondaryAllocationIds = new[]
        {
            secondary.Id,
        },
        SecondaryPrivateIpAddresses = new[]
        {
            "10.0.1.5",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.NatGateway;
import com.pulumi.aws.ec2.NatGatewayArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new NatGateway("example", NatGatewayArgs.builder()
            .allocationId(exampleAwsEip.id())
            .subnetId(exampleAwsSubnet.id())
            .secondaryAllocationIds(secondary.id())
            .secondaryPrivateIpAddresses("10.0.1.5")
            .build());

    }
}
resources:
  example:
    type: aws:ec2:NatGateway
    properties:
      allocationId: ${exampleAwsEip.id}
      subnetId: ${exampleAwsSubnet.id}
      secondaryAllocationIds:
        - ${secondary.id}
      secondaryPrivateIpAddresses:
        - 10.0.1.5

The secondaryAllocationIds property adds additional Elastic IPs to the NAT gateway, while secondaryPrivateIpAddresses assigns specific private IPs. Each additional IP provides roughly 64,000 additional ports for outbound connections. This configuration extends the basic public NAT setup with extra capacity.

Deploy multi-AZ NAT with automatic expansion

Regional NAT gateways provide high availability by automatically expanding to new availability zones and allocating EIPs as your VPC grows.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const available = aws.getAvailabilityZones({});
const example = new aws.ec2.Vpc("example", {cidrBlock: "10.0.0.0/16"});
const exampleInternetGateway = new aws.ec2.InternetGateway("example", {vpcId: example.id});
const exampleNatGateway = new aws.ec2.NatGateway("example", {
    vpcId: example.id,
    availabilityMode: "regional",
});
import pulumi
import pulumi_aws as aws

available = aws.get_availability_zones()
example = aws.ec2.Vpc("example", cidr_block="10.0.0.0/16")
example_internet_gateway = aws.ec2.InternetGateway("example", vpc_id=example.id)
example_nat_gateway = aws.ec2.NatGateway("example",
    vpc_id=example.id,
    availability_mode="regional")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := aws.GetAvailabilityZones(ctx, &aws.GetAvailabilityZonesArgs{}, nil)
		if err != nil {
			return err
		}
		example, err := ec2.NewVpc(ctx, "example", &ec2.VpcArgs{
			CidrBlock: pulumi.String("10.0.0.0/16"),
		})
		if err != nil {
			return err
		}
		_, err = ec2.NewInternetGateway(ctx, "example", &ec2.InternetGatewayArgs{
			VpcId: example.ID(),
		})
		if err != nil {
			return err
		}
		_, err = ec2.NewNatGateway(ctx, "example", &ec2.NatGatewayArgs{
			VpcId:            example.ID(),
			AvailabilityMode: pulumi.String("regional"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var available = Aws.GetAvailabilityZones.Invoke();

    var example = new Aws.Ec2.Vpc("example", new()
    {
        CidrBlock = "10.0.0.0/16",
    });

    var exampleInternetGateway = new Aws.Ec2.InternetGateway("example", new()
    {
        VpcId = example.Id,
    });

    var exampleNatGateway = new Aws.Ec2.NatGateway("example", new()
    {
        VpcId = example.Id,
        AvailabilityMode = "regional",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.AwsFunctions;
import com.pulumi.aws.inputs.GetAvailabilityZonesArgs;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.InternetGateway;
import com.pulumi.aws.ec2.InternetGatewayArgs;
import com.pulumi.aws.ec2.NatGateway;
import com.pulumi.aws.ec2.NatGatewayArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var available = AwsFunctions.getAvailabilityZones(GetAvailabilityZonesArgs.builder()
            .build());

        var example = new Vpc("example", VpcArgs.builder()
            .cidrBlock("10.0.0.0/16")
            .build());

        var exampleInternetGateway = new InternetGateway("exampleInternetGateway", InternetGatewayArgs.builder()
            .vpcId(example.id())
            .build());

        var exampleNatGateway = new NatGateway("exampleNatGateway", NatGatewayArgs.builder()
            .vpcId(example.id())
            .availabilityMode("regional")
            .build());

    }
}
resources:
  example:
    type: aws:ec2:Vpc
    properties:
      cidrBlock: 10.0.0.0/16
  exampleInternetGateway:
    type: aws:ec2:InternetGateway
    name: example
    properties:
      vpcId: ${example.id}
  exampleNatGateway:
    type: aws:ec2:NatGateway
    name: example
    properties:
      vpcId: ${example.id}
      availabilityMode: regional
variables:
  available:
    fn::invoke:
      function: aws:getAvailabilityZones
      arguments: {}

Setting availabilityMode to “regional” creates a NAT gateway that spans multiple availability zones. When you don’t specify availabilityZoneAddresses, AWS automatically manages AZ coverage and EIP allocation (auto mode). The vpcId property identifies which VPC the regional gateway serves. Regional NAT gateways require an internet gateway for public connectivity.

Control EIP placement across availability zones

Teams that need predictable IP addresses or specific EIP distribution across zones can manually configure which EIPs are used in each availability zone.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const available = aws.getAvailabilityZones({});
const example = new aws.ec2.Vpc("example", {cidrBlock: "10.0.0.0/16"});
const exampleInternetGateway = new aws.ec2.InternetGateway("example", {vpcId: example.id});
const exampleEip: aws.ec2.Eip[] = [];
for (const range = {value: 0}; range.value < 3; range.value++) {
    exampleEip.push(new aws.ec2.Eip(`example-${range.value}`, {domain: "vpc"}));
}
const exampleNatGateway = new aws.ec2.NatGateway("example", {
    vpcId: example.id,
    availabilityMode: "regional",
    availabilityZoneAddresses: [
        {
            allocationIds: [exampleEip[0].id],
            availabilityZone: available.then(available => available.names?.[0]),
        },
        {
            allocationIds: [
                exampleEip[1].id,
                exampleEip[2].id,
            ],
            availabilityZone: available.then(available => available.names?.[1]),
        },
    ],
});
import pulumi
import pulumi_aws as aws

available = aws.get_availability_zones()
example = aws.ec2.Vpc("example", cidr_block="10.0.0.0/16")
example_internet_gateway = aws.ec2.InternetGateway("example", vpc_id=example.id)
example_eip = []
for range in [{"value": i} for i in range(0, 3)]:
    example_eip.append(aws.ec2.Eip(f"example-{range['value']}", domain="vpc"))
example_nat_gateway = aws.ec2.NatGateway("example",
    vpc_id=example.id,
    availability_mode="regional",
    availability_zone_addresses=[
        {
            "allocation_ids": [example_eip[0].id],
            "availability_zone": available.names[0],
        },
        {
            "allocation_ids": [
                example_eip[1].id,
                example_eip[2].id,
            ],
            "availability_zone": available.names[1],
        },
    ])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		available, err := aws.GetAvailabilityZones(ctx, &aws.GetAvailabilityZonesArgs{}, nil)
		if err != nil {
			return err
		}
		example, err := ec2.NewVpc(ctx, "example", &ec2.VpcArgs{
			CidrBlock: pulumi.String("10.0.0.0/16"),
		})
		if err != nil {
			return err
		}
		_, err = ec2.NewInternetGateway(ctx, "example", &ec2.InternetGatewayArgs{
			VpcId: example.ID(),
		})
		if err != nil {
			return err
		}
		var exampleEip []*ec2.Eip
		for index := 0; index < 3; index++ {
			key0 := index
			_ := index
			__res, err := ec2.NewEip(ctx, fmt.Sprintf("example-%v", key0), &ec2.EipArgs{
				Domain: pulumi.String("vpc"),
			})
			if err != nil {
				return err
			}
			exampleEip = append(exampleEip, __res)
		}
		_, err = ec2.NewNatGateway(ctx, "example", &ec2.NatGatewayArgs{
			VpcId:            example.ID(),
			AvailabilityMode: pulumi.String("regional"),
			AvailabilityZoneAddresses: ec2.NatGatewayAvailabilityZoneAddressArray{
				&ec2.NatGatewayAvailabilityZoneAddressArgs{
					AllocationIds: pulumi.StringArray{
						exampleEip[0].ID(),
					},
					AvailabilityZone: pulumi.String(available.Names[0]),
				},
				&ec2.NatGatewayAvailabilityZoneAddressArgs{
					AllocationIds: pulumi.StringArray{
						exampleEip[1].ID(),
						exampleEip[2].ID(),
					},
					AvailabilityZone: pulumi.String(available.Names[1]),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var available = Aws.GetAvailabilityZones.Invoke();

    var example = new Aws.Ec2.Vpc("example", new()
    {
        CidrBlock = "10.0.0.0/16",
    });

    var exampleInternetGateway = new Aws.Ec2.InternetGateway("example", new()
    {
        VpcId = example.Id,
    });

    var exampleEip = new List<Aws.Ec2.Eip>();
    for (var rangeIndex = 0; rangeIndex < 3; rangeIndex++)
    {
        var range = new { Value = rangeIndex };
        exampleEip.Add(new Aws.Ec2.Eip($"example-{range.Value}", new()
        {
            Domain = "vpc",
        }));
    }
    var exampleNatGateway = new Aws.Ec2.NatGateway("example", new()
    {
        VpcId = example.Id,
        AvailabilityMode = "regional",
        AvailabilityZoneAddresses = new[]
        {
            new Aws.Ec2.Inputs.NatGatewayAvailabilityZoneAddressArgs
            {
                AllocationIds = new[]
                {
                    exampleEip[0].Id,
                },
                AvailabilityZone = available.Apply(getAvailabilityZonesResult => getAvailabilityZonesResult.Names[0]),
            },
            new Aws.Ec2.Inputs.NatGatewayAvailabilityZoneAddressArgs
            {
                AllocationIds = new[]
                {
                    exampleEip[1].Id,
                    exampleEip[2].Id,
                },
                AvailabilityZone = available.Apply(getAvailabilityZonesResult => getAvailabilityZonesResult.Names[1]),
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.AwsFunctions;
import com.pulumi.aws.inputs.GetAvailabilityZonesArgs;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.InternetGateway;
import com.pulumi.aws.ec2.InternetGatewayArgs;
import com.pulumi.aws.ec2.Eip;
import com.pulumi.aws.ec2.EipArgs;
import com.pulumi.aws.ec2.NatGateway;
import com.pulumi.aws.ec2.NatGatewayArgs;
import com.pulumi.aws.ec2.inputs.NatGatewayAvailabilityZoneAddressArgs;
import com.pulumi.codegen.internal.KeyedValue;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var available = AwsFunctions.getAvailabilityZones(GetAvailabilityZonesArgs.builder()
            .build());

        var example = new Vpc("example", VpcArgs.builder()
            .cidrBlock("10.0.0.0/16")
            .build());

        var exampleInternetGateway = new InternetGateway("exampleInternetGateway", InternetGatewayArgs.builder()
            .vpcId(example.id())
            .build());

        for (var i = 0; i < 3; i++) {
            new Eip("exampleEip-" + i, EipArgs.builder()
                .domain("vpc")
                .build());

        
}
        var exampleNatGateway = new NatGateway("exampleNatGateway", NatGatewayArgs.builder()
            .vpcId(example.id())
            .availabilityMode("regional")
            .availabilityZoneAddresses(            
                NatGatewayAvailabilityZoneAddressArgs.builder()
                    .allocationIds(exampleEip[0].id())
                    .availabilityZone(available.names()[0])
                    .build(),
                NatGatewayAvailabilityZoneAddressArgs.builder()
                    .allocationIds(                    
                        exampleEip[1].id(),
                        exampleEip[2].id())
                    .availabilityZone(available.names()[1])
                    .build())
            .build());

    }
}
resources:
  example:
    type: aws:ec2:Vpc
    properties:
      cidrBlock: 10.0.0.0/16
  exampleInternetGateway:
    type: aws:ec2:InternetGateway
    name: example
    properties:
      vpcId: ${example.id}
  exampleEip:
    type: aws:ec2:Eip
    name: example
    properties:
      domain: vpc
    options: {}
  exampleNatGateway:
    type: aws:ec2:NatGateway
    name: example
    properties:
      vpcId: ${example.id}
      availabilityMode: regional
      availabilityZoneAddresses:
        - allocationIds:
            - ${exampleEip[0].id}
          availabilityZone: ${available.names[0]}
        - allocationIds:
            - ${exampleEip[1].id}
            - ${exampleEip[2].id}
          availabilityZone: ${available.names[1]}
variables:
  available:
    fn::invoke:
      function: aws:getAvailabilityZones
      arguments: {}

The availabilityZoneAddresses block lets you specify exact EIP allocations per availability zone. Each block defines an availabilityZone and its allocationIds array. This disables auto-expansion; you control which zones the NAT gateway covers. You can assign multiple EIPs to a single zone for additional connection capacity.

Beyond these examples

These snippets focus on specific NAT gateway features: public and private connectivity modes, zonal and regional availability configurations, and secondary IP allocation for connection scaling. They’re intentionally minimal rather than full VPC networking solutions.

The examples may reference pre-existing infrastructure such as VPC with public and private subnets, Elastic IP addresses for public NAT gateways, and Internet Gateway for public connectivity. They focus on configuring the NAT gateway rather than provisioning everything around it.

To keep things focused, common NAT patterns are omitted, including:

  • Route table configuration to direct traffic through NAT
  • Security group rules for instances using NAT
  • CloudWatch metrics and alarms for NAT monitoring
  • Cost optimization (single NAT vs per-AZ NAT trade-offs)

These omissions are intentional: the goal is to illustrate how each NAT gateway feature is wired, not provide drop-in networking modules. See the NAT Gateway resource reference for all available configuration options.

Let's create AWS VPC NAT Gateways

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Common Errors & Pitfalls
Why am I seeing perpetual differences when using secondaryAllocationIds?
Using secondaryAllocationIds with aws.ec2.NatGatewayEipAssociation causes conflicts and may overwrite associations. Choose one approach: manage secondary EIPs via secondaryAllocationIds OR use aws.ec2.NatGatewayEipAssociation, but not both.
What properties can't I change after creating a NAT Gateway?
These properties are immutable and require resource replacement if changed: availabilityMode, connectivityType, allocationId, subnetId, vpcId, and privateIp.
Why does my public NAT Gateway require an Internet Gateway?
Public NAT Gateways need an Internet Gateway to route outbound traffic. Use dependsOn to ensure the Internet Gateway is created first.
NAT Gateway Types & Modes
What's the difference between zonal and regional NAT Gateways?
Zonal NAT Gateways operate in a single availability zone (default), while regional NAT Gateways span multiple AZs for higher availability. Regional NAT requires vpcId and must use connectivityType: "public".
What's the difference between auto and manual mode for regional NAT Gateways?
Auto mode (default): AWS automatically expands to new AZs and allocates EIPs when it detects network interfaces. Manual mode: You specify availabilityZoneAddresses to control which AZs and EIPs to use, disabling auto-expansion.
Can I create a private regional NAT Gateway?
No, regional NAT Gateways must use connectivityType: "public". Private NAT is only supported in zonal mode.
Configuration Requirements
What are the configuration requirements for different NAT Gateway types?
Public zonal (default): Requires allocationId and subnetId. Private zonal: Requires connectivityType: "private" and subnetId. Regional: Requires vpcId and availabilityMode: "regional". Must NOT set allocationId or subnetId; use availabilityZoneAddresses for manual mode.
How do I create a public NAT Gateway?
For zonal (default): Set allocationId (Elastic IP) and subnetId. For regional: Set vpcId and availabilityMode: "regional" (connectivity defaults to public).
How do I create a private NAT Gateway?
Set connectivityType: "private" and subnetId. Private NAT is only available in zonal mode.
Secondary IP Addresses
How do I add secondary IP addresses to my NAT Gateway?

You have three options:

  1. Secondary EIPs - Use secondaryAllocationIds with a list of EIP allocation IDs
  2. Specific private IPs - Use secondaryPrivateIpAddresses with a list of IPv4 addresses
  3. Auto-assigned private IPs - Use secondaryPrivateIpAddressCount to specify how many
How do I remove all secondary IP addresses?
Specify an empty list for secondaryAllocationIds or secondaryPrivateIpAddresses to remove all secondary allocations.

Using a different cloud?

Explore networking guides for other cloud providers: