Create AWS Load Balancers

The aws:lb/loadBalancer:LoadBalancer resource, part of the Pulumi AWS provider, provisions the load balancer itself: its type (Application, Network, or Gateway), network placement, and operational settings. This guide focuses on three capabilities: creating Application and Network Load Balancers, subnet placement strategies, and static IP assignment.

Load balancers require VPC subnets, security groups (for Application Load Balancers), and optionally S3 buckets for logging or Elastic IPs for static addressing. The examples are intentionally small. Combine them with your own target groups, listeners, and routing rules.

Create an internet-facing Application Load Balancer

Most web applications distribute HTTP/HTTPS traffic across multiple targets using an Application Load Balancer.

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

const test = new aws.lb.LoadBalancer("test", {
    name: "test-lb-tf",
    internal: false,
    loadBalancerType: "application",
    securityGroups: [lbSg.id],
    subnets: .map(subnet => (subnet.id)),
    enableDeletionProtection: true,
    accessLogs: {
        bucket: lbLogs.id,
        prefix: "test-lb",
        enabled: true,
    },
    tags: {
        Environment: "production",
    },
});
import pulumi
import pulumi_aws as aws

test = aws.lb.LoadBalancer("test",
    name="test-lb-tf",
    internal=False,
    load_balancer_type="application",
    security_groups=[lb_sg["id"]],
    subnets=[subnet["id"] for subnet in public],
    enable_deletion_protection=True,
    access_logs={
        "bucket": lb_logs["id"],
        "prefix": "test-lb",
        "enabled": True,
    },
    tags={
        "Environment": "production",
    })
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var test = new Aws.LB.LoadBalancer("test", new()
    {
        Name = "test-lb-tf",
        Internal = false,
        LoadBalancerType = "application",
        SecurityGroups = new[]
        {
            lbSg.Id,
        },
        Subnets = .Select(subnet => 
        {
            return subnet.Id;
        }).ToList(),
        EnableDeletionProtection = true,
        AccessLogs = new Aws.LB.Inputs.LoadBalancerAccessLogsArgs
        {
            Bucket = lbLogs.Id,
            Prefix = "test-lb",
            Enabled = true,
        },
        Tags = 
        {
            { "Environment", "production" },
        },
    });

});

The loadBalancerType property determines the load balancer’s behavior: application for HTTP/HTTPS routing, network for TCP/UDP connection-level balancing. Setting internal to false creates an internet-facing load balancer with public IP addresses. The subnets property places the load balancer across availability zones, while securityGroups controls inbound traffic. The accessLogs block enables request logging to S3, and enableDeletionProtection prevents accidental deletion.

Create a Network Load Balancer for TCP traffic

Applications requiring ultra-low latency or handling millions of requests per second use Network Load Balancers, which operate at the connection level.

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

const test = new aws.lb.LoadBalancer("test", {
    name: "test-lb-tf",
    internal: false,
    loadBalancerType: "network",
    subnets: .map(subnet => (subnet.id)),
    enableDeletionProtection: true,
    tags: {
        Environment: "production",
    },
});
import pulumi
import pulumi_aws as aws

test = aws.lb.LoadBalancer("test",
    name="test-lb-tf",
    internal=False,
    load_balancer_type="network",
    subnets=[subnet["id"] for subnet in public],
    enable_deletion_protection=True,
    tags={
        "Environment": "production",
    })
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var test = new Aws.LB.LoadBalancer("test", new()
    {
        Name = "test-lb-tf",
        Internal = false,
        LoadBalancerType = "network",
        Subnets = .Select(subnet => 
        {
            return subnet.Id;
        }).ToList(),
        EnableDeletionProtection = true,
        Tags = 
        {
            { "Environment", "production" },
        },
    });

});

Network Load Balancers don’t require security groups by default; they operate at Layer 4 and preserve source IP addresses. The subnets property determines availability zone placement. Unlike Application Load Balancers, Network Load Balancers can only add subnets after creation, not remove them.

Assign static Elastic IPs to load balancer nodes

Some integrations require stable IP addresses that don’t change when the load balancer scales or fails over.

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

const example = new aws.lb.LoadBalancer("example", {
    name: "example",
    loadBalancerType: "network",
    subnetMappings: [
        {
            subnetId: example1AwsSubnet.id,
            allocationId: example1.id,
        },
        {
            subnetId: example2AwsSubnet.id,
            allocationId: example2.id,
        },
    ],
});
import pulumi
import pulumi_aws as aws

example = aws.lb.LoadBalancer("example",
    name="example",
    load_balancer_type="network",
    subnet_mappings=[
        {
            "subnet_id": example1_aws_subnet["id"],
            "allocation_id": example1["id"],
        },
        {
            "subnet_id": example2_aws_subnet["id"],
            "allocation_id": example2["id"],
        },
    ])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := lb.NewLoadBalancer(ctx, "example", &lb.LoadBalancerArgs{
			Name:             pulumi.String("example"),
			LoadBalancerType: pulumi.String("network"),
			SubnetMappings: lb.LoadBalancerSubnetMappingArray{
				&lb.LoadBalancerSubnetMappingArgs{
					SubnetId:     pulumi.Any(example1AwsSubnet.Id),
					AllocationId: pulumi.Any(example1.Id),
				},
				&lb.LoadBalancerSubnetMappingArgs{
					SubnetId:     pulumi.Any(example2AwsSubnet.Id),
					AllocationId: pulumi.Any(example2.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.LB.LoadBalancer("example", new()
    {
        Name = "example",
        LoadBalancerType = "network",
        SubnetMappings = new[]
        {
            new Aws.LB.Inputs.LoadBalancerSubnetMappingArgs
            {
                SubnetId = example1AwsSubnet.Id,
                AllocationId = example1.Id,
            },
            new Aws.LB.Inputs.LoadBalancerSubnetMappingArgs
            {
                SubnetId = example2AwsSubnet.Id,
                AllocationId = example2.Id,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lb.LoadBalancer;
import com.pulumi.aws.lb.LoadBalancerArgs;
import com.pulumi.aws.lb.inputs.LoadBalancerSubnetMappingArgs;
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 LoadBalancer("example", LoadBalancerArgs.builder()
            .name("example")
            .loadBalancerType("network")
            .subnetMappings(            
                LoadBalancerSubnetMappingArgs.builder()
                    .subnetId(example1AwsSubnet.id())
                    .allocationId(example1.id())
                    .build(),
                LoadBalancerSubnetMappingArgs.builder()
                    .subnetId(example2AwsSubnet.id())
                    .allocationId(example2.id())
                    .build())
            .build());

    }
}
resources:
  example:
    type: aws:lb:LoadBalancer
    properties:
      name: example
      loadBalancerType: network
      subnetMappings:
        - subnetId: ${example1AwsSubnet.id}
          allocationId: ${example1.id}
        - subnetId: ${example2AwsSubnet.id}
          allocationId: ${example2.id}

The subnetMappings property replaces the subnets property when you need control over IP addressing. Each mapping pairs a subnet with an Elastic IP allocation, giving your load balancer predictable addresses for firewall rules or DNS records. This configuration only works with Network Load Balancers.

Assign specific private IPs for internal load balancers

Internal applications sometimes need predictable private IP addresses for integration with on-premises systems.

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

const example = new aws.lb.LoadBalancer("example", {
    name: "example",
    loadBalancerType: "network",
    subnetMappings: [
        {
            subnetId: example1.id,
            privateIpv4Address: "10.0.1.15",
        },
        {
            subnetId: example2.id,
            privateIpv4Address: "10.0.2.15",
        },
    ],
});
import pulumi
import pulumi_aws as aws

example = aws.lb.LoadBalancer("example",
    name="example",
    load_balancer_type="network",
    subnet_mappings=[
        {
            "subnet_id": example1["id"],
            "private_ipv4_address": "10.0.1.15",
        },
        {
            "subnet_id": example2["id"],
            "private_ipv4_address": "10.0.2.15",
        },
    ])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := lb.NewLoadBalancer(ctx, "example", &lb.LoadBalancerArgs{
			Name:             pulumi.String("example"),
			LoadBalancerType: pulumi.String("network"),
			SubnetMappings: lb.LoadBalancerSubnetMappingArray{
				&lb.LoadBalancerSubnetMappingArgs{
					SubnetId:           pulumi.Any(example1.Id),
					PrivateIpv4Address: pulumi.String("10.0.1.15"),
				},
				&lb.LoadBalancerSubnetMappingArgs{
					SubnetId:           pulumi.Any(example2.Id),
					PrivateIpv4Address: pulumi.String("10.0.2.15"),
				},
			},
		})
		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.LB.LoadBalancer("example", new()
    {
        Name = "example",
        LoadBalancerType = "network",
        SubnetMappings = new[]
        {
            new Aws.LB.Inputs.LoadBalancerSubnetMappingArgs
            {
                SubnetId = example1.Id,
                PrivateIpv4Address = "10.0.1.15",
            },
            new Aws.LB.Inputs.LoadBalancerSubnetMappingArgs
            {
                SubnetId = example2.Id,
                PrivateIpv4Address = "10.0.2.15",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lb.LoadBalancer;
import com.pulumi.aws.lb.LoadBalancerArgs;
import com.pulumi.aws.lb.inputs.LoadBalancerSubnetMappingArgs;
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 LoadBalancer("example", LoadBalancerArgs.builder()
            .name("example")
            .loadBalancerType("network")
            .subnetMappings(            
                LoadBalancerSubnetMappingArgs.builder()
                    .subnetId(example1.id())
                    .privateIpv4Address("10.0.1.15")
                    .build(),
                LoadBalancerSubnetMappingArgs.builder()
                    .subnetId(example2.id())
                    .privateIpv4Address("10.0.2.15")
                    .build())
            .build());

    }
}
resources:
  example:
    type: aws:lb:LoadBalancer
    properties:
      name: example
      loadBalancerType: network
      subnetMappings:
        - subnetId: ${example1.id}
          privateIpv4Address: 10.0.1.15
        - subnetId: ${example2.id}
          privateIpv4Address: 10.0.2.15

For internal load balancers, subnetMappings can specify privateIpv4Address instead of allocationId. The load balancer claims these addresses from the subnet’s CIDR range. The addresses must be available and not already assigned to other resources.

Beyond these examples

These snippets focus on specific load balancer features: Application and Network Load Balancer types, subnet placement and IP addressing, and access logging and deletion protection. They’re intentionally minimal rather than full traffic routing solutions.

The examples reference pre-existing infrastructure such as VPC subnets across multiple availability zones, security groups (for Application Load Balancers), S3 buckets for access logs, and Elastic IP allocations (for static IP scenarios). They focus on provisioning the load balancer rather than configuring target groups or listeners.

To keep things focused, common load balancer patterns are omitted, including:

  • Cross-zone load balancing configuration (enableCrossZoneLoadBalancing)
  • Connection and health check logging (connectionLogs, healthCheckLogs)
  • HTTP/2 and TLS header settings (enableHttp2, enableTlsVersionAndCipherSuiteHeaders)
  • Idle timeout and client keep-alive tuning (idleTimeout, clientKeepAlive)
  • X-Forwarded-For header processing (xffHeaderProcessingMode, enableXffClientPort)
  • Target group and listener configuration (separate resources)

These omissions are intentional: the goal is to illustrate how each load balancer feature is wired, not provide drop-in routing modules. See the Load Balancer resource reference for all available configuration options.

Let's create AWS Load Balancers

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Load Balancer Types & Configuration
What are the differences between application, network, and gateway load balancers?
The loadBalancerType can be application (default), network, or gateway. Application load balancers support HTTP/2, header manipulation, and connection logs. Network load balancers support Elastic IPs, secondary IPs, and DNS routing policies. Gateway load balancers are for third-party virtual appliances. This property is immutable after creation.
What's the difference between subnets and subnetMappings?
One of subnets or subnetMappings is required. Use subnets for simple subnet attachment. Use subnetMappings when you need to specify Elastic IPs (allocationId) or private IPv4 addresses (privateIpv4Address) for each subnet.
Is cross-zone load balancing enabled by default?
It depends on the load balancer type. For network and gateway types, enableCrossZoneLoadBalancing defaults to false. For application load balancers, it’s always true and cannot be disabled.
Network Load Balancer Limitations
What can't I change on a network load balancer without forcing recreation?
Network load balancers have several immutability constraints: security groups cannot be added if none exist, and cannot all be removed once added; subnets can only be added (deletion forces recreation); subnet mappings can only be added; and decreasing secondaryIpsAutoAssignedPerSubnet forces recreation.
Can I add security groups to my network load balancer after creation?
Only if security groups were configured at creation. For network load balancers, security groups cannot be added if none are currently present. Additionally, you cannot remove all security groups once added, as either condition forces resource recreation.
Can I remove subnets from my network load balancer?
No, deleting a subnet from a network load balancer forces recreation of the resource. You can only add subnets to network load balancers.
Networking & IP Configuration
Why can't I use dualstack IP addressing on my internal load balancer?
Internal load balancers can only use ipv4 as the ipAddressType. You can only change to dualstack if the selected subnets are IPv6 enabled.
How do I specify Elastic IPs for my network load balancer?
Use subnetMappings with allocationId to assign Elastic IPs to each subnet, rather than using the subnets property.
How do I assign private IP addresses to an internal network load balancer?
Use subnetMappings with privateIpv4Address to specify the private IP for each subnet.
Application Load Balancer Features
What's the default idle timeout for application load balancers?
The idleTimeout defaults to 60 seconds for application load balancers. This property is only valid for application type load balancers.
How does the load balancer handle the X-Forwarded-For header?
The xffHeaderProcessingMode determines how the X-Forwarded-For header is modified. Options are append (default), preserve, or remove. This is only valid for application load balancers.
What are the desync mitigation modes?
The desyncMitigationMode controls how the load balancer handles HTTP desync security risks. Valid values are monitor, defensive (default), and strictest.
Naming & Resource Management
What are the naming constraints for load balancers?
The name must be unique within your AWS account, have a maximum of 32 characters, contain only alphanumeric characters or hyphens, and must not begin or end with a hyphen. If not specified, Pulumi autogenerates a name beginning with tf-lb.
How do I prevent accidental deletion of my load balancer?
Set enableDeletionProtection to true. This prevents deletion via the AWS API and will prevent Pulumi from deleting the load balancer. It defaults to false.

Using a different cloud?

Explore networking guides for other cloud providers: