
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.
| Concept | Explanation | When to use |
|---|---|---|
| Private Managed Zone | A 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 DNS | Running 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 Zone | A 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 Forwarding | Configures 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 Policy | Enables 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).
| Feature | thisService | altA | altB |
|---|---|---|---|
| Scope | Private Zone: Bound to authorized VPCs | Public Zone: Worldwide Internet | DNS Peering: Delegated across VPCs |
| Query Origin | Internal GCP VMs, GKE pods, Cloud Run | Public Internet Clients | Consumer VPC Workloads |
| Access Control | VPC Network IAM Visibility | Public | Consumer VPC Authorizations |
| Hybrid Support | Supports Inbound/Outbound Forwarding | Public resolution only | VPC-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
| Feature | metric | cost1k | cost10k | cost100k | cost1m |
|---|---|---|---|---|---|
| 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 |