The aws:alb/targetGroup:TargetGroup resource, part of the Pulumi AWS provider, defines a target group that routes load balancer traffic to registered targets: EC2 instances, IP addresses, Lambda functions, or other load balancers. This guide focuses on three capabilities: target type selection, protocol configuration, and connection termination behavior.
Target groups require a VPC (except for Lambda targets) and connect to load balancers through listener rules. The examples are intentionally small. Combine them with your own load balancer, health checks, and target registration.
Route traffic to EC2 instances by ID
Most deployments route HTTP traffic to EC2 instances, where the target group tracks instances by ID and forwards to a specific port.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const main = new aws.ec2.Vpc("main", {cidrBlock: "10.0.0.0/16"});
const test = new aws.lb.TargetGroup("test", {
name: "tf-example-lb-tg",
port: 80,
protocol: "HTTP",
vpcId: main.id,
});
import pulumi
import pulumi_aws as aws
main = aws.ec2.Vpc("main", cidr_block="10.0.0.0/16")
test = aws.lb.TargetGroup("test",
name="tf-example-lb-tg",
port=80,
protocol="HTTP",
vpc_id=main.id)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"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 {
main, err := ec2.NewVpc(ctx, "main", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.0.0.0/16"),
})
if err != nil {
return err
}
_, err = lb.NewTargetGroup(ctx, "test", &lb.TargetGroupArgs{
Name: pulumi.String("tf-example-lb-tg"),
Port: pulumi.Int(80),
Protocol: pulumi.String("HTTP"),
VpcId: main.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 main = new Aws.Ec2.Vpc("main", new()
{
CidrBlock = "10.0.0.0/16",
});
var test = new Aws.LB.TargetGroup("test", new()
{
Name = "tf-example-lb-tg",
Port = 80,
Protocol = "HTTP",
VpcId = main.Id,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.lb.TargetGroup;
import com.pulumi.aws.lb.TargetGroupArgs;
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 main = new Vpc("main", VpcArgs.builder()
.cidrBlock("10.0.0.0/16")
.build());
var test = new TargetGroup("test", TargetGroupArgs.builder()
.name("tf-example-lb-tg")
.port(80)
.protocol("HTTP")
.vpcId(main.id())
.build());
}
}
resources:
test:
type: aws:lb:TargetGroup
properties:
name: tf-example-lb-tg
port: 80
protocol: HTTP
vpcId: ${main.id}
main:
type: aws:ec2:Vpc
properties:
cidrBlock: 10.0.0.0/16
The targetType defaults to “instance”, meaning you register EC2 instance IDs as targets. The port and protocol define where traffic is forwarded. The vpcId places the target group in your VPC, enabling instance discovery.
Route traffic to specific IP addresses
Container workloads and on-premises services often need IP-based routing rather than instance IDs.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const main = new aws.ec2.Vpc("main", {cidrBlock: "10.0.0.0/16"});
const ip_example = new aws.lb.TargetGroup("ip-example", {
name: "tf-example-lb-tg",
port: 80,
protocol: "HTTP",
targetType: "ip",
vpcId: main.id,
});
import pulumi
import pulumi_aws as aws
main = aws.ec2.Vpc("main", cidr_block="10.0.0.0/16")
ip_example = aws.lb.TargetGroup("ip-example",
name="tf-example-lb-tg",
port=80,
protocol="HTTP",
target_type="ip",
vpc_id=main.id)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"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 {
main, err := ec2.NewVpc(ctx, "main", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.0.0.0/16"),
})
if err != nil {
return err
}
_, err = lb.NewTargetGroup(ctx, "ip-example", &lb.TargetGroupArgs{
Name: pulumi.String("tf-example-lb-tg"),
Port: pulumi.Int(80),
Protocol: pulumi.String("HTTP"),
TargetType: pulumi.String("ip"),
VpcId: main.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 main = new Aws.Ec2.Vpc("main", new()
{
CidrBlock = "10.0.0.0/16",
});
var ip_example = new Aws.LB.TargetGroup("ip-example", new()
{
Name = "tf-example-lb-tg",
Port = 80,
Protocol = "HTTP",
TargetType = "ip",
VpcId = main.Id,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.lb.TargetGroup;
import com.pulumi.aws.lb.TargetGroupArgs;
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 main = new Vpc("main", VpcArgs.builder()
.cidrBlock("10.0.0.0/16")
.build());
var ip_example = new TargetGroup("ip-example", TargetGroupArgs.builder()
.name("tf-example-lb-tg")
.port(80)
.protocol("HTTP")
.targetType("ip")
.vpcId(main.id())
.build());
}
}
resources:
ip-example:
type: aws:lb:TargetGroup
properties:
name: tf-example-lb-tg
port: 80
protocol: HTTP
targetType: ip
vpcId: ${main.id}
main:
type: aws:ec2:Vpc
properties:
cidrBlock: 10.0.0.0/16
Setting targetType to “ip” changes how you register targets: you provide IP addresses instead of instance IDs. IPs must come from VPC subnets or RFC 1918/6598 private ranges. This mode works well for ECS tasks, Kubernetes pods, or hybrid architectures.
Invoke Lambda functions from load balancer requests
Serverless applications can receive HTTP requests through a load balancer by routing to Lambda functions.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const lambda_example = new aws.lb.TargetGroup("lambda-example", {
name: "tf-example-lb-tg",
targetType: "lambda",
});
import pulumi
import pulumi_aws as aws
lambda_example = aws.lb.TargetGroup("lambda-example",
name="tf-example-lb-tg",
target_type="lambda")
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.NewTargetGroup(ctx, "lambda-example", &lb.TargetGroupArgs{
Name: pulumi.String("tf-example-lb-tg"),
TargetType: pulumi.String("lambda"),
})
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 lambda_example = new Aws.LB.TargetGroup("lambda-example", new()
{
Name = "tf-example-lb-tg",
TargetType = "lambda",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lb.TargetGroup;
import com.pulumi.aws.lb.TargetGroupArgs;
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 lambda_example = new TargetGroup("lambda-example", TargetGroupArgs.builder()
.name("tf-example-lb-tg")
.targetType("lambda")
.build());
}
}
resources:
lambda-example:
type: aws:lb:TargetGroup
properties:
name: tf-example-lb-tg
targetType: lambda
When targetType is “lambda”, the target group invokes a Lambda function for each request. You don’t specify port or protocol because Lambda handles HTTP internally. The load balancer passes request details to the function and returns its response to the client.
Preserve connections to unhealthy targets
Some Network Load Balancer workloads maintain connections even when targets fail health checks, allowing graceful degradation.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const tcp_example = new aws.lb.TargetGroup("tcp-example", {
name: "tf-example-lb-nlb-tg",
port: 25,
protocol: "TCP",
vpcId: main.id,
targetHealthStates: [{
enableUnhealthyConnectionTermination: false,
}],
});
import pulumi
import pulumi_aws as aws
tcp_example = aws.lb.TargetGroup("tcp-example",
name="tf-example-lb-nlb-tg",
port=25,
protocol="TCP",
vpc_id=main["id"],
target_health_states=[{
"enable_unhealthy_connection_termination": False,
}])
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.NewTargetGroup(ctx, "tcp-example", &lb.TargetGroupArgs{
Name: pulumi.String("tf-example-lb-nlb-tg"),
Port: pulumi.Int(25),
Protocol: pulumi.String("TCP"),
VpcId: pulumi.Any(main.Id),
TargetHealthStates: lb.TargetGroupTargetHealthStateArray{
&lb.TargetGroupTargetHealthStateArgs{
EnableUnhealthyConnectionTermination: pulumi.Bool(false),
},
},
})
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 tcp_example = new Aws.LB.TargetGroup("tcp-example", new()
{
Name = "tf-example-lb-nlb-tg",
Port = 25,
Protocol = "TCP",
VpcId = main.Id,
TargetHealthStates = new[]
{
new Aws.LB.Inputs.TargetGroupTargetHealthStateArgs
{
EnableUnhealthyConnectionTermination = false,
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lb.TargetGroup;
import com.pulumi.aws.lb.TargetGroupArgs;
import com.pulumi.aws.lb.inputs.TargetGroupTargetHealthStateArgs;
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 tcp_example = new TargetGroup("tcp-example", TargetGroupArgs.builder()
.name("tf-example-lb-nlb-tg")
.port(25)
.protocol("TCP")
.vpcId(main.id())
.targetHealthStates(TargetGroupTargetHealthStateArgs.builder()
.enableUnhealthyConnectionTermination(false)
.build())
.build());
}
}
resources:
tcp-example:
type: aws:lb:TargetGroup
properties:
name: tf-example-lb-nlb-tg
port: 25
protocol: TCP
vpcId: ${main.id}
targetHealthStates:
- enableUnhealthyConnectionTermination: false
The targetHealthStates block controls connection termination behavior. Setting enableUnhealthyConnectionTermination to false keeps connections open when targets become unhealthy. This only applies to Network Load Balancers using TCP or TLS protocols.
Beyond these examples
These snippets focus on specific target group features: target type selection, protocol and port configuration, and connection termination behavior. They’re intentionally minimal rather than full load balancing solutions.
The examples reference pre-existing infrastructure such as VPCs for instance, IP, and ALB target groups, and load balancers to attach the target group to. They focus on target group configuration rather than provisioning the surrounding infrastructure.
To keep things focused, common target group patterns are omitted, including:
- Health check configuration (healthCheck block)
- Session stickiness (stickiness block)
- Deregistration delay and connection draining
- Load balancing algorithms (roundRobin, leastOutstandingRequests, weightedRandom)
- Cross-zone load balancing settings
These omissions are intentional: the goal is to illustrate how each target group feature is wired, not provide drop-in load balancing modules. See the Target Group resource reference for all available configuration options.
Let's configure AWS Load Balancer Target Groups
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Target Types & Configuration
instance (EC2 instances), ip (IP addresses), lambda (Lambda functions), or alb (Application Load Balancers). The default is instance.lambda target type. Application Load Balancers don’t support the alb target type.instance, ip, or alb targets, you must specify port, protocol, and vpcId. For lambda targets, these properties don’t apply and shouldn’t be specified.instance or ip as your target type.Immutability & Lifecycle
name, namePrefix, targetType, port, protocol, protocolVersion, vpcId, ipAddressType, and targetControlPort.Load Balancing Algorithms & Features
round_robin (default), least_outstanding_requests, and weighted_random. These only apply to ALB target groups.weighted_random load balancing algorithm. Set loadBalancingAlgorithmType to weighted_random before enabling it.use_load_balancer_configuration, which inherits the setting from the load balancer. You can override it with true or false.Deregistration & Health Checks
slowStart to a value between 30 and 900 seconds for the warm-up period. The default is 0, which disables slow start.targetHealthStates with enableUnhealthyConnectionTermination set to false. This only applies to Network Load Balancers with TCP or TLS protocols.Naming & Constraints
name to specify the full name, or namePrefix (max 6 characters) to generate a unique name with that prefix. These properties conflict with each other.Using a different cloud?
Explore networking guides for other cloud providers: