The aws:route53/record:Record resource, part of the Pulumi AWS provider, defines DNS records within Route 53 hosted zones: their names, types, values, and routing behavior. This guide focuses on three capabilities: basic A and CNAME records, weighted routing for traffic distribution, and alias records for AWS services.
DNS records belong to hosted zones and often point to load balancers, accelerators, or other infrastructure. The examples are intentionally small. Combine them with your own hosted zones and target resources.
Point a domain to an IP address
Most DNS configurations start with simple A records that map domain names to IP addresses.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const www = new aws.route53.Record("www", {
zoneId: primary.zoneId,
name: "www.example.com",
type: aws.route53.RecordType.A,
ttl: 300,
records: [lb.publicIp],
});
import pulumi
import pulumi_aws as aws
www = aws.route53.Record("www",
zone_id=primary["zoneId"],
name="www.example.com",
type=aws.route53.RecordType.A,
ttl=300,
records=[lb["publicIp"]])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/route53"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := route53.NewRecord(ctx, "www", &route53.RecordArgs{
ZoneId: pulumi.Any(primary.ZoneId),
Name: pulumi.String("www.example.com"),
Type: pulumi.String(route53.RecordTypeA),
Ttl: pulumi.Int(300),
Records: pulumi.StringArray{
lb.PublicIp,
},
})
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 www = new Aws.Route53.Record("www", new()
{
ZoneId = primary.ZoneId,
Name = "www.example.com",
Type = Aws.Route53.RecordType.A,
Ttl = 300,
Records = new[]
{
lb.PublicIp,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.route53.Record;
import com.pulumi.aws.route53.RecordArgs;
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 www = new Record("www", RecordArgs.builder()
.zoneId(primary.zoneId())
.name("www.example.com")
.type("A")
.ttl(300)
.records(lb.publicIp())
.build());
}
}
resources:
www:
type: aws:route53:Record
properties:
zoneId: ${primary.zoneId}
name: www.example.com
type: A
ttl: 300
records:
- ${lb.publicIp}
The name property sets the domain name, type specifies the record type (A for IPv4 addresses), and records contains the IP addresses to return. The ttl property controls how long resolvers cache the response (300 seconds here). This is standard DNS resolution: clients query the name and receive the IP.
Split traffic between environments with weighted routing
Teams running blue-green deployments or gradual rollouts control what percentage of traffic reaches each environment through weighted routing.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const www_dev = new aws.route53.Record("www-dev", {
zoneId: primary.zoneId,
name: "www",
type: aws.route53.RecordType.CNAME,
ttl: 5,
weightedRoutingPolicies: [{
weight: 10,
}],
setIdentifier: "dev",
records: ["dev.example.com"],
});
const www_live = new aws.route53.Record("www-live", {
zoneId: primary.zoneId,
name: "www",
type: aws.route53.RecordType.CNAME,
ttl: 5,
weightedRoutingPolicies: [{
weight: 90,
}],
setIdentifier: "live",
records: ["live.example.com"],
});
import pulumi
import pulumi_aws as aws
www_dev = aws.route53.Record("www-dev",
zone_id=primary["zoneId"],
name="www",
type=aws.route53.RecordType.CNAME,
ttl=5,
weighted_routing_policies=[{
"weight": 10,
}],
set_identifier="dev",
records=["dev.example.com"])
www_live = aws.route53.Record("www-live",
zone_id=primary["zoneId"],
name="www",
type=aws.route53.RecordType.CNAME,
ttl=5,
weighted_routing_policies=[{
"weight": 90,
}],
set_identifier="live",
records=["live.example.com"])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/route53"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := route53.NewRecord(ctx, "www-dev", &route53.RecordArgs{
ZoneId: pulumi.Any(primary.ZoneId),
Name: pulumi.String("www"),
Type: pulumi.String(route53.RecordTypeCNAME),
Ttl: pulumi.Int(5),
WeightedRoutingPolicies: route53.RecordWeightedRoutingPolicyArray{
&route53.RecordWeightedRoutingPolicyArgs{
Weight: pulumi.Int(10),
},
},
SetIdentifier: pulumi.String("dev"),
Records: pulumi.StringArray{
pulumi.String("dev.example.com"),
},
})
if err != nil {
return err
}
_, err = route53.NewRecord(ctx, "www-live", &route53.RecordArgs{
ZoneId: pulumi.Any(primary.ZoneId),
Name: pulumi.String("www"),
Type: pulumi.String(route53.RecordTypeCNAME),
Ttl: pulumi.Int(5),
WeightedRoutingPolicies: route53.RecordWeightedRoutingPolicyArray{
&route53.RecordWeightedRoutingPolicyArgs{
Weight: pulumi.Int(90),
},
},
SetIdentifier: pulumi.String("live"),
Records: pulumi.StringArray{
pulumi.String("live.example.com"),
},
})
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 www_dev = new Aws.Route53.Record("www-dev", new()
{
ZoneId = primary.ZoneId,
Name = "www",
Type = Aws.Route53.RecordType.CNAME,
Ttl = 5,
WeightedRoutingPolicies = new[]
{
new Aws.Route53.Inputs.RecordWeightedRoutingPolicyArgs
{
Weight = 10,
},
},
SetIdentifier = "dev",
Records = new[]
{
"dev.example.com",
},
});
var www_live = new Aws.Route53.Record("www-live", new()
{
ZoneId = primary.ZoneId,
Name = "www",
Type = Aws.Route53.RecordType.CNAME,
Ttl = 5,
WeightedRoutingPolicies = new[]
{
new Aws.Route53.Inputs.RecordWeightedRoutingPolicyArgs
{
Weight = 90,
},
},
SetIdentifier = "live",
Records = new[]
{
"live.example.com",
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.route53.Record;
import com.pulumi.aws.route53.RecordArgs;
import com.pulumi.aws.route53.inputs.RecordWeightedRoutingPolicyArgs;
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 www_dev = new Record("www-dev", RecordArgs.builder()
.zoneId(primary.zoneId())
.name("www")
.type("CNAME")
.ttl(5)
.weightedRoutingPolicies(RecordWeightedRoutingPolicyArgs.builder()
.weight(10)
.build())
.setIdentifier("dev")
.records("dev.example.com")
.build());
var www_live = new Record("www-live", RecordArgs.builder()
.zoneId(primary.zoneId())
.name("www")
.type("CNAME")
.ttl(5)
.weightedRoutingPolicies(RecordWeightedRoutingPolicyArgs.builder()
.weight(90)
.build())
.setIdentifier("live")
.records("live.example.com")
.build());
}
}
resources:
www-dev:
type: aws:route53:Record
properties:
zoneId: ${primary.zoneId}
name: www
type: CNAME
ttl: 5
weightedRoutingPolicies:
- weight: 10
setIdentifier: dev
records:
- dev.example.com
www-live:
type: aws:route53:Record
properties:
zoneId: ${primary.zoneId}
name: www
type: CNAME
ttl: 5
weightedRoutingPolicies:
- weight: 90
setIdentifier: live
records:
- live.example.com
Each record with the same name and type needs a unique setIdentifier. The weightedRoutingPolicies block assigns a weight; Route 53 distributes traffic proportionally (10% to dev, 90% to live in this case). Both records must exist for weighted routing to work.
Create alias records for AWS load balancers
AWS services like ELB expose DNS names that change over time. Alias records point your domain to these services without hardcoding IPs.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const main = new aws.elb.LoadBalancer("main", {
name: "foobar-elb",
availabilityZones: ["us-east-1c"],
listeners: [{
instancePort: 80,
instanceProtocol: "http",
lbPort: 80,
lbProtocol: "http",
}],
});
const www = new aws.route53.Record("www", {
zoneId: primary.zoneId,
name: "example.com",
type: aws.route53.RecordType.A,
aliases: [{
name: main.dnsName,
zoneId: main.zoneId,
evaluateTargetHealth: true,
}],
});
import pulumi
import pulumi_aws as aws
main = aws.elb.LoadBalancer("main",
name="foobar-elb",
availability_zones=["us-east-1c"],
listeners=[{
"instance_port": 80,
"instance_protocol": "http",
"lb_port": 80,
"lb_protocol": "http",
}])
www = aws.route53.Record("www",
zone_id=primary["zoneId"],
name="example.com",
type=aws.route53.RecordType.A,
aliases=[{
"name": main.dns_name,
"zone_id": main.zone_id,
"evaluate_target_health": True,
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/elb"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/route53"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
main, err := elb.NewLoadBalancer(ctx, "main", &elb.LoadBalancerArgs{
Name: pulumi.String("foobar-elb"),
AvailabilityZones: pulumi.StringArray{
pulumi.String("us-east-1c"),
},
Listeners: elb.LoadBalancerListenerArray{
&elb.LoadBalancerListenerArgs{
InstancePort: pulumi.Int(80),
InstanceProtocol: pulumi.String("http"),
LbPort: pulumi.Int(80),
LbProtocol: pulumi.String("http"),
},
},
})
if err != nil {
return err
}
_, err = route53.NewRecord(ctx, "www", &route53.RecordArgs{
ZoneId: pulumi.Any(primary.ZoneId),
Name: pulumi.String("example.com"),
Type: pulumi.String(route53.RecordTypeA),
Aliases: route53.RecordAliasArray{
&route53.RecordAliasArgs{
Name: main.DnsName,
ZoneId: main.ZoneId,
EvaluateTargetHealth: pulumi.Bool(true),
},
},
})
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.Elb.LoadBalancer("main", new()
{
Name = "foobar-elb",
AvailabilityZones = new[]
{
"us-east-1c",
},
Listeners = new[]
{
new Aws.Elb.Inputs.LoadBalancerListenerArgs
{
InstancePort = 80,
InstanceProtocol = "http",
LbPort = 80,
LbProtocol = "http",
},
},
});
var www = new Aws.Route53.Record("www", new()
{
ZoneId = primary.ZoneId,
Name = "example.com",
Type = Aws.Route53.RecordType.A,
Aliases = new[]
{
new Aws.Route53.Inputs.RecordAliasArgs
{
Name = main.DnsName,
ZoneId = main.ZoneId,
EvaluateTargetHealth = true,
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elb.LoadBalancer;
import com.pulumi.aws.elb.LoadBalancerArgs;
import com.pulumi.aws.elb.inputs.LoadBalancerListenerArgs;
import com.pulumi.aws.route53.Record;
import com.pulumi.aws.route53.RecordArgs;
import com.pulumi.aws.route53.inputs.RecordAliasArgs;
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 LoadBalancer("main", LoadBalancerArgs.builder()
.name("foobar-elb")
.availabilityZones("us-east-1c")
.listeners(LoadBalancerListenerArgs.builder()
.instancePort(80)
.instanceProtocol("http")
.lbPort(80)
.lbProtocol("http")
.build())
.build());
var www = new Record("www", RecordArgs.builder()
.zoneId(primary.zoneId())
.name("example.com")
.type("A")
.aliases(RecordAliasArgs.builder()
.name(main.dnsName())
.zoneId(main.zoneId())
.evaluateTargetHealth(true)
.build())
.build());
}
}
resources:
main:
type: aws:elb:LoadBalancer
properties:
name: foobar-elb
availabilityZones:
- us-east-1c
listeners:
- instancePort: 80
instanceProtocol: http
lbPort: 80
lbProtocol: http
www:
type: aws:route53:Record
properties:
zoneId: ${primary.zoneId}
name: example.com
type: A
aliases:
- name: ${main.dnsName}
zoneId: ${main.zoneId}
evaluateTargetHealth: true
Alias records use the aliases property instead of records. The name and zoneId come from the target service (here, an ELB). Route 53 automatically tracks the underlying IPs. The evaluateTargetHealth property determines whether Route 53 checks the target’s health before routing traffic. Alias records have a fixed 60-second TTL; you cannot set ttl explicitly.
Manage NS records for a hosted zone
Route 53 automatically creates NS and SOA records when you create a hosted zone. The allowOverwrite property lets you customize them without importing first.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.route53.Zone("example", {name: "test.example.com"});
const exampleRecord = new aws.route53.Record("example", {
allowOverwrite: true,
name: "test.example.com",
ttl: 172800,
type: aws.route53.RecordType.NS,
zoneId: example.zoneId,
records: [
example.nameServers[0],
example.nameServers[1],
example.nameServers[2],
example.nameServers[3],
],
});
import pulumi
import pulumi_aws as aws
example = aws.route53.Zone("example", name="test.example.com")
example_record = aws.route53.Record("example",
allow_overwrite=True,
name="test.example.com",
ttl=172800,
type=aws.route53.RecordType.NS,
zone_id=example.zone_id,
records=[
example.name_servers[0],
example.name_servers[1],
example.name_servers[2],
example.name_servers[3],
])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/route53"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
example, err := route53.NewZone(ctx, "example", &route53.ZoneArgs{
Name: pulumi.String("test.example.com"),
})
if err != nil {
return err
}
_, err = route53.NewRecord(ctx, "example", &route53.RecordArgs{
AllowOverwrite: pulumi.Bool(true),
Name: pulumi.String("test.example.com"),
Ttl: pulumi.Int(172800),
Type: pulumi.String(route53.RecordTypeNS),
ZoneId: example.ZoneId,
Records: pulumi.StringArray{
example.NameServers.ApplyT(func(nameServers []string) (string, error) {
return nameServers[0], nil
}).(pulumi.StringOutput),
example.NameServers.ApplyT(func(nameServers []string) (string, error) {
return nameServers[1], nil
}).(pulumi.StringOutput),
example.NameServers.ApplyT(func(nameServers []string) (string, error) {
return nameServers[2], nil
}).(pulumi.StringOutput),
example.NameServers.ApplyT(func(nameServers []string) (string, error) {
return nameServers[3], nil
}).(pulumi.StringOutput),
},
})
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.Route53.Zone("example", new()
{
Name = "test.example.com",
});
var exampleRecord = new Aws.Route53.Record("example", new()
{
AllowOverwrite = true,
Name = "test.example.com",
Ttl = 172800,
Type = Aws.Route53.RecordType.NS,
ZoneId = example.ZoneId,
Records = new[]
{
example.NameServers.Apply(nameServers => nameServers[0]),
example.NameServers.Apply(nameServers => nameServers[1]),
example.NameServers.Apply(nameServers => nameServers[2]),
example.NameServers.Apply(nameServers => nameServers[3]),
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.route53.Zone;
import com.pulumi.aws.route53.ZoneArgs;
import com.pulumi.aws.route53.Record;
import com.pulumi.aws.route53.RecordArgs;
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 Zone("example", ZoneArgs.builder()
.name("test.example.com")
.build());
var exampleRecord = new Record("exampleRecord", RecordArgs.builder()
.allowOverwrite(true)
.name("test.example.com")
.ttl(172800)
.type("NS")
.zoneId(example.zoneId())
.records(
example.nameServers().applyValue(_nameServers -> _nameServers[0]),
example.nameServers().applyValue(_nameServers -> _nameServers[1]),
example.nameServers().applyValue(_nameServers -> _nameServers[2]),
example.nameServers().applyValue(_nameServers -> _nameServers[3]))
.build());
}
}
resources:
example:
type: aws:route53:Zone
properties:
name: test.example.com
exampleRecord:
type: aws:route53:Record
name: example
properties:
allowOverwrite: true
name: test.example.com
ttl: 172800
type: NS
zoneId: ${example.zoneId}
records:
- ${example.nameServers[0]}
- ${example.nameServers[1]}
- ${example.nameServers[2]}
- ${example.nameServers[3]}
The allowOverwrite property permits replacing the auto-generated NS record. The records array references the zone’s nameServers output. This pattern is useful when you need to manage NS records alongside zone creation, but it’s not recommended for most environments since Route 53 handles these records automatically.
Beyond these examples
These snippets focus on specific record-level features: basic A/CNAME records with TTL, weighted routing for traffic splitting, and alias records for AWS services. They’re intentionally minimal rather than full DNS configurations.
The examples may reference pre-existing infrastructure such as Route 53 hosted zones, and load balancers, accelerators, or other AWS services with DNS names. They focus on configuring the record rather than provisioning the surrounding infrastructure.
To keep things focused, common record patterns are omitted, including:
- Health checks and failover routing (healthCheckId, failoverRoutingPolicies)
- Geographic routing (geolocationRoutingPolicies, geoproximityRoutingPolicy)
- Latency-based routing (latencyRoutingPolicies)
- CIDR-based routing (cidrRoutingPolicy)
These omissions are intentional: the goal is to illustrate how each record feature is wired, not provide drop-in DNS modules. See the Route 53 Record resource reference for all available configuration options.
Let's configure AWS Route 53 DNS Records
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Alias Records & TTL
aliases property, while non-alias records use the records property with IP addresses or domain names. You must specify exactly one of aliases or records, not both.ttl property when creating alias records.aliases property with the resource’s dnsName and zoneId (or hostedZoneId for Global Accelerator), set evaluateTargetHealth as needed, and omit the ttl property.Routing Policies
setIdentifier is required when using any routing policy: cidrRoutingPolicy, failoverRoutingPolicy, geolocationRoutingPolicy, geoproximityRoutingPolicy, latencyRoutingPolicy, multivalueAnswerRoutingPolicy, or weightedRoutingPolicy.name and type, each with a unique setIdentifier and different weights in weightedRoutingPolicies. For example, assign weight 10 to dev and weight 90 to live.Record Configuration
name, type, and zoneId properties are immutable and cannot be changed after creation.\"\" inside the configuration string at the 255-character boundary. For example: "first255characters\"\"morecharacters".NS & SOA Management
allowOverwrite to true to manage the auto-created NS and SOA records in a single deployment. However, this configuration is not recommended for most environments.allowOverwrite is not recommended for most environments as it can lead to unintended record overwrites. Use it only for specific cases like managing auto-created NS/SOA records.Using a different cloud?
Explore networking guides for other cloud providers: