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: their names, types, data values, and optional routing policies. This guide focuses on three capabilities: standard record types (A, MX, TXT, CNAME), geographic routing, and failover configurations.

Record sets belong to managed zones and may reference compute instances, load balancers, or networks for 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 by mapping hostnames to IP addresses using A records.

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 type property specifies the record type. The rrdatas array contains the IP addresses to return. The ttl property controls how long resolvers cache the 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.

Each entry in rrdatas combines a priority number with a mail server hostname. Mail clients attempt delivery to lower-priority servers first, falling back to higher-priority servers if delivery fails. The ttl of 3600 seconds (1 hour) is common for MX records.

Add SPF records for email authentication

SPF records authenticate email senders by listing authorized IP addresses and domains in a TXT record.

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 spf = new gcp.dns.RecordSet("spf", {
    name: pulumi.interpolate`frontend.${prod.dnsName}`,
    managedZone: prod.name,
    type: "TXT",
    ttl: 300,
    rrdatas: ["\"v=spf1 ip4:111.111.111.111 include:backoff.email-example.com -all\""],
});
import pulumi
import pulumi_gcp as gcp

prod = gcp.dns.ManagedZone("prod",
    name="prod-zone",
    dns_name="prod.mydomain.com.")
spf = gcp.dns.RecordSet("spf",
    name=prod.dns_name.apply(lambda dns_name: f"frontend.{dns_name}"),
    managed_zone=prod.name,
    type="TXT",
    ttl=300,
    rrdatas=["\"v=spf1 ip4:111.111.111.111 include:backoff.email-example.com -all\""])
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, "spf", &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("TXT"),
			Ttl:         pulumi.Int(300),
			Rrdatas: pulumi.StringArray{
				pulumi.String("\"v=spf1 ip4:111.111.111.111 include:backoff.email-example.com -all\""),
			},
		})
		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 spf = new Gcp.Dns.RecordSet("spf", new()
    {
        Name = prod.DnsName.Apply(dnsName => $"frontend.{dnsName}"),
        ManagedZone = prod.Name,
        Type = "TXT",
        Ttl = 300,
        Rrdatas = new[]
        {
            "\"v=spf1 ip4:111.111.111.111 include:backoff.email-example.com -all\"",
        },
    });

});
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 spf = new RecordSet("spf", RecordSetArgs.builder()
            .name(prod.dnsName().applyValue(_dnsName -> String.format("frontend.%s", _dnsName)))
            .managedZone(prod.name())
            .type("TXT")
            .ttl(300)
            .rrdatas("\"v=spf1 ip4:111.111.111.111 include:backoff.email-example.com -all\"")
            .build());

    }
}
resources:
  spf:
    type: gcp:dns:RecordSet
    properties:
      name: frontend.${prod.dnsName}
      managedZone: ${prod.name}
      type: TXT
      ttl: 300
      rrdatas:
        - '"v=spf1 ip4:111.111.111.111 include:backoff.email-example.com -all"'
  prod:
    type: gcp:dns:ManagedZone
    properties:
      name: prod-zone
      dnsName: prod.mydomain.com.

The type is TXT, not SPF. The rrdatas value must be wrapped in escaped quotes (\") to prevent Cloud DNS from splitting the string on spaces. The SPF syntax (v=spf1) defines which IP addresses and domains can send email on behalf of your domain.

Create aliases with CNAME records

CNAME records create aliases that point one hostname to another, enabling flexible DNS configurations without duplicating IP addresses.

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.

The rrdatas array should contain exactly one string: the canonical name to which this alias points. CNAME records cannot coexist with other record types for the same name.

Route traffic by geographic location

Applications serving global users often route traffic to regional endpoints based on client 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 (
	"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 routing. The geos array maps geographic locations to IP addresses. Cloud DNS returns the IP address matching the client’s location. When you use routingPolicy, you cannot set rrdatas.

Configure failover with primary and backup targets

High-availability deployments use failover routing to automatically redirect traffic when primary endpoints 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 targets. The trickleRatio controls what percentage of traffic goes to backups even when the primary is healthy (useful for testing). The internalLoadBalancers block references a forwarding rule by IP address, port, and network. When the primary fails, all traffic shifts to the backup geos.

Beyond these examples

These snippets focus on specific record set features: standard record types (A, MX, TXT, CNAME) 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 routing examples. They focus on configuring the record set rather than provisioning everything around it.

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

  • Weighted round-robin routing (WRR)
  • Health check configuration for routing policies
  • AAAA records for IPv6 addresses
  • NS and SOA record management (handled automatically by Cloud DNS)

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 Management & Behavior
Why are my existing DNS records being replaced when I create a record set?
This resource is authoritative, meaning it overwrites all existing records of the same type (including default records) when created. Plan accordingly to avoid unintended data loss.
Why didn't my NS or SOA records get deleted when I destroyed the record set?
Google Cloud DNS requires NS and SOA records at all times. Pulumi reports their removal but they remain in the zone; this is expected behavior.
What properties can't be changed after creation?
The managedZone, name, and project properties are immutable. Changing them requires destroying and recreating the record set.
Record Formatting & Data
How do I format TXT or SPF records with spaces or long values?
For records with spaces, add surrounding quotes: ["\"v=spf1 ip4:111.111.111.111 -all\""]. For values longer than 255 characters (like DKIM), use \" \" as a separator: ["first255characters\" \"morecharacters"].
Why can I only add one value to my CNAME record?
CNAME records must contain a single string corresponding to the canonical name. Use a single-element array: rrdatas: ["frontend.mydomain.com."].
How do I add multiple MX records with different priorities?
Provide multiple strings in the rrdatas array, each prefixed with its priority: ["1 aspmx.l.google.com.", "5 alt1.aspmx.l.google.com."].
Routing Policies
Can I use both rrdatas and routingPolicy?
No, use either rrdatas for simple records or routingPolicy for traffic steering (geolocation or failover). They’re mutually exclusive.
How do I configure geolocation-based routing?
Set routingPolicy.geos with a location and rrdatas for each region: geos: [{ location: "asia-east1", rrdatas: ["10.128.1.1"] }].
How do I set up failover routing with health checks?
Use routingPolicy.primaryBackup with primary and backupGeos. For public zones, add a healthCheck to monitor primary availability.
Import & Configuration
Why is my import failing with an invalid name error?
Record names must include the trailing dot for import: example.com. not example.com. Use format {{zone}}/{{name}}/{{type}} with the dot.

Using a different cloud?

Explore networking guides for other cloud providers: