Create GCP DNS Record Sets

The gcp:dns/recordSet:RecordSet resource, part of the Pulumi GCP provider, defines DNS records within a Cloud DNS managed zone: hostname mappings, mail routing, and traffic steering policies. This guide focuses on three capabilities: standard record types (A, MX, CNAME), dynamic IP binding from compute resources, and geographic and failover routing.

Record sets belong to managed zones and may reference compute instances, load balancers, or networks for dynamic values and routing policies. The examples are intentionally small. Combine them with your own managed zones and infrastructure.

Add an A record with static IP addresses

Most DNS configurations start with A records that map hostnames to IPv4 addresses, directing traffic to servers or load balancers.

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

const prod = new gcp.dns.ManagedZone("prod", {
    name: "prod-zone",
    dnsName: "prod.mydomain.com.",
});
const a = new gcp.dns.RecordSet("a", {
    name: pulumi.interpolate`backend.${prod.dnsName}`,
    managedZone: prod.name,
    type: "A",
    ttl: 300,
    rrdatas: ["8.8.8.8"],
});
import pulumi
import pulumi_gcp as gcp

prod = gcp.dns.ManagedZone("prod",
    name="prod-zone",
    dns_name="prod.mydomain.com.")
a = gcp.dns.RecordSet("a",
    name=prod.dns_name.apply(lambda dns_name: f"backend.{dns_name}"),
    managed_zone=prod.name,
    type="A",
    ttl=300,
    rrdatas=["8.8.8.8"])
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/dns"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		prod, err := dns.NewManagedZone(ctx, "prod", &dns.ManagedZoneArgs{
			Name:    pulumi.String("prod-zone"),
			DnsName: pulumi.String("prod.mydomain.com."),
		})
		if err != nil {
			return err
		}
		_, err = dns.NewRecordSet(ctx, "a", &dns.RecordSetArgs{
			Name: prod.DnsName.ApplyT(func(dnsName string) (string, error) {
				return fmt.Sprintf("backend.%v", dnsName), nil
			}).(pulumi.StringOutput),
			ManagedZone: prod.Name,
			Type:        pulumi.String("A"),
			Ttl:         pulumi.Int(300),
			Rrdatas: pulumi.StringArray{
				pulumi.String("8.8.8.8"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var prod = new Gcp.Dns.ManagedZone("prod", new()
    {
        Name = "prod-zone",
        DnsName = "prod.mydomain.com.",
    });

    var a = new Gcp.Dns.RecordSet("a", new()
    {
        Name = prod.DnsName.Apply(dnsName => $"backend.{dnsName}"),
        ManagedZone = prod.Name,
        Type = "A",
        Ttl = 300,
        Rrdatas = new[]
        {
            "8.8.8.8",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.dns.ManagedZone;
import com.pulumi.gcp.dns.ManagedZoneArgs;
import com.pulumi.gcp.dns.RecordSet;
import com.pulumi.gcp.dns.RecordSetArgs;
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 prod = new ManagedZone("prod", ManagedZoneArgs.builder()
            .name("prod-zone")
            .dnsName("prod.mydomain.com.")
            .build());

        var a = new RecordSet("a", RecordSetArgs.builder()
            .name(prod.dnsName().applyValue(_dnsName -> String.format("backend.%s", _dnsName)))
            .managedZone(prod.name())
            .type("A")
            .ttl(300)
            .rrdatas("8.8.8.8")
            .build());

    }
}
resources:
  a:
    type: gcp:dns:RecordSet
    properties:
      name: backend.${prod.dnsName}
      managedZone: ${prod.name}
      type: A
      ttl: 300
      rrdatas:
        - 8.8.8.8
  prod:
    type: gcp:dns:ManagedZone
    properties:
      name: prod-zone
      dnsName: prod.mydomain.com.

The name property sets the fully qualified domain name (must end with a dot). The rrdatas array contains one or more IPv4 addresses. The ttl property controls how long resolvers cache this record (in seconds).

Configure mail routing with MX records

Email delivery requires MX records that specify mail servers and their priority order, with lower values indicating preferred servers.

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

const prod = new gcp.dns.ManagedZone("prod", {
    name: "prod-zone",
    dnsName: "prod.mydomain.com.",
});
const mx = new gcp.dns.RecordSet("mx", {
    name: prod.dnsName,
    managedZone: prod.name,
    type: "MX",
    ttl: 3600,
    rrdatas: [
        "1 aspmx.l.google.com.",
        "5 alt1.aspmx.l.google.com.",
        "5 alt2.aspmx.l.google.com.",
        "10 alt3.aspmx.l.google.com.",
        "10 alt4.aspmx.l.google.com.",
    ],
});
import pulumi
import pulumi_gcp as gcp

prod = gcp.dns.ManagedZone("prod",
    name="prod-zone",
    dns_name="prod.mydomain.com.")
mx = gcp.dns.RecordSet("mx",
    name=prod.dns_name,
    managed_zone=prod.name,
    type="MX",
    ttl=3600,
    rrdatas=[
        "1 aspmx.l.google.com.",
        "5 alt1.aspmx.l.google.com.",
        "5 alt2.aspmx.l.google.com.",
        "10 alt3.aspmx.l.google.com.",
        "10 alt4.aspmx.l.google.com.",
    ])
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/dns"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		prod, err := dns.NewManagedZone(ctx, "prod", &dns.ManagedZoneArgs{
			Name:    pulumi.String("prod-zone"),
			DnsName: pulumi.String("prod.mydomain.com."),
		})
		if err != nil {
			return err
		}
		_, err = dns.NewRecordSet(ctx, "mx", &dns.RecordSetArgs{
			Name:        prod.DnsName,
			ManagedZone: prod.Name,
			Type:        pulumi.String("MX"),
			Ttl:         pulumi.Int(3600),
			Rrdatas: pulumi.StringArray{
				pulumi.String("1 aspmx.l.google.com."),
				pulumi.String("5 alt1.aspmx.l.google.com."),
				pulumi.String("5 alt2.aspmx.l.google.com."),
				pulumi.String("10 alt3.aspmx.l.google.com."),
				pulumi.String("10 alt4.aspmx.l.google.com."),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var prod = new Gcp.Dns.ManagedZone("prod", new()
    {
        Name = "prod-zone",
        DnsName = "prod.mydomain.com.",
    });

    var mx = new Gcp.Dns.RecordSet("mx", new()
    {
        Name = prod.DnsName,
        ManagedZone = prod.Name,
        Type = "MX",
        Ttl = 3600,
        Rrdatas = new[]
        {
            "1 aspmx.l.google.com.",
            "5 alt1.aspmx.l.google.com.",
            "5 alt2.aspmx.l.google.com.",
            "10 alt3.aspmx.l.google.com.",
            "10 alt4.aspmx.l.google.com.",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.dns.ManagedZone;
import com.pulumi.gcp.dns.ManagedZoneArgs;
import com.pulumi.gcp.dns.RecordSet;
import com.pulumi.gcp.dns.RecordSetArgs;
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 prod = new ManagedZone("prod", ManagedZoneArgs.builder()
            .name("prod-zone")
            .dnsName("prod.mydomain.com.")
            .build());

        var mx = new RecordSet("mx", RecordSetArgs.builder()
            .name(prod.dnsName())
            .managedZone(prod.name())
            .type("MX")
            .ttl(3600)
            .rrdatas(            
                "1 aspmx.l.google.com.",
                "5 alt1.aspmx.l.google.com.",
                "5 alt2.aspmx.l.google.com.",
                "10 alt3.aspmx.l.google.com.",
                "10 alt4.aspmx.l.google.com.")
            .build());

    }
}
resources:
  mx:
    type: gcp:dns:RecordSet
    properties:
      name: ${prod.dnsName}
      managedZone: ${prod.name}
      type: MX
      ttl: 3600
      rrdatas:
        - 1 aspmx.l.google.com.
        - 5 alt1.aspmx.l.google.com.
        - 5 alt2.aspmx.l.google.com.
        - 10 alt3.aspmx.l.google.com.
        - 10 alt4.aspmx.l.google.com.
  prod:
    type: gcp:dns:ManagedZone
    properties:
      name: prod-zone
      dnsName: prod.mydomain.com.

MX records use the rrdatas array to list mail servers with priority prefixes (e.g., “1 aspmx.l.google.com.”). Mail servers try the lowest priority first, falling back to higher values if delivery fails.

Create aliases with CNAME records

CNAME records create aliases that point one hostname to another, useful for service endpoints or CDN configurations.

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

const prod = new gcp.dns.ManagedZone("prod", {
    name: "prod-zone",
    dnsName: "prod.mydomain.com.",
});
const cname = new gcp.dns.RecordSet("cname", {
    name: pulumi.interpolate`frontend.${prod.dnsName}`,
    managedZone: prod.name,
    type: "CNAME",
    ttl: 300,
    rrdatas: ["frontend.mydomain.com."],
});
import pulumi
import pulumi_gcp as gcp

prod = gcp.dns.ManagedZone("prod",
    name="prod-zone",
    dns_name="prod.mydomain.com.")
cname = gcp.dns.RecordSet("cname",
    name=prod.dns_name.apply(lambda dns_name: f"frontend.{dns_name}"),
    managed_zone=prod.name,
    type="CNAME",
    ttl=300,
    rrdatas=["frontend.mydomain.com."])
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/dns"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		prod, err := dns.NewManagedZone(ctx, "prod", &dns.ManagedZoneArgs{
			Name:    pulumi.String("prod-zone"),
			DnsName: pulumi.String("prod.mydomain.com."),
		})
		if err != nil {
			return err
		}
		_, err = dns.NewRecordSet(ctx, "cname", &dns.RecordSetArgs{
			Name: prod.DnsName.ApplyT(func(dnsName string) (string, error) {
				return fmt.Sprintf("frontend.%v", dnsName), nil
			}).(pulumi.StringOutput),
			ManagedZone: prod.Name,
			Type:        pulumi.String("CNAME"),
			Ttl:         pulumi.Int(300),
			Rrdatas: pulumi.StringArray{
				pulumi.String("frontend.mydomain.com."),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var prod = new Gcp.Dns.ManagedZone("prod", new()
    {
        Name = "prod-zone",
        DnsName = "prod.mydomain.com.",
    });

    var cname = new Gcp.Dns.RecordSet("cname", new()
    {
        Name = prod.DnsName.Apply(dnsName => $"frontend.{dnsName}"),
        ManagedZone = prod.Name,
        Type = "CNAME",
        Ttl = 300,
        Rrdatas = new[]
        {
            "frontend.mydomain.com.",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.dns.ManagedZone;
import com.pulumi.gcp.dns.ManagedZoneArgs;
import com.pulumi.gcp.dns.RecordSet;
import com.pulumi.gcp.dns.RecordSetArgs;
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 prod = new ManagedZone("prod", ManagedZoneArgs.builder()
            .name("prod-zone")
            .dnsName("prod.mydomain.com.")
            .build());

        var cname = new RecordSet("cname", RecordSetArgs.builder()
            .name(prod.dnsName().applyValue(_dnsName -> String.format("frontend.%s", _dnsName)))
            .managedZone(prod.name())
            .type("CNAME")
            .ttl(300)
            .rrdatas("frontend.mydomain.com.")
            .build());

    }
}
resources:
  cname:
    type: gcp:dns:RecordSet
    properties:
      name: frontend.${prod.dnsName}
      managedZone: ${prod.name}
      type: CNAME
      ttl: 300
      rrdatas:
        - frontend.mydomain.com.
  prod:
    type: gcp:dns:ManagedZone
    properties:
      name: prod-zone
      dnsName: prod.mydomain.com.

CNAME records require exactly one entry in rrdatas: the canonical hostname. The type property must be “CNAME”, and the target hostname must end with a dot.

Bind DNS to dynamically assigned instance IPs

When launching compute instances, their public IPs are assigned at creation time. DNS records can reference these dynamic values using Pulumi’s interpolation.

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

const frontendInstance = new gcp.compute.Instance("frontend", {
    networkInterfaces: [{
        accessConfigs: [{}],
        network: "default",
    }],
    name: "frontend",
    machineType: "g1-small",
    zone: "us-central1-b",
    bootDisk: {
        initializeParams: {
            image: "debian-cloud/debian-11",
        },
    },
});
const prod = new gcp.dns.ManagedZone("prod", {
    name: "prod-zone",
    dnsName: "prod.mydomain.com.",
});
const frontend = new gcp.dns.RecordSet("frontend", {
    name: pulumi.interpolate`frontend.${prod.dnsName}`,
    type: "A",
    ttl: 300,
    managedZone: prod.name,
    rrdatas: [frontendInstance.networkInterfaces.apply(networkInterfaces => networkInterfaces[0].accessConfigs?.[0]?.natIp)],
});
import pulumi
import pulumi_gcp as gcp

frontend_instance = gcp.compute.Instance("frontend",
    network_interfaces=[{
        "access_configs": [{}],
        "network": "default",
    }],
    name="frontend",
    machine_type="g1-small",
    zone="us-central1-b",
    boot_disk={
        "initialize_params": {
            "image": "debian-cloud/debian-11",
        },
    })
prod = gcp.dns.ManagedZone("prod",
    name="prod-zone",
    dns_name="prod.mydomain.com.")
frontend = gcp.dns.RecordSet("frontend",
    name=prod.dns_name.apply(lambda dns_name: f"frontend.{dns_name}"),
    type="A",
    ttl=300,
    managed_zone=prod.name,
    rrdatas=[frontend_instance.network_interfaces[0].access_configs[0].nat_ip])
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/dns"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		frontendInstance, err := compute.NewInstance(ctx, "frontend", &compute.InstanceArgs{
			NetworkInterfaces: compute.InstanceNetworkInterfaceArray{
				&compute.InstanceNetworkInterfaceArgs{
					AccessConfigs: compute.InstanceNetworkInterfaceAccessConfigArray{
						&compute.InstanceNetworkInterfaceAccessConfigArgs{},
					},
					Network: pulumi.String("default"),
				},
			},
			Name:        pulumi.String("frontend"),
			MachineType: pulumi.String("g1-small"),
			Zone:        pulumi.String("us-central1-b"),
			BootDisk: &compute.InstanceBootDiskArgs{
				InitializeParams: &compute.InstanceBootDiskInitializeParamsArgs{
					Image: pulumi.String("debian-cloud/debian-11"),
				},
			},
		})
		if err != nil {
			return err
		}
		prod, err := dns.NewManagedZone(ctx, "prod", &dns.ManagedZoneArgs{
			Name:    pulumi.String("prod-zone"),
			DnsName: pulumi.String("prod.mydomain.com."),
		})
		if err != nil {
			return err
		}
		_, err = dns.NewRecordSet(ctx, "frontend", &dns.RecordSetArgs{
			Name: prod.DnsName.ApplyT(func(dnsName string) (string, error) {
				return fmt.Sprintf("frontend.%v", dnsName), nil
			}).(pulumi.StringOutput),
			Type:        pulumi.String("A"),
			Ttl:         pulumi.Int(300),
			ManagedZone: prod.Name,
			Rrdatas: pulumi.StringArray{
				pulumi.String(frontendInstance.NetworkInterfaces.ApplyT(func(networkInterfaces []compute.InstanceNetworkInterface) (*string, error) {
					return &networkInterfaces[0].AccessConfigs[0].NatIp, nil
				}).(pulumi.StringPtrOutput)),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var frontendInstance = new Gcp.Compute.Instance("frontend", new()
    {
        NetworkInterfaces = new[]
        {
            new Gcp.Compute.Inputs.InstanceNetworkInterfaceArgs
            {
                AccessConfigs = new[]
                {
                    null,
                },
                Network = "default",
            },
        },
        Name = "frontend",
        MachineType = "g1-small",
        Zone = "us-central1-b",
        BootDisk = new Gcp.Compute.Inputs.InstanceBootDiskArgs
        {
            InitializeParams = new Gcp.Compute.Inputs.InstanceBootDiskInitializeParamsArgs
            {
                Image = "debian-cloud/debian-11",
            },
        },
    });

    var prod = new Gcp.Dns.ManagedZone("prod", new()
    {
        Name = "prod-zone",
        DnsName = "prod.mydomain.com.",
    });

    var frontend = new Gcp.Dns.RecordSet("frontend", new()
    {
        Name = prod.DnsName.Apply(dnsName => $"frontend.{dnsName}"),
        Type = "A",
        Ttl = 300,
        ManagedZone = prod.Name,
        Rrdatas = new[]
        {
            frontendInstance.NetworkInterfaces.Apply(networkInterfaces => networkInterfaces[0].AccessConfigs[0]?.NatIp),
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Instance;
import com.pulumi.gcp.compute.InstanceArgs;
import com.pulumi.gcp.compute.inputs.InstanceNetworkInterfaceArgs;
import com.pulumi.gcp.compute.inputs.InstanceBootDiskArgs;
import com.pulumi.gcp.compute.inputs.InstanceBootDiskInitializeParamsArgs;
import com.pulumi.gcp.dns.ManagedZone;
import com.pulumi.gcp.dns.ManagedZoneArgs;
import com.pulumi.gcp.dns.RecordSet;
import com.pulumi.gcp.dns.RecordSetArgs;
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 frontendInstance = new Instance("frontendInstance", InstanceArgs.builder()
            .networkInterfaces(InstanceNetworkInterfaceArgs.builder()
                .accessConfigs(InstanceNetworkInterfaceAccessConfigArgs.builder()
                    .build())
                .network("default")
                .build())
            .name("frontend")
            .machineType("g1-small")
            .zone("us-central1-b")
            .bootDisk(InstanceBootDiskArgs.builder()
                .initializeParams(InstanceBootDiskInitializeParamsArgs.builder()
                    .image("debian-cloud/debian-11")
                    .build())
                .build())
            .build());

        var prod = new ManagedZone("prod", ManagedZoneArgs.builder()
            .name("prod-zone")
            .dnsName("prod.mydomain.com.")
            .build());

        var frontend = new RecordSet("frontend", RecordSetArgs.builder()
            .name(prod.dnsName().applyValue(_dnsName -> String.format("frontend.%s", _dnsName)))
            .type("A")
            .ttl(300)
            .managedZone(prod.name())
            .rrdatas(frontendInstance.networkInterfaces().applyValue(_networkInterfaces -> _networkInterfaces[0].accessConfigs()[0].natIp()))
            .build());

    }
}
resources:
  frontend:
    type: gcp:dns:RecordSet
    properties:
      name: frontend.${prod.dnsName}
      type: A
      ttl: 300
      managedZone: ${prod.name}
      rrdatas:
        - ${frontendInstance.networkInterfaces[0].accessConfigs[0].natIp}
  frontendInstance:
    type: gcp:compute:Instance
    name: frontend
    properties:
      networkInterfaces:
        - accessConfigs:
            - {}
          network: default
      name: frontend
      machineType: g1-small
      zone: us-central1-b
      bootDisk:
        initializeParams:
          image: debian-cloud/debian-11
  prod:
    type: gcp:dns:ManagedZone
    properties:
      name: prod-zone
      dnsName: prod.mydomain.com.

The rrdatas array references the instance’s natIp from its network interface configuration. Pulumi resolves this value at deployment time, creating the DNS record after the instance receives its IP address.

Route traffic by geographic location

Applications serving global users can direct traffic to regional endpoints based on the client’s geographic location, reducing latency.

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

const geo = new gcp.dns.RecordSet("geo", {
    name: `backend.${prod.dnsName}`,
    managedZone: prod.name,
    type: "A",
    ttl: 300,
    routingPolicy: {
        geos: [
            {
                location: "asia-east1",
                rrdatas: ["10.128.1.1"],
            },
            {
                location: "us-central1",
                rrdatas: ["10.130.1.1"],
            },
        ],
    },
});
import pulumi
import pulumi_gcp as gcp

geo = gcp.dns.RecordSet("geo",
    name=f"backend.{prod['dnsName']}",
    managed_zone=prod["name"],
    type="A",
    ttl=300,
    routing_policy={
        "geos": [
            {
                "location": "asia-east1",
                "rrdatas": ["10.128.1.1"],
            },
            {
                "location": "us-central1",
                "rrdatas": ["10.130.1.1"],
            },
        ],
    })
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/dns"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := dns.NewRecordSet(ctx, "geo", &dns.RecordSetArgs{
			Name:        pulumi.Sprintf("backend.%v", prod.DnsName),
			ManagedZone: pulumi.Any(prod.Name),
			Type:        pulumi.String("A"),
			Ttl:         pulumi.Int(300),
			RoutingPolicy: &dns.RecordSetRoutingPolicyArgs{
				Geos: dns.RecordSetRoutingPolicyGeoArray{
					&dns.RecordSetRoutingPolicyGeoArgs{
						Location: pulumi.String("asia-east1"),
						Rrdatas: pulumi.StringArray{
							pulumi.String("10.128.1.1"),
						},
					},
					&dns.RecordSetRoutingPolicyGeoArgs{
						Location: pulumi.String("us-central1"),
						Rrdatas: pulumi.StringArray{
							pulumi.String("10.130.1.1"),
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var geo = new Gcp.Dns.RecordSet("geo", new()
    {
        Name = $"backend.{prod.DnsName}",
        ManagedZone = prod.Name,
        Type = "A",
        Ttl = 300,
        RoutingPolicy = new Gcp.Dns.Inputs.RecordSetRoutingPolicyArgs
        {
            Geos = new[]
            {
                new Gcp.Dns.Inputs.RecordSetRoutingPolicyGeoArgs
                {
                    Location = "asia-east1",
                    Rrdatas = new[]
                    {
                        "10.128.1.1",
                    },
                },
                new Gcp.Dns.Inputs.RecordSetRoutingPolicyGeoArgs
                {
                    Location = "us-central1",
                    Rrdatas = new[]
                    {
                        "10.130.1.1",
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.dns.RecordSet;
import com.pulumi.gcp.dns.RecordSetArgs;
import com.pulumi.gcp.dns.inputs.RecordSetRoutingPolicyArgs;
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 geo = new RecordSet("geo", RecordSetArgs.builder()
            .name(String.format("backend.%s", prod.dnsName()))
            .managedZone(prod.name())
            .type("A")
            .ttl(300)
            .routingPolicy(RecordSetRoutingPolicyArgs.builder()
                .geos(                
                    RecordSetRoutingPolicyGeoArgs.builder()
                        .location("asia-east1")
                        .rrdatas("10.128.1.1")
                        .build(),
                    RecordSetRoutingPolicyGeoArgs.builder()
                        .location("us-central1")
                        .rrdatas("10.130.1.1")
                        .build())
                .build())
            .build());

    }
}
resources:
  geo:
    type: gcp:dns:RecordSet
    properties:
      name: backend.${prod.dnsName}
      managedZone: ${prod.name}
      type: A
      ttl: 300
      routingPolicy:
        geos:
          - location: asia-east1
            rrdatas:
              - 10.128.1.1
          - location: us-central1
            rrdatas:
              - 10.130.1.1

The routingPolicy property replaces rrdatas for advanced traffic steering. The geos array maps GCP regions to IP addresses. Cloud DNS returns the IP closest to the query origin.

Configure failover with primary and backup targets

High-availability architectures need automatic failover to backup endpoints when primary targets become unavailable.

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

const prod = new gcp.dns.ManagedZone("prod", {
    name: "prod-zone",
    dnsName: "prod.mydomain.com.",
    visibility: "private",
});
const prodRegionBackendService = new gcp.compute.RegionBackendService("prod", {
    name: "prod-backend",
    region: "us-central1",
});
const prodNetwork = new gcp.compute.Network("prod", {name: "prod-network"});
const prodForwardingRule = new gcp.compute.ForwardingRule("prod", {
    name: "prod-ilb",
    region: "us-central1",
    loadBalancingScheme: "INTERNAL",
    backendService: prodRegionBackendService.id,
    allPorts: true,
    network: prodNetwork.name,
    allowGlobalAccess: true,
});
const a = new gcp.dns.RecordSet("a", {
    name: pulumi.interpolate`backend.${prod.dnsName}`,
    managedZone: prod.name,
    type: "A",
    ttl: 300,
    routingPolicy: {
        primaryBackup: {
            trickleRatio: 0.1,
            primary: {
                internalLoadBalancers: [{
                    loadBalancerType: "regionalL4ilb",
                    ipAddress: prodForwardingRule.ipAddress,
                    port: "80",
                    ipProtocol: "tcp",
                    networkUrl: prodNetwork.id,
                    project: prodForwardingRule.project,
                    region: prodForwardingRule.region,
                }],
            },
            backupGeos: [
                {
                    location: "asia-east1",
                    rrdatas: ["10.128.1.1"],
                },
                {
                    location: "us-west1",
                    rrdatas: ["10.130.1.1"],
                },
            ],
        },
    },
});
import pulumi
import pulumi_gcp as gcp

prod = gcp.dns.ManagedZone("prod",
    name="prod-zone",
    dns_name="prod.mydomain.com.",
    visibility="private")
prod_region_backend_service = gcp.compute.RegionBackendService("prod",
    name="prod-backend",
    region="us-central1")
prod_network = gcp.compute.Network("prod", name="prod-network")
prod_forwarding_rule = gcp.compute.ForwardingRule("prod",
    name="prod-ilb",
    region="us-central1",
    load_balancing_scheme="INTERNAL",
    backend_service=prod_region_backend_service.id,
    all_ports=True,
    network=prod_network.name,
    allow_global_access=True)
a = gcp.dns.RecordSet("a",
    name=prod.dns_name.apply(lambda dns_name: f"backend.{dns_name}"),
    managed_zone=prod.name,
    type="A",
    ttl=300,
    routing_policy={
        "primary_backup": {
            "trickle_ratio": 0.1,
            "primary": {
                "internal_load_balancers": [{
                    "load_balancer_type": "regionalL4ilb",
                    "ip_address": prod_forwarding_rule.ip_address,
                    "port": "80",
                    "ip_protocol": "tcp",
                    "network_url": prod_network.id,
                    "project": prod_forwarding_rule.project,
                    "region": prod_forwarding_rule.region,
                }],
            },
            "backup_geos": [
                {
                    "location": "asia-east1",
                    "rrdatas": ["10.128.1.1"],
                },
                {
                    "location": "us-west1",
                    "rrdatas": ["10.130.1.1"],
                },
            ],
        },
    })
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/dns"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		prod, err := dns.NewManagedZone(ctx, "prod", &dns.ManagedZoneArgs{
			Name:       pulumi.String("prod-zone"),
			DnsName:    pulumi.String("prod.mydomain.com."),
			Visibility: pulumi.String("private"),
		})
		if err != nil {
			return err
		}
		prodRegionBackendService, err := compute.NewRegionBackendService(ctx, "prod", &compute.RegionBackendServiceArgs{
			Name:   pulumi.String("prod-backend"),
			Region: pulumi.String("us-central1"),
		})
		if err != nil {
			return err
		}
		prodNetwork, err := compute.NewNetwork(ctx, "prod", &compute.NetworkArgs{
			Name: pulumi.String("prod-network"),
		})
		if err != nil {
			return err
		}
		prodForwardingRule, err := compute.NewForwardingRule(ctx, "prod", &compute.ForwardingRuleArgs{
			Name:                pulumi.String("prod-ilb"),
			Region:              pulumi.String("us-central1"),
			LoadBalancingScheme: pulumi.String("INTERNAL"),
			BackendService:      prodRegionBackendService.ID(),
			AllPorts:            pulumi.Bool(true),
			Network:             prodNetwork.Name,
			AllowGlobalAccess:   pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		_, err = dns.NewRecordSet(ctx, "a", &dns.RecordSetArgs{
			Name: prod.DnsName.ApplyT(func(dnsName string) (string, error) {
				return fmt.Sprintf("backend.%v", dnsName), nil
			}).(pulumi.StringOutput),
			ManagedZone: prod.Name,
			Type:        pulumi.String("A"),
			Ttl:         pulumi.Int(300),
			RoutingPolicy: &dns.RecordSetRoutingPolicyArgs{
				PrimaryBackup: &dns.RecordSetRoutingPolicyPrimaryBackupArgs{
					TrickleRatio: pulumi.Float64(0.1),
					Primary: &dns.RecordSetRoutingPolicyPrimaryBackupPrimaryArgs{
						InternalLoadBalancers: dns.RecordSetRoutingPolicyPrimaryBackupPrimaryInternalLoadBalancerArray{
							&dns.RecordSetRoutingPolicyPrimaryBackupPrimaryInternalLoadBalancerArgs{
								LoadBalancerType: pulumi.String("regionalL4ilb"),
								IpAddress:        prodForwardingRule.IpAddress,
								Port:             pulumi.String("80"),
								IpProtocol:       pulumi.String("tcp"),
								NetworkUrl:       prodNetwork.ID(),
								Project:          prodForwardingRule.Project,
								Region:           prodForwardingRule.Region,
							},
						},
					},
					BackupGeos: dns.RecordSetRoutingPolicyPrimaryBackupBackupGeoArray{
						&dns.RecordSetRoutingPolicyPrimaryBackupBackupGeoArgs{
							Location: pulumi.String("asia-east1"),
							Rrdatas: pulumi.StringArray{
								pulumi.String("10.128.1.1"),
							},
						},
						&dns.RecordSetRoutingPolicyPrimaryBackupBackupGeoArgs{
							Location: pulumi.String("us-west1"),
							Rrdatas: pulumi.StringArray{
								pulumi.String("10.130.1.1"),
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var prod = new Gcp.Dns.ManagedZone("prod", new()
    {
        Name = "prod-zone",
        DnsName = "prod.mydomain.com.",
        Visibility = "private",
    });

    var prodRegionBackendService = new Gcp.Compute.RegionBackendService("prod", new()
    {
        Name = "prod-backend",
        Region = "us-central1",
    });

    var prodNetwork = new Gcp.Compute.Network("prod", new()
    {
        Name = "prod-network",
    });

    var prodForwardingRule = new Gcp.Compute.ForwardingRule("prod", new()
    {
        Name = "prod-ilb",
        Region = "us-central1",
        LoadBalancingScheme = "INTERNAL",
        BackendService = prodRegionBackendService.Id,
        AllPorts = true,
        Network = prodNetwork.Name,
        AllowGlobalAccess = true,
    });

    var a = new Gcp.Dns.RecordSet("a", new()
    {
        Name = prod.DnsName.Apply(dnsName => $"backend.{dnsName}"),
        ManagedZone = prod.Name,
        Type = "A",
        Ttl = 300,
        RoutingPolicy = new Gcp.Dns.Inputs.RecordSetRoutingPolicyArgs
        {
            PrimaryBackup = new Gcp.Dns.Inputs.RecordSetRoutingPolicyPrimaryBackupArgs
            {
                TrickleRatio = 0.1,
                Primary = new Gcp.Dns.Inputs.RecordSetRoutingPolicyPrimaryBackupPrimaryArgs
                {
                    InternalLoadBalancers = new[]
                    {
                        new Gcp.Dns.Inputs.RecordSetRoutingPolicyPrimaryBackupPrimaryInternalLoadBalancerArgs
                        {
                            LoadBalancerType = "regionalL4ilb",
                            IpAddress = prodForwardingRule.IpAddress,
                            Port = "80",
                            IpProtocol = "tcp",
                            NetworkUrl = prodNetwork.Id,
                            Project = prodForwardingRule.Project,
                            Region = prodForwardingRule.Region,
                        },
                    },
                },
                BackupGeos = new[]
                {
                    new Gcp.Dns.Inputs.RecordSetRoutingPolicyPrimaryBackupBackupGeoArgs
                    {
                        Location = "asia-east1",
                        Rrdatas = new[]
                        {
                            "10.128.1.1",
                        },
                    },
                    new Gcp.Dns.Inputs.RecordSetRoutingPolicyPrimaryBackupBackupGeoArgs
                    {
                        Location = "us-west1",
                        Rrdatas = new[]
                        {
                            "10.130.1.1",
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.dns.ManagedZone;
import com.pulumi.gcp.dns.ManagedZoneArgs;
import com.pulumi.gcp.compute.RegionBackendService;
import com.pulumi.gcp.compute.RegionBackendServiceArgs;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.ForwardingRule;
import com.pulumi.gcp.compute.ForwardingRuleArgs;
import com.pulumi.gcp.dns.RecordSet;
import com.pulumi.gcp.dns.RecordSetArgs;
import com.pulumi.gcp.dns.inputs.RecordSetRoutingPolicyArgs;
import com.pulumi.gcp.dns.inputs.RecordSetRoutingPolicyPrimaryBackupArgs;
import com.pulumi.gcp.dns.inputs.RecordSetRoutingPolicyPrimaryBackupPrimaryArgs;
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 prod = new ManagedZone("prod", ManagedZoneArgs.builder()
            .name("prod-zone")
            .dnsName("prod.mydomain.com.")
            .visibility("private")
            .build());

        var prodRegionBackendService = new RegionBackendService("prodRegionBackendService", RegionBackendServiceArgs.builder()
            .name("prod-backend")
            .region("us-central1")
            .build());

        var prodNetwork = new Network("prodNetwork", NetworkArgs.builder()
            .name("prod-network")
            .build());

        var prodForwardingRule = new ForwardingRule("prodForwardingRule", ForwardingRuleArgs.builder()
            .name("prod-ilb")
            .region("us-central1")
            .loadBalancingScheme("INTERNAL")
            .backendService(prodRegionBackendService.id())
            .allPorts(true)
            .network(prodNetwork.name())
            .allowGlobalAccess(true)
            .build());

        var a = new RecordSet("a", RecordSetArgs.builder()
            .name(prod.dnsName().applyValue(_dnsName -> String.format("backend.%s", _dnsName)))
            .managedZone(prod.name())
            .type("A")
            .ttl(300)
            .routingPolicy(RecordSetRoutingPolicyArgs.builder()
                .primaryBackup(RecordSetRoutingPolicyPrimaryBackupArgs.builder()
                    .trickleRatio(0.1)
                    .primary(RecordSetRoutingPolicyPrimaryBackupPrimaryArgs.builder()
                        .internalLoadBalancers(RecordSetRoutingPolicyPrimaryBackupPrimaryInternalLoadBalancerArgs.builder()
                            .loadBalancerType("regionalL4ilb")
                            .ipAddress(prodForwardingRule.ipAddress())
                            .port("80")
                            .ipProtocol("tcp")
                            .networkUrl(prodNetwork.id())
                            .project(prodForwardingRule.project())
                            .region(prodForwardingRule.region())
                            .build())
                        .build())
                    .backupGeos(                    
                        RecordSetRoutingPolicyPrimaryBackupBackupGeoArgs.builder()
                            .location("asia-east1")
                            .rrdatas("10.128.1.1")
                            .build(),
                        RecordSetRoutingPolicyPrimaryBackupBackupGeoArgs.builder()
                            .location("us-west1")
                            .rrdatas("10.130.1.1")
                            .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  a:
    type: gcp:dns:RecordSet
    properties:
      name: backend.${prod.dnsName}
      managedZone: ${prod.name}
      type: A
      ttl: 300
      routingPolicy:
        primaryBackup:
          trickleRatio: 0.1
          primary:
            internalLoadBalancers:
              - loadBalancerType: regionalL4ilb
                ipAddress: ${prodForwardingRule.ipAddress}
                port: '80'
                ipProtocol: tcp
                networkUrl: ${prodNetwork.id}
                project: ${prodForwardingRule.project}
                region: ${prodForwardingRule.region}
          backupGeos:
            - location: asia-east1
              rrdatas:
                - 10.128.1.1
            - location: us-west1
              rrdatas:
                - 10.130.1.1
  prod:
    type: gcp:dns:ManagedZone
    properties:
      name: prod-zone
      dnsName: prod.mydomain.com.
      visibility: private
  prodForwardingRule:
    type: gcp:compute:ForwardingRule
    name: prod
    properties:
      name: prod-ilb
      region: us-central1
      loadBalancingScheme: INTERNAL
      backendService: ${prodRegionBackendService.id}
      allPorts: true
      network: ${prodNetwork.name}
      allowGlobalAccess: true
  prodRegionBackendService:
    type: gcp:compute:RegionBackendService
    name: prod
    properties:
      name: prod-backend
      region: us-central1
  prodNetwork:
    type: gcp:compute:Network
    name: prod
    properties:
      name: prod-network

The primaryBackup configuration defines a primary target (here, an internal load balancer) and backup geographic endpoints. The trickleRatio controls what percentage of traffic goes to backups even when the primary is healthy, enabling gradual failover testing.

Beyond these examples

These snippets focus on specific record set features: standard record types (A, MX, CNAME, TXT), dynamic IP binding from compute resources, and geographic and failover routing policies. They’re intentionally minimal rather than full DNS configurations.

The examples may reference pre-existing infrastructure such as Cloud DNS managed zones, and compute instances, load balancers, and networks for dynamic binding. They focus on configuring the record set rather than provisioning the surrounding DNS infrastructure.

To keep things focused, common record set patterns are omitted, including:

  • SPF record quoting requirements (TXT records need escaped quotes)
  • Health check integration with routing policies
  • Weighted round-robin routing (WRR)
  • Public zone failover with external endpoints

These omissions are intentional: the goal is to illustrate how each record set feature is wired, not provide drop-in DNS modules. See the DNS RecordSet resource reference for all available configuration options.

Let's create GCP DNS Record Sets

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Record Configuration & Types
Why isn't my SPF record working correctly?
SPF records require quotes around the rrdatas value to prevent the string from being split on spaces. Wrap the entire SPF value in escaped quotes: ["\"v=spf1 ip4:111.111.111.111 include:example.com -all\""].
How many targets can I specify for a CNAME record?
CNAME records should contain only a single string in the rrdatas array corresponding to the canonical name. Multiple entries aren’t supported for CNAME records.
What DNS record types are supported?
The type property accepts standard DNS record types including A, AAAA, CNAME, MX, TXT, and others. Examples in the schema demonstrate A, MX, TXT (for SPF), and CNAME records.
Routing & Traffic Management
What's the difference between rrdatas and routingPolicy?
Use rrdatas for simple static DNS records with fixed IP addresses or values. Use routingPolicy for advanced traffic steering like geolocation-based routing, weighted round robin, or failover configurations.
How do I route traffic based on geographic location?
Configure routingPolicy.geos with an array of locations and their corresponding rrdatas. Each entry specifies a location (like asia-east1 or us-central1) and the IP addresses to return for queries from that region.
How do I set up DNS failover?
Use routingPolicy.primaryBackup with a primary target (either internalLoadBalancers or externalEndpoints) and backupGeos for failover locations. You can also specify a trickleRatio to gradually shift traffic.
Can I use health checks with routing policies?
Yes, specify a healthCheck ID in the routingPolicy to monitor endpoint health. The public zone failover example shows using a health check with primaryBackup routing and healthCheckedTargets in backup geos.
Resource Management
What properties can't be changed after creating a record set?
The managedZone, name, and project properties are immutable and cannot be changed after creation. You’ll need to delete and recreate the record set to modify these values.
Do I need to include a trailing dot in the DNS name?
Yes, when importing existing record sets, the name must include the trailing dot (e.g., example.com. not example.com). The examples also show names with trailing dots when using zone DNS names.

Using a different cloud?

Explore networking guides for other cloud providers: