Home / Networking

Cloud DNS Private Zones: Forwarding, Peering & Split-Horizon DNS | 2026

Cloud DNS Private Zones: Forwarding, Peering & Split-Horizon DNS | 2026

DNS is the backbone of cloud routing and security. In Google Cloud, Cloud DNS Private Zones allow you to manage internal domain names (e.g. `service.internal.company.com`) restricted to authorized VPC networks. Split-Horizon DNS lets internal workloads resolve corporate domain names to private IPs while public internet users resolve to public IPs. This guide demonstrates how to configure Private Managed Zones, set up DNS Peering across multi-tenant VPCs, and deploy Inbound/Outbound DNS Forwarding for hybrid on-premises connectivity in Terraform.

By Mateusz Chmielewski · Aug 29, 2026 · 15 min read

What is Cloud DNS Private Zone Architecture?

Cloud DNS Private Zones manage DNS record sets that are visible exclusively to designated Virtual Private Cloud (VPC) networks. Unlike public DNS zones, private zones do not publish records to public root servers and are inaccessible from the open internet.

Think of Public DNS like the public phone directory — anyone in the world can look up your company's front desk number. A Private DNS Zone is like an internal intercom directory posted inside the employee breakroom. Only staff inside the building (authorized VPCs) can see the extension numbers (private IP addresses) to call internal desks directly.

ConceptExplanationWhen to use
Private Managed ZoneA DNS zone authoritative for a specific domain name (e.g., `prod.gcp.internal`) bound to one or more VPC networks.Create for internal microservices, database clusters, and internal load balancers.
Split-Horizon DNSRunning matching public and private zones for the same domain name (`api.company.com`) to serve different IP answers based on query origin.Use when internal services need private IPs inside GCP but public IPs on the internet.
DNS Peering ZoneA DNS zone in a consumer VPC that delegates all name queries for a domain to a primary DNS zone in a producer VPC.Share a central DNS namespace across multi-tenant or Shared VPC environments.
Outbound DNS ForwardingConfigures Cloud DNS to forward queries for specific domain names (e.g., `corp.local`) to on-premises DNS servers via VPN/Interconnect.Required when GCP workloads need to resolve on-premises corporate hostname records.
Inbound DNS PolicyEnables entry-point internal IP addresses in your VPC that allow on-premises DNS servers to query GCP Cloud DNS zones.Required when on-premises workloads need to resolve GCP internal domain names.

Split-Horizon & Hybrid DNS Architectural Patterns

Hardcoding internal IP addresses in microservice configurations creates fragile deployments. Conversely, using public domain names for internal traffic routes requests over the internet, incurring NAT costs and exposing internal endpoints.

Cloud DNS Private Zones provide secure, automated name resolution inside GCP. Split-Horizon DNS ensures seamless developer experience: `api.company.com` resolves to internal IP `10.0.0.50` inside GCP, and external IP `35.200.x.x` outside GCP. Combine with [Hybrid Connectivity (Cloud VPN & Interconnect)](/tutorial/hybrid-connectivity-cloud-vpn-partner-dedicated-interconnect-guide) for on-prem DNS forwarding and [Private Service Connect](/tutorial/private-service-connect-google-apis-terraform).

FeaturethisServicealtAaltB
ScopePrivate Zone: Bound to authorized VPCsPublic Zone: Worldwide InternetDNS Peering: Delegated across VPCs
Query OriginInternal GCP VMs, GKE pods, Cloud RunPublic Internet ClientsConsumer VPC Workloads
Access ControlVPC Network IAM VisibilityPublicConsumer VPC Authorizations
Hybrid SupportSupports Inbound/Outbound ForwardingPublic resolution onlyVPC-to-VPC delegation
Cost per Month$0.20 / zone + $0.40/M queries$0.20 / zone + $0.40/M queries$0.20 / zone + $0.40/M queries

Prerequisites

  • GCP Project with Cloud DNS API (`dns.googleapis.com`) enabled
  • Terraform CLI v1.6+
  • At least one Custom Mode VPC Network
  • gcloud CLI v480.0+ configured

Step-by-Step Guide

Step 1: Provision Private Managed DNS Zone in Terraform

Create a private managed zone authoritative for `internal.company.com` and bind it to your production VPC network. Binding the private zone to your VPC network ensures that only resources within that VPC can resolve internal domain names.

# dns_private_zone.tf — Cloud DNS Private Zone
resource "google_dns_managed_zone" "private_internal" {
  name        = "prod-internal-zone"
  dns_name    = "internal.company.com."
  description = "Private internal DNS zone for production microservices"
  visibility  = "private"

  private_visibility_config {
    networks {
      network_url = google_compute_network.prod_vpc.id
    }
  }

  project = var.project_id
}

# A Record for Internal Database Service
resource "google_dns_record_set" "db_internal" {
  name         = "postgres.internal.company.com."
  managed_zone = google_dns_managed_zone.private_internal.name
  type         = "A"
  ttl          = 300
  rrdatas      = ["10.100.0.50"]
  project      = var.project_id
}

Step 2: Configure Split-Horizon DNS for Dual Internal/External Resolution

Create a private zone with the exact same name as your public domain (`api.company.com`) to implement Split-Horizon DNS. Split-Horizon DNS directs internal traffic to internal load balancers while public traffic routes to external Cloud Armor WAF endpoints without changing domain names.

# dns_split_horizon.tf — Split-Horizon Private Zone Override
resource "google_dns_managed_zone" "split_horizon_private" {
  name        = "split-horizon-company-com"
  dns_name    = "company.com."
  description = "Internal Private Override for company.com"
  visibility  = "private"

  private_visibility_config {
    networks {
      network_url = google_compute_network.prod_vpc.id
    }
  }

  project = var.project_id
}

# Internal A record for api.company.com (Points to Internal Load Balancer)
resource "google_dns_record_set" "api_internal" {
  name         = "api.company.com."
  managed_zone = google_dns_managed_zone.split_horizon_private.name
  type         = "A"
  ttl          = 300
  rrdatas      = ["10.100.0.100"] # Private Internal IP inside GCP
  project      = var.project_id
}

Step 3: Set Up Multi-VPC DNS Peering in Terraform

Delegate DNS name resolution from a consumer VPC network to a central producer VPC network using DNS Peering. DNS Peering allows multi-tenant spoke VPCs or dev networks to query a centralized hub DNS zone without duplicating DNS record definitions.

# dns_peering.tf — Cross-VPC DNS Peering Zone
resource "google_dns_managed_zone" "peering_zone" {
  name        = "spoke-to-hub-dns-peering"
  dns_name    = "shared.services.internal."
  description = "Peers spoke-vpc DNS queries to central hub-vpc"
  visibility  = "private"

  private_visibility_config {
    networks {
      network_url = google_compute_network.spoke_vpc.id # Consumer VPC
    }
  }

  peering_config {
    target_network {
      network_url = google_compute_network.hub_vpc.id # Producer/Hub VPC holding master records
    }
  }

  project = var.project_id
}

Step 4: Configure Outbound DNS Forwarding to On-Premises DNS Servers

Create an Outbound DNS Forwarding Zone in Terraform to route queries for `onprem.corp` to on-premises DNS server IPs via VPN/Interconnect. Enables GCP workloads to resolve on-premises Active Directory or BIND domain names transparently.

# dns_outbound_forwarding.tf — Forward queries to On-Premises DNS
resource "google_dns_managed_zone" "onprem_forwarding" {
  name        = "forward-to-onprem-corp"
  dns_name    = "onprem.corp."
  description = "Forwards onprem.corp queries to on-premises DNS servers"
  visibility  = "private"

  private_visibility_config {
    networks {
      network_url = google_compute_network.prod_vpc.id
    }
  }

  forwarding_config {
    target_name_servers {
      ipv4_address = "192.168.1.10" # Primary On-Premises DNS Server
    }
    target_name_servers {
      ipv4_address = "192.168.1.11" # Secondary On-Premises DNS Server
    }
  }

  project = var.project_id
}

Step 5: Enable Inbound DNS Entry Points for On-Premises Clients

Create an Inbound Server Policy in Terraform to generate internal IP entry points for on-premises clients to query GCP Cloud DNS. On-premises workloads require an internal GCP IP address to target when resolving `.gcp.internal` domain names.

# dns_inbound_policy.tf — Inbound DNS Policy for On-Premises Queries
resource "google_dns_policy" "inbound_dns_policy" {
  name                      = "inbound-dns-policy"
  enable_inbound_forwarding = true
  description               = "Enables Inbound DNS Entry Points for VPC"

  networks {
    network_url = google_compute_network.prod_vpc.id
  }

  project = var.project_id
}

Step 6: Test DNS Resolution and Query Paths

Verify private zone resolution, split-horizon responses, and forwarding targets using `dig` and `gcloud`. Testing confirms that DNS queries follow expected private pathways without falling back to public root servers.

# Step 1: Query internal private zone A record
dig postgres.internal.company.com @169.254.169.254

# Expected output:
# ;; ANSWER SECTION:
# postgres.internal.company.com. 300 IN A 10.100.0.50

# Step 2: Query split-horizon override record
dig api.company.com @169.254.169.254

# Expected output: 10.100.0.100 (Internal IP!)

# Step 3: Discover Inbound DNS Entry Point IPs for On-Premises configuration
gcloud compute addresses list \
  --filter="purpose=DNS_RESPONSE" \
  --project=PROJECT_ID \
  --format="table(name,address,subnetwork,region)"

Verification & Health Check

Best Practices

  • Create Subdomain Private Zones for Split-Horizon to Avoid Root Shadowing
  • Always Specify Trailing Dots in FQDN Names

Common Mistakes

  • {"errorCode":"DNS_QUERY_TIMEOUT_ONPREM","symptoms":"Outbound DNS forwarding queries to on-premises IPs time out.","rootCause":"Firewall rules on-premises or in GCP VPC block UDP/TCP port 53, or Cloud Router is not advertising the GCP subnet IP range to on-premises.","fixCommand":"gcloud compute firewall-rules create allow-dns-outbound --allow=udp:53,tcp:53 --destination-ranges=192.168.1.0/24","code":"resource \"google_compute_firewall\" \"allow_dns\" {\n name = \"allow-dns-outbound\"\n network = google_compute_network.prod_vpc.id\n allow {\n protocol = \"udp\"\n ports = [\"53\"]\n }\n allow {\n protocol = \"tcp\"\n ports = [\"53\"]\n }\n destination_ranges = [\"192.168.1.0/24\"]\n}\n","language":"hcl","filename":"fix_dns_fw.tf","prevention":"Verify port 53 bi-directional firewall rules during hybrid DNS setup."}

Cost Analysis

Featuremetriccost1kcost10kcost100kcost1m
Private Managed Zones (5 zones × $0.20 / month)$1.00/mo$1.00/mo$1.00/mo$12.00/yr
DNS Query Volume (10 Million × $0.40 / Million)$4.00/mo$4.00/mo$4.00/mo$48.00/yr
Total Cloud DNS Cost~$5.00/mo~$5.00/mo~$5.00/mo~$60.00/yr

References

Browse all tutorials