Reconciler Provider API / Reconciliation

Duplicate record write rejected by provider API

A script tries to add a DNS record, and the provider API says no: the record already exists. This is not a bug in the API. It is a script that never checked what was already in the zone before it tried to write. Here is why Cloudflare rejects the call, and a small reconciler that lists first, then only creates or updates what actually needs to change.

Python and Node.js Cloudflare DNS API Safe by default (dry run)
A close up of a network with wires connected to it
Photo by Albert Stoynov on Unsplash
The short answer

A script or automation sent a create call to add a DNS record without first checking whether one already existed at that exact name and type. Cloudflare's API rejects the write with error 81057, "The record already exists," for a same-type clash, or error 81053, "An A, AAAA, or CNAME record with that host already exists," when a CNAME conflicts with any other record type at the same name. The fix is to change the automation from always creating to listing first, then creating only when nothing is there, or updating in place when something already is. Full code, tests, and a dry run guard are below.

The problem in plain words

DNS is simple about one thing: a script that wants to add a record has to check first whether that record is already there. Most automation does not. It reads a desired state, a name and a type and a value, and just fires a create call every time it runs. The first run works. The second run, or the run after someone else already made the record by hand, hits a wall.

Cloudflare will not silently accept a second copy of the same record, and it will not let a CNAME sit next to any other record type at the same name. So the create call comes back as a failure, the automation stops or logs an error, and whoever is watching the pipeline has to go find out why "adding a DNS record" broke.

Script runs desired: A app.example.com POST create no check first record already exists in zone HTTP 400 success: false code 81057 code 81053
The script never asked what was already in the zone. Cloudflare refuses the duplicate create and hands back an error code instead of a new record.

Why it happens

The key insight

Cloudflare's API is not being difficult. It is enforcing a rule that DNS has always had: one name cannot hold two conflicting answers of the same kind, and a CNAME cannot share a name with anything else at all. The fix is not to retry the same create call, it is to stop treating every write as a create. List what is there first, then create only when it is missing, or update in place when it is already there but wrong.

The fix, as a flow

Replace the blind create with a small reconciliation step. Before writing anything, ask the API what already exists at that exact name and type. If nothing comes back, it is safe to create. If one record comes back and it already matches what you want, there is nothing to do. If one record comes back but the content, TTL, or proxied flag is different, update that record in place with a PATCH instead of creating a second one. If the conflict is a CNAME blocking an A or AAAA record (or the reverse), decide which one should own that name and delete the other first.

List records at name.exact + type Any record found? no Create (POST) nothing to clash with yes, one exists Matches desired? yes No-op, skip already correct no, differs Update (PATCH) only the changed fields Zone now matches
Listing before writing turns every run into a safe no-op, a safe create, or a targeted update, and never a duplicate.

How to fix it

1

Check what is already at that exact name and type

Before writing anything, ask Cloudflare what already exists at the exact name and type you are about to write. A non-empty result means a blind create will fail. This one call is the entire fix for the root cause: it turns a guess into a fact.

Terminal
list-before-write.sh
curl -s "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name.exact=app.example.com" \
  -H "Authorization: Bearer $CF_TOKEN" \
  | jq '.result[] | {id, name, type, content}'

# a non-empty result array means the record already exists
# and a blind POST will fail
2

Reproduce the actual failure

See the error for yourself. POST the same record twice: the first call succeeds, the second returns HTTP 400 with the exact error code that names the conflict.

Terminal
reproduce-81057.sh
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
  -H "Authorization: Bearer $CF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"type":"A","name":"app.example.com","content":"203.0.113.10","ttl":300}'

# second call with the same body returns:
# HTTP 400, "success": false
# errors[0].code = 81057  (same-type duplicate)
# or 81053  (CNAME/A-type clash) if a CNAME is already at that name
3

Cross-check what actually resolves

Confirm which record is really answering for that name before you decide what to delete or keep. If a CNAME comes back where you expected an A record, that CNAME is what is blocking the write.

Terminal
check-what-resolves.sh
dig +short A app.example.com
dig +short CNAME app.example.com

# against a public resolver too, in case of caching differences
dig +short A app.example.com @1.1.1.1
dig +short CNAME app.example.com @1.1.1.1
4

Change the write from "always create" to "list, then upsert"

List first. If the array is empty, POST the new record. If a record already exists, take its id and PATCH just the changed fields instead of POSTing a duplicate. If the conflict is a CNAME blocking an A or AAAA record (or the reverse), decide which record should own the name, delete the other one, then create the intended record.

DNS record
records-before-and-after
# BEFORE: the script always sends this, even on the second run
POST /zones/{zone_id}/dns_records
{"type":"A","name":"app.example.com","content":"203.0.113.10","ttl":300,"proxied":false}
# -> HTTP 400, code 81057, "The record already exists"

# AFTER: list first
GET /zones/{zone_id}/dns_records?type=A&name.exact=app.example.com
# -> one result, id "abc123", content "203.0.113.10" (already correct)
# decision: noop, nothing to send

# AFTER: content changed, so PATCH the existing record instead of creating one
PATCH /zones/{zone_id}/dns_records/abc123
{"content":"203.0.113.20"}

# AFTER: CNAME blocking an A record at the same name, pick a winner
DELETE /zones/{zone_id}/dns_records/{cname_record_id}
POST /zones/{zone_id}/dns_records
{"type":"A","name":"app.example.com","content":"203.0.113.10","ttl":300,"proxied":false}
5

Apply the change through the Cloudflare API

With the decision made, send only the call that matches it: POST to create, PATCH to update in place, or DELETE followed by POST to resolve a CNAME versus A/AAAA clash.

Terminal
cloudflare-upsert.sh
# create, only when the list call returned zero results
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
  -H "Authorization: Bearer $CF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"type":"A","name":"app.example.com","content":"203.0.113.10","ttl":300,"proxied":false}'

# update in place, only when the list call returned one existing record
curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
  -H "Authorization: Bearer $CF_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"content":"203.0.113.20"}'

How to check it worked

Re-run the same create call: it should now succeed with no 81057 or 81053 error, because the script lists first, finds the existing record, and PATCHes it instead of POSTing a duplicate.

Terminal
verify.sh
# confirm exactly one record exists at that name/type, not zero and not a duplicate
curl -s "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name.exact=app.example.com" \
  -H "Authorization: Bearer $CF_TOKEN" | jq '.result | length'
# good answer: 1

# confirm externally that exactly one IP resolves, with no stale duplicate
dig +short A app.example.com @1.1.1.1
# good answer: one IP, the intended one, nothing else

The full code

Here is the complete reconciler in one file for each language. It reads the desired record from the environment, lists what already exists at that name and type through the Cloudflare API, decides whether to create, update, or do nothing with a pure function, and only writes when you turn dry run off.

View this code on GitHub Full runnable folder with tests in the dns-fixes repo.

duplicate_record_write_conflict.py
"""List DNS records before writing, then create or update instead of
blindly POSTing a duplicate. Repairs the write through Cloudflare.

Safe by default. Set DRY_RUN=false to let it write.

Env vars:
  DNS_DOMAIN               the name to check, e.g. "app.example.com"
  DNS_RECORD_TYPE          record type to check, default "A"
  DNS_RECORD_CONTENT       desired content, e.g. an IP for an A record
  DNS_RECORD_TTL           desired TTL, default 300
  DNS_RECORD_PROXIED       "true" or "false", default "false"
  CLOUDFLARE_API_TOKEN     Cloudflare API token (only needed for repair)
  CLOUDFLARE_ZONE_ID       Cloudflare zone id (only needed for repair)
  DRY_RUN                  default "true"; set to "false" to actually write
"""
import os
import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("duplicate_record_write_conflict")

DNS_DOMAIN = os.environ.get("DNS_DOMAIN", "app.example.com")
DNS_RECORD_TYPE = os.environ.get("DNS_RECORD_TYPE", "A")
DNS_RECORD_CONTENT = os.environ.get("DNS_RECORD_CONTENT", "203.0.113.10")
DNS_RECORD_TTL = int(os.environ.get("DNS_RECORD_TTL", "300"))
DNS_RECORD_PROXIED = os.environ.get("DNS_RECORD_PROXIED", "false").lower() == "true"
CLOUDFLARE_API_TOKEN = os.environ.get("CLOUDFLARE_API_TOKEN", "")
CLOUDFLARE_ZONE_ID = os.environ.get("CLOUDFLARE_ZONE_ID", "")
DRY_RUN = os.environ.get("DRY_RUN", "true").lower() == "true"

CF_API = "https://api.cloudflare.com/client/v4"


def plan_dns_write(existing_records, desired):
    """Pure decision function. No I/O.

    existing_records: list of {id, name, type, content, ttl, proxied}
                       already at (desired['name'], desired['type'])
    desired: {name, type, content, ttl, proxied}

    Returns one of:
      {"action": "create", "body": desired}
      {"action": "noop", "id": }
      {"action": "update", "id": , "body": {changed fields only}}
    """
    if not existing_records:
        return {"action": "create", "body": desired}

    current = existing_records[0]
    diff = {}
    for field in ("content", "ttl", "proxied"):
        if field in desired and current.get(field) != desired[field]:
            diff[field] = desired[field]

    if not diff:
        return {"action": "noop", "id": current["id"]}

    return {"action": "update", "id": current["id"], "body": diff}


def resolve_via_dns(domain, record_type):
    """Look up what a public resolver actually returns right now, using
    dnspython. Informational only, used before deciding what to touch.
    """
    import dns.resolver

    try:
        answer = dns.resolver.resolve(domain, record_type)
        return [str(rdata) for rdata in answer]
    except dns.resolver.NXDOMAIN:
        return []
    except dns.resolver.NoAnswer:
        return []


def list_existing_records(name, record_type):
    """List DNS records at the exact name and type through the Cloudflare API."""
    import requests

    headers = {"Authorization": f"Bearer {CLOUDFLARE_API_TOKEN}"}
    params = {"type": record_type, "name.exact": name}
    r = requests.get(
        f"{CF_API}/zones/{CLOUDFLARE_ZONE_ID}/dns_records",
        headers=headers, params=params, timeout=30,
    )
    r.raise_for_status()
    return [
        {
            "id": rec["id"],
            "name": rec["name"],
            "type": rec["type"],
            "content": rec["content"],
            "ttl": rec.get("ttl", 1),
            "proxied": rec.get("proxied", False),
        }
        for rec in r.json()["result"]
    ]


def create_record(body):
    import requests

    headers = {"Authorization": f"Bearer {CLOUDFLARE_API_TOKEN}"}
    if DRY_RUN:
        log.info("[dry run] would create record %s", body)
        return
    r = requests.post(
        f"{CF_API}/zones/{CLOUDFLARE_ZONE_ID}/dns_records",
        headers=headers, json=body, timeout=30,
    )
    r.raise_for_status()
    log.info("Created record %s", body)


def update_record(record_id, body):
    import requests

    headers = {"Authorization": f"Bearer {CLOUDFLARE_API_TOKEN}"}
    if DRY_RUN:
        log.info("[dry run] would update record %s with %s", record_id, body)
        return
    r = requests.patch(
        f"{CF_API}/zones/{CLOUDFLARE_ZONE_ID}/dns_records/{record_id}",
        headers=headers, json=body, timeout=30,
    )
    r.raise_for_status()
    log.info("Updated record %s with %s", record_id, body)


def run():
    if not (CLOUDFLARE_API_TOKEN and CLOUDFLARE_ZONE_ID):
        log.warning("No Cloudflare credentials set. Nothing to reconcile for %s.", DNS_DOMAIN)
        return

    desired = {
        "name": DNS_DOMAIN,
        "type": DNS_RECORD_TYPE,
        "content": DNS_RECORD_CONTENT,
        "ttl": DNS_RECORD_TTL,
        "proxied": DNS_RECORD_PROXIED,
    }

    existing = list_existing_records(DNS_DOMAIN, DNS_RECORD_TYPE)
    plan = plan_dns_write(existing, desired)

    if plan["action"] == "noop":
        log.info("%s already matches the desired state. Nothing to do.", DNS_DOMAIN)
        return

    if plan["action"] == "create":
        log.info("%s has no existing %s record. Creating.", DNS_DOMAIN, DNS_RECORD_TYPE)
        create_record(plan["body"])
        return

    log.info("%s exists but differs. Updating in place instead of duplicating.", DNS_DOMAIN)
    update_record(plan["id"], plan["body"])


if __name__ == "__main__":
    run()
duplicate-record-write-conflict.js
/**
 * List DNS records before writing, then create or update instead of
 * blindly POSTing a duplicate. Repairs the write through Cloudflare.
 *
 * Safe by default. Set DRY_RUN=false to let it write.
 *
 * Env vars:
 *   DNS_DOMAIN               the name to check, e.g. "app.example.com"
 *   DNS_RECORD_TYPE          record type to check, default "A"
 *   DNS_RECORD_CONTENT       desired content, e.g. an IP for an A record
 *   DNS_RECORD_TTL           desired TTL, default 300
 *   DNS_RECORD_PROXIED       "true" or "false", default "false"
 *   CLOUDFLARE_API_TOKEN     Cloudflare API token (only needed for repair)
 *   CLOUDFLARE_ZONE_ID       Cloudflare zone id (only needed for repair)
 *   DRY_RUN                  default "true"; set to "false" to actually write
 */
import { pathToFileURL } from "node:url";

const DNS_DOMAIN = process.env.DNS_DOMAIN || "app.example.com";
const DNS_RECORD_TYPE = process.env.DNS_RECORD_TYPE || "A";
const DNS_RECORD_CONTENT = process.env.DNS_RECORD_CONTENT || "203.0.113.10";
const DNS_RECORD_TTL = Number(process.env.DNS_RECORD_TTL || 300);
const DNS_RECORD_PROXIED = (process.env.DNS_RECORD_PROXIED || "false").toLowerCase() === "true";
const CLOUDFLARE_API_TOKEN = process.env.CLOUDFLARE_API_TOKEN || "";
const CLOUDFLARE_ZONE_ID = process.env.CLOUDFLARE_ZONE_ID || "";
const DRY_RUN = (process.env.DRY_RUN || "true").toLowerCase() === "true";

const CF_API = "https://api.cloudflare.com/client/v4";

export function planDnsWrite(existingRecords, desired) {
  // Pure decision function. No I/O.
  // existingRecords: array of { id, name, type, content, ttl, proxied }
  //                  already at (desired.name, desired.type)
  // desired: { name, type, content, ttl, proxied }
  //
  // Returns one of:
  //   { action: "create", body: desired }
  //   { action: "noop", id }
  //   { action: "update", id, body: { changed fields only } }
  if (!existingRecords.length) {
    return { action: "create", body: desired };
  }

  const current = existingRecords[0];
  const diff = {};
  for (const field of ["content", "ttl", "proxied"]) {
    if (field in desired && current[field] !== desired[field]) {
      diff[field] = desired[field];
    }
  }

  if (Object.keys(diff).length === 0) {
    return { action: "noop", id: current.id };
  }

  return { action: "update", id: current.id, body: diff };
}

async function resolveViaDns(domain, recordType) {
  // Look up what a public resolver actually returns right now, using the
  // built-in dns module. Informational only, used before deciding what to
  // touch.
  const dns = await import("node:dns");
  const { promises: dnsPromises } = dns;
  try {
    if (recordType === "CNAME") return await dnsPromises.resolveCname(domain);
    return await dnsPromises.resolve4(domain);
  } catch (err) {
    if (err.code === "ENODATA" || err.code === "ENOTFOUND") return [];
    throw err;
  }
}

async function listExistingRecords(name, recordType) {
  const headers = { Authorization: `Bearer ${CLOUDFLARE_API_TOKEN}` };
  const params = new URLSearchParams({ type: recordType, "name.exact": name });
  const res = await fetch(
    `${CF_API}/zones/${CLOUDFLARE_ZONE_ID}/dns_records?${params.toString()}`,
    { headers },
  );
  if (!res.ok) throw new Error(`Cloudflare list returned ${res.status}`);
  const body = await res.json();
  return body.result.map((rec) => ({
    id: rec.id,
    name: rec.name,
    type: rec.type,
    content: rec.content,
    ttl: rec.ttl ?? 1,
    proxied: rec.proxied ?? false,
  }));
}

async function createRecord(body) {
  const headers = {
    Authorization: `Bearer ${CLOUDFLARE_API_TOKEN}`,
    "Content-Type": "application/json",
  };
  if (DRY_RUN) {
    console.log(`[dry run] would create record ${JSON.stringify(body)}`);
    return;
  }
  const res = await fetch(`${CF_API}/zones/${CLOUDFLARE_ZONE_ID}/dns_records`, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`Cloudflare create returned ${res.status}`);
  console.log(`Created record ${JSON.stringify(body)}`);
}

async function updateRecord(recordId, body) {
  const headers = {
    Authorization: `Bearer ${CLOUDFLARE_API_TOKEN}`,
    "Content-Type": "application/json",
  };
  if (DRY_RUN) {
    console.log(`[dry run] would update record ${recordId} with ${JSON.stringify(body)}`);
    return;
  }
  const res = await fetch(`${CF_API}/zones/${CLOUDFLARE_ZONE_ID}/dns_records/${recordId}`, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`Cloudflare update returned ${res.status}`);
  console.log(`Updated record ${recordId} with ${JSON.stringify(body)}`);
}

async function run() {
  if (!(CLOUDFLARE_API_TOKEN && CLOUDFLARE_ZONE_ID)) {
    console.warn(`No Cloudflare credentials set. Nothing to reconcile for ${DNS_DOMAIN}.`);
    return;
  }

  const desired = {
    name: DNS_DOMAIN,
    type: DNS_RECORD_TYPE,
    content: DNS_RECORD_CONTENT,
    ttl: DNS_RECORD_TTL,
    proxied: DNS_RECORD_PROXIED,
  };

  const existing = await listExistingRecords(DNS_DOMAIN, DNS_RECORD_TYPE);
  const plan = planDnsWrite(existing, desired);

  if (plan.action === "noop") {
    console.log(`${DNS_DOMAIN} already matches the desired state. Nothing to do.`);
    return;
  }

  if (plan.action === "create") {
    console.log(`${DNS_DOMAIN} has no existing ${DNS_RECORD_TYPE} record. Creating.`);
    await createRecord(plan.body);
    return;
  }

  console.log(`${DNS_DOMAIN} exists but differs. Updating in place instead of duplicating.`);
  await updateRecord(plan.id, plan.body);
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  run().catch((err) => {
    console.error(err);
    process.exit(1);
  });
}

Add a test

The upsert decision is the part most worth testing, because it decides whether the script sends a create, an update, or nothing at all. Because plan_dns_write is pure, the test needs no network and no Cloudflare account. It just feeds in plain record lists and checks the plan.

test_write_conflict.py
from duplicate_record_write_conflict import plan_dns_write


def desired(**over):
    base = {"name": "app.example.com", "type": "A", "content": "203.0.113.10",
            "ttl": 300, "proxied": False}
    base.update(over)
    return base


def existing(**over):
    base = {"id": "rec_1", "name": "app.example.com", "type": "A",
            "content": "203.0.113.10", "ttl": 300, "proxied": False}
    base.update(over)
    return base


def test_creates_when_nothing_exists():
    plan = plan_dns_write([], desired())
    assert plan["action"] == "create"
    assert plan["body"] == desired()


def test_noop_when_existing_matches_desired():
    plan = plan_dns_write([existing()], desired())
    assert plan == {"action": "noop", "id": "rec_1"}


def test_update_when_content_differs():
    plan = plan_dns_write([existing(content="203.0.113.99")], desired())
    assert plan["action"] == "update"
    assert plan["id"] == "rec_1"
    assert plan["body"] == {"content": "203.0.113.10"}


def test_update_only_sends_changed_fields():
    plan = plan_dns_write([existing(ttl=60)], desired())
    assert plan["body"] == {"ttl": 300}
duplicate-record-write-conflict.test.js
import { test } from "node:test";
import assert from "node:assert/strict";
import { planDnsWrite } from "./duplicate-record-write-conflict.js";

const desired = (over = {}) => ({
  name: "app.example.com", type: "A", content: "203.0.113.10",
  ttl: 300, proxied: false, ...over,
});

const existing = (over = {}) => ({
  id: "rec_1", name: "app.example.com", type: "A",
  content: "203.0.113.10", ttl: 300, proxied: false, ...over,
});

test("creates when nothing exists", () => {
  const plan = planDnsWrite([], desired());
  assert.equal(plan.action, "create");
  assert.deepEqual(plan.body, desired());
});

test("noop when existing matches desired", () => {
  const plan = planDnsWrite([existing()], desired());
  assert.deepEqual(plan, { action: "noop", id: "rec_1" });
});

test("update when content differs", () => {
  const plan = planDnsWrite([existing({ content: "203.0.113.99" })], desired());
  assert.equal(plan.action, "update");
  assert.equal(plan.id, "rec_1");
  assert.deepEqual(plan.body, { content: "203.0.113.10" });
});

test("update only sends changed fields", () => {
  const plan = planDnsWrite([existing({ ttl: 60 })], desired());
  assert.deepEqual(plan.body, { ttl: 300 });
});

Case studies

Retry storm

The deploy job that fought itself

A deploy pipeline added a DNS record for every new preview environment by calling a create endpoint directly. When a deploy took longer than the pipeline's timeout, the job retried, sending the exact same create call a second time. Every retried deploy failed with error 81057, even though the record from the first attempt was correct and already live.

Switching the step to list first, then create only when nothing existed, made retries harmless. The second attempt now saw the record from the first attempt, matched it, and returned a clean no-op instead of an error.

CNAME clash

The onboarding flow that could not add its own record

A SaaS product asked customers to add a CNAME for a custom domain, but some customers already had an A record at that name from a previous provider. The onboarding script's create call failed every time with error 81053, and the error message on screen just said "something went wrong," which generated support tickets.

The fix listed the existing records at that name first. When a conflicting A record was found, the flow showed the customer exactly which record to remove, and only after confirming it was gone did the script create the CNAME. The confusing failure became a clear, one-step instruction.

What good looks like

After the fix, the automation can run any number of times without ever hitting error 81057 or 81053 again. Every run either creates a record that was missing, updates one that had drifted, or does nothing because the zone already matches what is wanted. Re-running the same job twice in a row is now a safe, boring no-op instead of a failure.

FAQ

Why does Cloudflare reject my DNS record with error 81057?

Error 81057, "The record already exists," means a record of the same type already sits at that exact name. Your script or tool sent a create (POST) call without first checking whether the record was already there, so Cloudflare refused to make a duplicate.

What is the difference between error 81057 and error 81053?

81057 is a same-type clash, for example you tried to create a second A record at a name that already has one with the exact same content or an existing record of that type. 81053 is a cross-type clash, most often trying to add a CNAME at a name that already has an A, AAAA, or other record, or the reverse. DNS forbids a CNAME from sharing a name with any other record type, so 81053 always needs one of the two records deleted.

How do I stop this from happening again?

Change the automation so it never blindly creates. Before every write, list what already exists at that exact name and type. If nothing is there, create it. If a record is already there and matches what you want, do nothing. If it is there but different, update it in place with a PATCH instead of creating a new one.

Related field notes

Citations

On the problem:

  1. Cloudflare DNS docs: cannot add DNS records with the same name. developers.cloudflare.com/dns
  2. Cloudflare Community: cannot add A record, record already exists (code 81057). community.cloudflare.com
  3. Cloudflare Community: an A, AAAA, or CNAME record with that host already exists (code 81053). community.cloudflare.com

On the solution:

  1. Cloudflare API: DNS Records, List method. developers.cloudflare.com/api
  2. Cloudflare API: DNS Records, Update (Overwrite) method. developers.cloudflare.com/api
  3. Cloudflare DNS docs: how to create DNS records. developers.cloudflare.com/dns

Stuck on a tricky one?

If you have a DNS, domain, or certificate problem you would rather hand off, this is the kind of work I do. Message me and we can work through it together.

Contact me on LinkedIn

Did this fix your zone?

If this saved you a failed deploy or a confusing support ticket, you can buy me a coffee. It is the best way to keep these field notes free and growing.

Buy me a coffee on Ko-fi

Back to all DNS and Domains field notes