DMARC Engine
Home/Blog/How to add DMARC, SPF and DKIM records on Google Cloud DNS
Blog

How to add DMARC, SPF and DKIM records on Google Cloud DNS

A precise, Google Cloud DNS-specific walkthrough for publishing TXT and CNAME records for DMARC, SPF and DKIM, including the gotchas that silently break authentication: public versus private managed zones, console versus gcloud TXT quoting, trailing dots on CNAME targets, 255-byte chunking and a TTL strategy for a safe staged rollout to p=reject.

13 June 2026 · 21 min read

How to add DMARC, SPF and DKIM records on Google Cloud DNS

Google Cloud DNS is built for engineers running infrastructure on Google Cloud Platform, not for someone who just wants to paste a DMARC string into a box. It uses its own vocabulary (managed zones, resource record sets, the gcloud CLI), it has firm and slightly unusual rules about how TXT values are quoted, and it presents fully qualified DNS names with a trailing dot that catches people out the first time. Get those details wrong and your SPF, DKIM or DMARC will silently fail validation even though the record looks present in the console.

This guide is specific to Google Cloud DNS, the authoritative DNS service inside Google Cloud Platform. It is not the same thing as the DNS that Google Workspace asks you to edit, and it is not the Google Domains registrar (which Google has wound down and migrated to Squarespace). If you bought your domain through Google Domains, your records now live wherever that migration put them, not necessarily here. Everything below assumes you are sitting in front of a Cloud DNS managed zone in the Google Cloud console or driving it with gcloud. If you need the underlying theory first, what is DMARC, what is SPF and what is DKIM are the companions; come back here to publish.

Before you touch Cloud DNS: confirm this is the authoritative zone

The single most common reason a record published in Cloud DNS does nothing is that Cloud DNS is not actually authoritative for the domain. Creating a managed zone is not the same as the world resolving through it.

Cloud DNS gives every public managed zone a set of four assigned name servers, something like:

ns-cloud-a1.googledomains.com.
ns-cloud-a2.googledomains.com.
ns-cloud-a3.googledomains.com.
ns-cloud-a4.googledomains.com.

Those ns-cloud-*.googledomains.com name servers are Cloud DNS infrastructure. Despite the googledomains.com suffix, they have nothing to do with the old Google Domains registrar product; do not let the name confuse you. Your domain only resolves through this managed zone if your registrar's name servers are set to these exact four values. If you registered the domain elsewhere (Squarespace, GoDaddy, Namecheap, Cloudflare and so on) but created a Cloud DNS managed zone, your records will not take effect until you point the registrar's delegation at Cloud DNS.

A second, sneakier trap is specific to Google Cloud: you can have a public managed zone and a private managed zone for the same domain name in the same project. Records in a private zone are only served to VMs inside your VPC, never to the public internet. If you accidentally add your DMARC record to a private zone, the console shows it sitting there happily while every receiver on earth gets NXDOMAIN. Email authentication records must go in a public managed zone.

Confirm what is actually authoritative before you spend an afternoon editing records nobody queries:

  • In the Cloud DNS console, open the managed zone and note the four NS values in the auto-created NS record set.
  • Confirm the zone's Visibility is Public, not Private.
  • Check those four name servers match what your registrar has set.
  • Run the live lookup with the DNS record checker and the TXT record checker to see what the public internet currently returns, not what the console claims it should.

If the live answers do not match your zone, fix delegation first. There is a longer treatment of getting delegation right in delegating your DNS and publishing DNS records across providers. Nothing else in this guide matters until queries actually reach Cloud DNS.

The Cloud DNS record editor, in plain terms

In the console, open your public managed zone and click Add standard (or Add record set). Cloud DNS gives you a compact form. The fields that matter for email authentication:

  • DNS name. This is the host part of the record, and Cloud DNS shows the zone suffix already filled in to the right of the input box. You type only the prefix into the editable field. For the apex (root), you leave the prefix empty so the name is just your domain. For DMARC you type _dmarc. The console assembles and previews the full name as you go, which is your safety check.
  • Resource record type. TXT for SPF and DMARC, CNAME for most DKIM (and for any provider that hands you CNAMEs).
  • TTL and TTL unit. How long resolvers may cache the answer. Cloud DNS defaults to 300 seconds for the quick path but lets you set any value and choose the unit (seconds, minutes, hours). Covered in detail below.
  • Routing policy. Leave this on the default (no special routing policy). Geolocation, weighted round robin and failover policies are for traffic management and have no place in SPF, DKIM or DMARC.
  • TXT data / Canonical name. The record contents. Cloud DNS quoting rules for TXT are the part that bites; see the next section.

One Cloud DNS concept to internalise now: the unit you create is a resource record set, which is one name plus one type holding one or more values. You can add several values to a single TXT record set by clicking Add item. This matters enormously for SPF, where having two separate SPF record sets is an error, but two strings inside one record set is fine, and for chunked DKIM keys.

How Cloud DNS quotes TXT values (the part that bites)

Cloud DNS is opinionated about quoting, and the behaviour differs between the console and the gcloud CLI. Understand this before you paste anything.

In the console, when you add a TXT record set, you should wrap the whole value in double quotes yourself. Cloud DNS treats a quoted span as a single DNS character-string. If your value has no spaces and you forget the quotes, the console will usually accept it; but the moment your value contains spaces (and an SPF or DMARC string is full of them), unquoted input is ambiguous and Cloud DNS may split it on the spaces into several separate strings. That is not what you want for SPF or DMARC, where the whole policy must be one logical string. So: always quote TXT values in the console.

"v=spf1 include:_spf.google.com -all"

In gcloud, the quoting is doubled because your shell strips one layer. A working command looks like this:

gcloud dns record-sets create example.com. \
  --zone=my-zone \
  --type=TXT \
  --ttl=3600 \
  --rrdatas='"v=spf1 include:_spf.google.com -all"'

Note the single quotes wrapping the double quotes. The shell consumes the single quotes; Cloud DNS stores the double-quoted string. If you pass only "v=spf1 ... -all", your shell eats the double quotes and Cloud DNS receives an unquoted, space-split mess. This single detail is responsible for a large share of broken SPF and DMARC records created from the command line.

A practical, less error-prone habit when scripting is record-sets import from a BIND-format zone file, where the quoting matches what you would write in a normal zone file. But for one or two records, the console with explicit double quotes is the simplest reliable path.

Adding the SPF record (TXT) in Cloud DNS

SPF lives in a TXT record at the root (apex) of your domain. There is no dedicated SPF record type any more; the historical SPF type was deprecated, so use TXT.

  1. In the managed zone, click Add standard.
  2. Leave the DNS name prefix empty so the record set is created at the apex. The console preview should read your bare domain, for example example.com. with its trailing dot.
  3. Set Resource record type to TXT.
  4. In TXT data, paste your SPF string wrapped in double quotes:
"v=spf1 include:_spf.google.com include:amazonses.com -all"
  1. Set TTL to 3600 seconds (one hour) for now.
  2. Leave routing policy on the default and click Create.

A few Cloud DNS-specific points that catch people out:

  • Quote it. As covered above, wrap the value in double quotes in the console, and double-wrap ('"..."') in gcloud. Unquoted multi-word SPF is the classic Cloud DNS mistake.
  • One SPF record set only. You must have exactly one TXT record set at the apex whose value begins v=spf1. If a verification token already lives at the root as a separate TXT, that is fine: keep tokens and the SPF string as separate values inside the same apex TXT record set, or as separate TXT record sets, but never create a second value that also starts with v=spf1. Two v=spf1 strings resolve to a permerror and SPF fails outright. There is more on this failure mode in common DMARC mistakes.
  • Stay under ten lookups. Every include, a, mx, ptr and exists mechanism costs a DNS lookup at evaluation time, and SPF caps the total at ten. Cloud DNS will happily store a record that blows past that limit; it does not validate SPF semantics at all. If you have stacked up several senders (Google Workspace plus a CRM plus a help desk plus a marketing tool), verify with the SPF checker, and read SPF PermError: too many DNS lookups. If you are over, our hosted SPF flattening keeps the published record flat and under the limit automatically as your senders change.

If you would rather assemble the string from a list of senders than hand-write it, the SPF generator produces a single valid record you can paste straight into the TXT data box. The mechanics of flattening are in how SPF flattening works.

Long SPF records and TXT chunking in Cloud DNS

A single TXT character-string cannot exceed 255 bytes. SPF records can grow past that once you have several includes. Cloud DNS handles the overflow by letting one record set hold multiple quoted strings, which DNS concatenates back together at query time with no separator.

In the console you express this either by putting two quoted strings together in the TXT data field, or by clicking Add item for a second value:

"v=spf1 include:_spf.google.com include:servers.mcsv.net include:amazonses.com " "include:spf.protection.outlook.com include:sendgrid.net -all"

Note the trailing space inside the first string, before its closing quote. Because DNS joins the chunks with no delimiter, you must make sure the join does not glue two mechanisms together (...amazonses.cominclude:spf...). Put the boundary on a natural space. This is exactly the kind of thing that is easy to get wrong by hand; the DNS record splitter chunks a long value into correctly quoted 255-character segments for you. Honestly, though, the better answer for a record this large is to flatten it so it never needs splitting in the first place.

Adding DKIM in Cloud DNS

DKIM is where the Cloud DNS form trips people most, because almost every sending platform gives you DKIM as CNAME records pointing at the provider, and the host names contain dots and underscores that look alarming in the prefix field.

DKIM records live at a selector under the special _domainkey label:

selector._domainkey.example.com

The selector is chosen by your sending platform. Different providers use different forms: Google Workspace uses google, Amazon SES gives you three long random selectors, Mailchimp uses k2 or k3, Microsoft 365 uses selector1 and selector2.

DKIM as CNAME (the common case)

Most modern providers, including Amazon SES, Microsoft 365 and Mailchimp, give you CNAMEs so they can rotate the underlying key without you editing DNS again. Adding one in Cloud DNS:

  1. Click Add standard.
  2. In the DNS name prefix field, type only the host portion that precedes your domain. If the provider tells you the full name is selector1._domainkey.example.com, you type selector1._domainkey into the prefix box and let Cloud DNS append the zone suffix it shows beside the field. Do not type the whole thing including example.com. Cloud DNS appends the zone name automatically, and typing it twice produces selector1._domainkey.example.com.example.com., a dead record. Watch the full-name preview.
  3. Set Resource record type to CNAME.
  4. In Canonical name, paste the target the provider gave you, exactly, and end it with a trailing dot. For Microsoft 365 that is something like:
selector1-example-com._domainkey.example.onmicrosoft.com.

The trailing dot is the Cloud DNS gotcha here. A CNAME target without the final dot is treated as relative to your zone, so dkim.amazonses.com (no dot) silently becomes dkim.amazonses.com.example.com., which does not resolve. Cloud DNS frequently appends the trailing dot for you in the console, but you should type it and confirm it is present. No quotes around CNAME values.
5. Set TTL to 3600 seconds.
6. Click Create.

An Amazon SES domain typically needs three of these CNAMEs:

abcdefg1234567._domainkey   CNAME   abcdefg1234567.dkim.amazonses.com.
hijklmn8901234._domainkey   CNAME   hijklmn8901234.dkim.amazonses.com.
opqrstu5678901._domainkey   CNAME   opqrstu5678901.dkim.amazonses.com.

The Cloud DNS-specific DKIM CNAME gotchas:

  • Trailing dots on the target. As above, a missing final dot makes the target relative to your zone and breaks resolution. This is the number-one Cloud DNS DKIM failure.
  • CNAME and other records cannot coexist on the same name. DNS forbids a CNAME alongside any other record type at the same name. Cloud DNS enforces this and will reject the second record set with an error. For DKIM this is rarely an issue because each selector name is unique, but if a provider ever asks you to publish both a CNAME and a TXT at the same selector, something is wrong with their instructions.
  • No proxy or alias concept to worry about. Unlike some hosts, Cloud DNS does not have a proxy toggle or an apex-alias feature that can quietly change what a CNAME returns. A CNAME record set in Cloud DNS resolves as a plain CNAME, which is exactly what DKIM needs.

Verify each selector resolves with the DKIM checker once published, and see DKIM selectors explained for how the selector mechanism works. For Google Workspace and Microsoft specifically, DMARC for Google Workspace and DMARC for Microsoft 365 cover the provider-side steps that pair with these records.

DKIM as TXT (the public-key case)

Some providers, and any setup where you generate the key yourself, give you the DKIM public key to publish directly as a TXT record at the selector. A 2048-bit key runs well over 255 characters, so the same chunking rule as SPF applies.

  1. Add standard, DNS name prefix selector._domainkey (host portion only).
  2. Type TXT.
  3. In TXT data, the public key, quoted, split into 255-character chunks across multiple quoted strings:
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1234" "567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ...IDAQAB"

The boundary between chunks can fall anywhere inside the base64 p= value because it is concatenated with no separator, but each piece must be 255 characters or fewer. Cloud DNS will reject a single TXT string longer than 255 characters with an error, which is your cue to chunk. The DNS record splitter does this correctly; a 2048-bit key is the textbook case for it. For the trade-offs between 1024-bit and 2048-bit keys, see DKIM key length, and DKIM setup for the wider workflow.

Adding the DMARC record (TXT) in Cloud DNS

DMARC is a TXT record published at the fixed host _dmarc. It is the policy that tells receivers what to do with mail that fails SPF and DKIM alignment, and where to send you the aggregate reports.

  1. Click Add standard.
  2. In the DNS name prefix field, type _dmarc (just that; Cloud DNS makes the full name _dmarc.example.com.).
  3. Set Resource record type to TXT.
  4. In TXT data, paste your DMARC policy, quoted. Start in monitoring mode:
"v=DMARC1; p=none; rua=mailto:dmarc@example.com; ruf=mailto:dmarc@example.com; fo=1; adkim=s; aspf=s"
  1. Set TTL to 3600 seconds.
  2. Click Create.

Cloud DNS-specific and DMARC-specific points:

  • _dmarc is a host name, not the apex. A surprising number of people paste the DMARC string into the root TXT record alongside SPF. That does nothing: receivers look up _dmarc.example.com specifically. SPF goes at the apex (empty prefix), DMARC goes at the _dmarc prefix. They are different record sets. There is a deeper walk-through in understanding your DMARC record.
  • The reporting address can be on another domain, but then there is an extra step. If rua points at a mailbox on a domain you do not control (a third-party monitoring service, for example), that other domain must publish a _report._dmarc authorisation record so it will accept your reports. The symptom people hit is reports silently never arriving. The DMARC checker flags external-destination problems, and DMARC RUA and RUF reports explains the addressing.
  • One DMARC record set only. As with SPF, two values beginning v=DMARC1 at _dmarc is invalid and many receivers will ignore the policy entirely.
  • Generate it rather than guessing the tags. The combination of p, sp, pct, adkim, aspf, fo, rua and ruf has real consequences. The DMARC generator builds a syntactically correct record from plain-English choices.

Confirm the published policy with the DMARC checker before moving on, and beware the comfortable trap described in p=none is false security: a record at p=none monitors but enforces nothing.

TTL strategy in Cloud DNS for a safe rollout

TTL (time to live) is the number of seconds a resolver may cache your record. Cloud DNS lets you set it per record set, with a unit selector, and it matters more for email authentication than people expect, because the whole point of DMARC is a staged rollout where you change the policy several times.

The logic is simple. A high TTL means that when you change a record, the old value lingers in caches around the world for up to that many seconds. A low TTL means changes propagate quickly but resolvers query Cloud DNS more often (a non-issue at Cloud DNS's scale and pricing).

Practical advice for Cloud DNS:

  • While you are actively changing things, keep authentication records at 300 seconds (five minutes). This includes the run-up to a DMARC policy change, when you want a mistake reversible in minutes, not hours.
  • Lower the TTL first, then wait, then change the value. If your DMARC record is at TTL 3600 and you want to move from p=none to p=quarantine, first edit the record set to drop the TTL to 300 and wait at least the old TTL (one hour) so existing caches expire. Only then change the policy. Otherwise the old p=none can persist in caches for up to an hour after you think you have enforced.
  • Once a record is stable (DKIM CNAMEs that will not change, a settled SPF record), you can raise TTL to 3600 or higher to reduce query volume. There is no deliverability benefit to a high TTL; it is purely about caching.
  • Watch the change-set model. Each edit in Cloud DNS is applied as an atomic change set (you can see them in the zone's change history). That history records that a change happened, but it is not a one-click rollback of the previous value, so copy the old value into a note before you edit a live SPF or DKIM record.

This TTL discipline is the mechanical half of the real goal, which is reaching enforcement without breaking mail. Confirm changes have actually propagated with the DNS propagation checker rather than trusting the console's success message.

The staged path to enforcement, Cloud DNS edition

Publishing the records is the easy part. The reason DMARC projects stall is the move from p=none to p=reject, where a forgotten sender suddenly gets its mail quarantined or rejected. Cloud DNS is just the editor here; the method is the same everywhere, and it is documented in the enforcement journey.

  1. Publish p=none and collect reports. With the DMARC record above live, receivers start sending you aggregate (RUA) reports listing every source sending as your domain and whether it passed aligned SPF or DKIM. Leave this running for two to four weeks. If nothing arrives, why are my DMARC reports empty covers the usual causes.
  2. Read the reports and fix alignment. The reports reveal legitimate senders that are not yet aligned (a SaaS tool you forgot, a regional office, a billing platform). For each, you go back into Cloud DNS and add the missing SPF include or DKIM CNAME. Raw XML is hard to read by hand; the DMARC report analyzer turns it into a sender-by-sender view, and reading your first DMARC report walks through it. If you are unsure whether SPF or DKIM is carrying a given sender, SPF vs DKIM: which aligns and DMARC alignment explained clear it up.
  3. Move to p=quarantine, optionally with pct. Once every legitimate sender is aligned, edit the _dmarc TXT in Cloud DNS to p=quarantine. You can ramp with pct=25 and increase, although sampling has caveats covered in pct and sampling.
  4. Move to p=reject. When quarantine has been clean for a couple of weeks, change the policy to p=reject. This is the state Gmail, Yahoo and Microsoft increasingly expect from bulk senders; see Gmail, Yahoo and Microsoft DMARC requirements and the wider sender requirements.

Two enforcement subtleties worth flagging:

  • Subdomains. DMARC at the apex covers subdomains through the inherited sp policy. If you send from mail.example.com or news.example.com, decide explicitly with the sp tag rather than letting it default. Subdomain policy with sp and the subdomain DMARC record cover the gotchas.
  • Forwarding breaks SPF, not DKIM. Mailing lists and forwarders rewrite the path and break SPF alignment, which is exactly why you want DKIM solidly in place: it survives forwarding. DKIM, alignment and forwarding and the DMARC forwarding problem explain why this is your safety net at enforcement.

For the full method end to end, reach p=reject without breaking email and moving from p=none to reject safely are the companions to this DNS-editing guide. If you are nervous about breakage, will enforcing DMARC break my email is worth reading before you tighten.

Doing it at scale: gcloud and Terraform

If you manage many domains, the console is not the only way, and Cloud DNS is genuinely good as code.

  • gcloud. Use gcloud dns record-sets create (or a transaction with record-sets transaction start/add/execute) to add records. Remember the double-quoting rule for TXT: --rrdatas='"v=DMARC1; p=none; rua=mailto:dmarc@example.com"', with single quotes around the double quotes so the shell does not strip them. For CNAMEs, give the target with a trailing dot: --rrdatas='dkim.amazonses.com.'.
  • Terraform. The google_dns_record_set resource declares SPF, DMARC and DKIM as code. In Terraform the rrdatas list values keep their literal double quotes for TXT (rrdatas = ["\"v=spf1 include:_spf.google.com -all\""]), and CNAME targets keep the trailing dot. This is the cleanest way to keep dozens of zones consistent and to code-review changes to authentication.

Whichever route you use, the rules from this guide do not change: one SPF TXT at the apex, one DMARC TXT at _dmarc, DKIM per selector, trailing dots on CNAME targets, double-quoted TXT values, and long DKIM keys split into 255-character chunks.

Cloud DNS mistakes that silently break authentication

A consolidated checklist of the failures that are specific to, or especially common in, Cloud DNS:

  • Editing a private zone, or a non-authoritative zone. A private managed zone serves only your VPC; a public zone only matters if the registrar's name servers point at it. Check both with the DNS record checker.
  • Forgetting the trailing dot on a CNAME target. dkim.amazonses.com without the final dot becomes dkim.amazonses.com.example.com. and dies. Always end CNAME targets with a dot.
  • Doubling the domain name on host fields. Typing selector._domainkey.example.com in the DNS name prefix creates ...example.com.example.com.. Use the host portion only and watch the preview.
  • Unquoted multi-word TXT. SPF and DMARC values contain spaces; without double quotes Cloud DNS may split them on the spaces. Always quote in the console, and double-quote in gcloud.
  • Putting DMARC at the apex. DMARC must be at _dmarc, not in the root TXT next to SPF.
  • Two v=spf1 or two v=DMARC1 record sets. One of each, full stop. Multiple values inside a single record set are fine; multiple record sets that each start the policy are not.
  • An unchunked long TXT. Strings over 255 bytes must be split into multiple quoted segments, with the boundary on a safe space (SPF) or anywhere inside the base64 (DKIM key).
  • A high TTL during a policy change. Lower TTL to 300, wait out the old TTL, then change the value.

After every change, do not trust the console; verify against live DNS. The DMARC checker, SPF checker and DKIM checker read what the world actually resolves, which is the only thing receivers act on. If you also run MTA-STS or BIMI, the MTA-STS checker and BIMI checker cover those, with setup in MTA-STS setup.

The practical takeaway

Google Cloud DNS is a perfectly good home for SPF, DKIM and DMARC once you respect its handful of habits: publish to a public managed zone that the registrar actually delegates to, leave the DNS name prefix empty for the apex and use only the host portion for everything else, double-quote your TXT values and double-wrap them in gcloud, and put a trailing dot on every CNAME target. The records themselves are the same standards-based strings you would publish anywhere; Cloud DNS just has engineer-oriented plumbing around them. Publish at p=none, read the reports, align every sender, and only then tighten to quarantine and reject, lowering your TTLs before each policy change so a mistake is always minutes from reversible.

If you would rather not hand-edit chunked TXT records, chase trailing dots and babysit a staged rollout, that is exactly what we do for you. Our done-for-you DMARC service manages the records on your behalf, hosted SPF flattening keeps you under the ten-lookup limit as senders change, hosted DKIM handles key rotation, and our emailed monitoring tells you the moment a record drifts or a new sender appears, so a clean Cloud DNS zone stays clean. Start by running your domain through the DMARC checker to see exactly where you stand today.

Share

See where your domain stands today

Run a free DMARC scan, then let us take you to enforced p=reject with no email outage.