Create AWS Application Load Balancers

The aws:alb/loadBalancer:LoadBalancer resource, part of the Pulumi AWS provider, provisions an Elastic Load Balancer: its type (Application, Network, or Gateway), network placement, and traffic distribution settings. This guide focuses on three capabilities: Application and Network Load Balancer deployment, subnet placement with standard or custom IP addressing, and access logging and deletion protection.

Load balancers require VPC subnets spanning multiple availability zones, 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 VPC infrastructure, target groups, and listeners.

Deploy an internet-facing Application Load Balancer

Most web applications distribute HTTP/HTTPS traffic across targets in different availability zones 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 routing behavior: application load balancers operate at the HTTP layer and support path-based routing and host-based routing. The internal property controls whether the load balancer receives a public DNS name (false) or only private IPs (true). The accessLogs block sends request logs to S3 for analysis. The enableDeletionProtection property prevents accidental deletion through the AWS API.

Deploy a Network Load Balancer for TCP traffic

Applications requiring ultra-low latency or handling millions of requests per second route TCP/UDP traffic at the connection level using Network Load Balancers.

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 operate at the transport layer, preserving source IP addresses and providing static IP addresses per availability zone. Unlike Application Load Balancers, they don’t require security groups by default (though they can be added). The subnets property places the load balancer in specific availability zones; for network type, subnets can only be added, not removed.

Assign static Elastic IPs to Network Load Balancer nodes

Some applications require stable IP addresses for firewall allowlisting or DNS configuration. Network Load Balancers can use Elastic IPs for predictable endpoints.

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 addresses. Each mapping pairs a subnet with an Elastic IP allocation. The allocationId references a pre-allocated Elastic IP; the load balancer assigns this IP to its node in that subnet. This provides stable addresses that persist across load balancer updates.

Configure private IP addresses for internal load balancing

Internal applications that need predictable private IP addresses for service discovery or network routing can specify exact IPs within their subnet ranges.

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 IP must fall within the subnet’s CIDR range and be available. This gives you control over the exact private IPs used for internal service endpoints, useful for DNS records or network routing rules that reference specific addresses.

Beyond these examples

These snippets focus on specific load balancer features: Application and Network Load Balancer types, subnet placement and IP address control, and access logging and deletion protection. They’re intentionally minimal rather than full traffic distribution 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 configurations. They focus on configuring the load balancer rather than provisioning the surrounding network.

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

  • Cross-zone load balancing (enableCrossZoneLoadBalancing)
  • Connection and health check logging (connectionLogs, healthCheckLogs)
  • HTTP/2 and header processing (enableHttp2, dropInvalidHeaderFields, xffHeaderProcessingMode)
  • Idle timeout and client keep-alive tuning (idleTimeout, clientKeepAlive)
  • WAF integration and TLS header forwarding (enableWafFailOpen, enableTlsVersionAndCipherSuiteHeaders)
  • Zonal shift and DNS routing policies (enableZonalShift, dnsRecordClientRoutingPolicy)

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

Let's create AWS Application 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 available load balancer types and which is the default?
You can create three types: application (default), network, or gateway. The type is immutable after creation.
What's the difference between subnets and subnetMappings?
One of either subnets or subnetMappings is required. Use subnetMappings when you need to specify Elastic IPs (allocationId) or private IP addresses (privateIpv4Address) for your load balancer.
Is aws.alb.LoadBalancer different from aws.lb.LoadBalancer?
No, they’re identical. aws.alb.LoadBalancer is an alias for aws.lb.LoadBalancer with the same functionality.
Networking & IP Addressing
Can I use dualstack IP addressing with an 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 assign Elastic IPs to my network load balancer?
Use subnetMappings with subnetId and allocationId for each subnet, as shown in the Elastic IPs example.
How do I specify private IP addresses for an internal load balancer?
Use subnetMappings with subnetId and privateIpv4Address for each subnet.
Can I remove subnets from a network load balancer?
No, for network type load balancers, subnets can only be added. Deleting a subnet forces recreation of the resource.
Security Groups
Why can't I add security groups to my network load balancer?
For network type load balancers, security groups cannot be added if none are currently present, and cannot all be removed once added. Either condition forces recreation of the resource.
Which load balancer types support security groups?
Security groups are valid for application or network type load balancers, but not for gateway type.
Immutability & Lifecycle
What properties are immutable after creation?
The following properties cannot be changed after creation: internal, name, namePrefix, loadBalancerType, and customerOwnedIpv4Pool.
What happens if I decrease secondaryIpsAutoAssignedPerSubnet?
Decreasing secondaryIpsAutoAssignedPerSubnet forces recreation of the resource. Only increase this value if needed.
Advanced Features & Defaults
Is cross-zone load balancing enabled by default?
For network and gateway types, it’s disabled by default (false). For application type, it’s always enabled (true) and cannot be disabled.
What's the default idle timeout for application load balancers?
The default idleTimeout is 60 seconds. This setting only applies to application type load balancers.

Using a different cloud?

Explore networking guides for other cloud providers: