# Manage DNS with the CLI

The `zcp` CLI manages your DNS zones and records from the terminal. This page covers each step: create a zone, read the name servers to delegate to, add and remove records, and verify the result.

## Before You Start

- The `zcp` CLI installed and authenticated. See the [CLI installation guide](https://docs.zcp.zsoftly.ca/public-cloud/cli/installation) and [CLI Quickstart](https://docs.zcp.zsoftly.ca/public-cloud/cli/quickstart).
- A domain you control at its registrar.

DNS commands are account-level. They do not need `--region` or `--project`, unlike most other `zcp` commands. The one exception is `zcp dns create`, which needs `--project` to place the new zone in a project.

## Create a Zone

Add your domain to ZCP. This creates the DNS zone ZSoftly serves.

```bash
zcp dns create --name example.com --project default-9
```

Output:

```text
FIELD    VALUE
Slug     examplecom
Name     example.com
Status   false
Created  2026-07-18T21:08:26.000000Z
```

ZCP generates a slug from the domain name. Every later command addresses the zone by this slug, not by the domain name. Copy it from this output or from `zcp dns list`. The `Status` value turns active shortly after creation.

ZCP serves DNS from a single account-wide region, so `zcp dns create` ignores `ZCP_REGION` and sets the region for you. Pass only `--name` and `--project`.

## Read Your Name Servers

When you create a zone, ZCP adds its `SOA` and `NS` records for you. The `NS` record set holds the two name servers you point your registrar at.

```bash
zcp dns show examplecom
```

Output:

```text
FIELD    VALUE
Slug     examplecom
Name     example.com
Status   true
Created  2026-07-18T21:08:26.000000Z
Updated  2026-07-18T21:08:26.000000Z


Records (2):
NAME          TYPE  CONTENT                                                                   TTL
example.com.  SOA   ns1.zsoftly.ca. hostmaster.zsoftly.ca. 2026071801 10800 3600 604800 3600  3600
example.com.  NS    ns1.zsoftly.ca., ns2.zsoftly.ca.                                          3600
```

Point your registrar at both name servers before you send live traffic. See [Domains](https://docs.zcp.zsoftly.ca/public-cloud/dns/domains) for the per-registrar steps and the Cloudflare Registrar exception.

## Add Records

Use `zcp dns record-create` with the zone slug, a relative name, a type, and the content.

The record output and `--show-zone` behavior described below require `zcp` v1.1.0 or later. Earlier versions print all zone records for `record-create` and a confirmation message without removed record details for `record-delete`. `zcp dns show` does not change.

```bash
# Apex A record (use @ for the zone root)
zcp dns record-create --domain examplecom --name @ --type A --content 203.0.113.10


# Subdomain A record
zcp dns record-create --domain examplecom --name www --type A --content 203.0.113.10


# IPv6 address
zcp dns record-create --domain examplecom --name ipv6 --type AAAA --content 2001:db8::10


# Alias one name to another
zcp dns record-create --domain examplecom --name blog --type CNAME --content www.example.com.


# Text record for SPF or domain verification
zcp dns record-create --domain examplecom --name @ --type TXT --content '"v=spf1 -all"'


# Mail server (MX needs a priority)
zcp dns record-create --domain examplecom --name @ --type MX --content mail.example.com. --priority 10
```

The `--name` value is relative to the zone. Pass `www`, not `www.example.com`. ZCP appends the zone for you. Passing a full name like `www.example.com` creates `www.example.com.example.com`. Use `@` for the zone root.

Record rules:

- The default TTL is `14400` seconds (4 hours). Override it with `--ttl`, for example `--ttl 3600`.
- Wrap `TXT` content in escaped quotes, for example `'"v=spf1 -all"'`, so the quotes reach the record.
- A `CAA` value includes a quoted value. Pass the complete value in shell quotes, for example `--content '0 issue "letsencrypt.org"'`. The CLI rejects a CAA value without its required quotes.
- End a `CNAME` target with a trailing dot (`www.example.com.`) to keep it fully qualified.
- An `MX` record needs `--priority`. Put the mail server in `--content` and the preference number in `--priority`, for example `--priority 10`. The CLI stops with an error if you leave it off.
- Creating a record at a name and type with existing values adds the new value to the set.
- Supported types are `A`, `AAAA`, `CAA`, `CNAME`, `MX`, `TXT`, and `NS`. The CLI does not create `SRV` or `LOC` records yet. Create them in the DNS section of the [console](https://cloud.zcp.zsoftly.ca/login) or with the [API](https://docs.zcp.zsoftly.ca/public-cloud/dns/api/).

By default, `record-create` prints one row for the value it created. The result includes the name, type, content, TTL, and, for an `MX` record, its priority. A priority of `0` is shown as `0`.

```text
NAME          TYPE  CONTENT            TTL    PRIORITY
example.com.  MX    mail.example.com.  14400  0
```

Add `--show-zone` when you need the resulting full zone instead.

```bash
zcp dns record-create --domain examplecom --name @ --type MX --content mail.example.com. --priority 0 --show-zone
```

`--output json` and `--output yaml` return the same one-record list by default. Use `--show-zone` with either format when a script needs the resulting full zone.

Create `MX` records in the [portal](https://docs.zcp.zsoftly.ca/public-cloud/dns/records) or with the [API](https://docs.zcp.zsoftly.ca/public-cloud/dns/api/#mx-records) too.

## List and Show Records

`zcp dns show` prints the zone and every record it holds.

```bash
zcp dns show examplecom
```

```text
Records (6):
NAME                  TYPE   CONTENT                                                                   TTL
www.example.com.      A      203.0.113.10                                                              14400
ipv6.example.com.     AAAA   2001:db8::10                                                              14400
blog.example.com.     CNAME  www.example.com.                                                          14400
example.com.          TXT    "v=spf1 -all"                                                             14400
example.com.          SOA    ns1.zsoftly.ca. hostmaster.zsoftly.ca. 2026071807 10800 3600 604800 3600  3600
example.com.          NS     ns1.zsoftly.ca., ns2.zsoftly.ca.                                          3600
```

For scripting, add `--output json` and pipe to `jq`.

```bash
zcp dns list --output json
zcp dns show examplecom --output json
```

## Update a Record

There is no update command. To change a record, delete it and create it again with the new value.

```bash
zcp dns record-delete --domain examplecom --name www --type A --yes
zcp dns record-create --domain examplecom --name www --type A --content 203.0.113.20
```

## Delete a Record

Address the record set by name and type. The command reads the matching values first, then deletes each value from that initial snapshot.

```bash
zcp dns record-delete --domain examplecom --name www --type A
```

Add `--yes` to skip the confirmation prompt in scripts. Output:

```text
NAME              TYPE  CONTENT       TTL    PRIORITY  SOURCE
www.example.com.  A     203.0.113.10  14400  -         Pre-delete snapshot
```

By default, the CLI reads the selected record set before deleting it, then lists every value from that pre-delete snapshot. The `SOURCE` column identifies this output as `Pre-delete snapshot`. The CLI only targets the values from this initial snapshot. A value added after the read is not deleted. This snapshot is not an authoritative list of values the command removed. If a matching value changes before deletion, the snapshot can differ from the values removed. The CLI also writes this warning to standard error.

In JSON and YAML output, each snapshot record has `source: pre_delete_snapshot`.

```json
[
  {
    "name": "www.example.com.",
    "type": "A",
    "content": "203.0.113.10",
    "ttl": "14400",
    "priority": "-",
    "source": "pre_delete_snapshot"
  }
]
```

If the initial read finds no matching values, the command succeeds with an empty result. With `--show-zone`, it prints the remaining zone instead.

If one value deletion fails, the command exits with an error and prints no successful deletion result. Values deleted before the failure remain deleted. Run `zcp dns show` before retrying so you can see the remaining values.

If deletion succeeds but the CLI could not read record details beforehand, it returns an empty result and a warning. `--show-zone` returns the resulting full zone and does not add snapshot metadata.

Use relative names or `@` for normal record deletion. If a temporary lookup failure prevents the snapshot, an explicit fully qualified `--name` ending in a dot, such as `www.example.ca.`, allows a name-and-type fallback request. This fallback cannot delete values individually and may fail for a multi-value record set. Authentication and authorization errors still stop the command. Add `--show-zone` to print the resulting full zone after deletion instead.

```bash
zcp dns record-delete --domain examplecom --name www --type A --show-zone
```

## Delete a Zone

This removes the zone and all of its records.

```bash
zcp dns delete examplecom
```

Add `--yes` to skip the confirmation prompt.

## Verify

Query the ZSoftly name servers directly to confirm a record before global propagation finishes.

```bash
# Ask a ZSoftly name server for the record
dig A www.example.com @ns1.zsoftly.ca +short


# After you delegate at your registrar, query any public resolver
dig A www.example.com @1.1.1.1 +short
```

See also: [DNS Overview](https://docs.zcp.zsoftly.ca/public-cloud/dns/overview), [Domains](https://docs.zcp.zsoftly.ca/public-cloud/dns/domains), [DNS Records](https://docs.zcp.zsoftly.ca/public-cloud/dns/records), [Worked examples](https://docs.zcp.zsoftly.ca/public-cloud/dns/examples), [Manage DNS with the API](https://docs.zcp.zsoftly.ca/public-cloud/dns/api/), [Troubleshooting](https://docs.zcp.zsoftly.ca/public-cloud/dns/troubleshooting), [CLI Reference](https://docs.zcp.zsoftly.ca/public-cloud/cli/reference)
