From 218d1a9b8c4ea27f6f9c3ce1dce1b9da84deb467 Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Tue, 3 Nov 2026 20:20:00 -0600 Subject: [PATCH 1/5] docs: add Private DNS quickstart, guides, and limits Add a quickstart that reaches an Instance by its private name and then by a custom name, a guide to private zones, records, and network associations, a limits and behavior reference, and Private DNS troubleshooting. Stop describing Private DNS as planned. --- compute/limitations-and-roadmap.mdx | 1 - docs.json | 3 + domain-dns/limitations-and-roadmap.mdx | 1 + domain-dns/private-dns-quickstart.mdx | 177 +++++++++++++++++++++++ domain-dns/private-dns-reference.mdx | 70 +++++++++ domain-dns/private-dns-zones.mdx | 167 +++++++++++++++++++++ domain-dns/troubleshooting.mdx | 41 ++++++ galactic-vpc/limitations-and-roadmap.mdx | 4 +- galactic-vpc/networks/traffic.mdx | 4 +- 9 files changed, 462 insertions(+), 6 deletions(-) create mode 100644 domain-dns/private-dns-quickstart.mdx create mode 100644 domain-dns/private-dns-reference.mdx create mode 100644 domain-dns/private-dns-zones.mdx diff --git a/compute/limitations-and-roadmap.mdx b/compute/limitations-and-roadmap.mdx index 90b8933..8d7ae71 100644 --- a/compute/limitations-and-roadmap.mdx +++ b/compute/limitations-and-roadmap.mdx @@ -34,7 +34,6 @@ The Compute preview has the following limitations. For limitations of each [runt - During the preview, Instances have private IPv6 addresses only. Private IPv4 addresses and public IP addresses are a preview limitation. - Instances reach IPv4-only internet services by hostname only, through DNS64 and NAT64. A connection to an IPv4 address fails. See [Reach the internet](/galactic-vpc/networks/traffic#reach-the-internet). - Every Instance can reach the internet, and you can't turn that off. Outbound traffic leaves from shared addresses that Datum doesn't publish, so you can't allowlist your Instances at a destination. -- Private DNS names for Instances are on the [roadmap](/galactic-vpc/limitations-and-roadmap#planned-capabilities). - Firewall rules to filter traffic between Instances on a network are on the [roadmap](/galactic-vpc/limitations-and-roadmap#planned-capabilities). - Public access is HTTP and HTTPS only, served through Datum's [ALB](/alb/overview). Raw TCP and UDP exposure is a preview limitation. diff --git a/docs.json b/docs.json index d885228..963a3f6 100644 --- a/docs.json +++ b/docs.json @@ -213,6 +213,9 @@ "domain-dns/domains", "domain-dns/dns", "domain-dns/private-dns", + "domain-dns/private-dns-quickstart", + "domain-dns/private-dns-zones", + "domain-dns/private-dns-reference", "domain-dns/operations", "domain-dns/point-to-alb", "domain-dns/observability", diff --git a/domain-dns/limitations-and-roadmap.mdx b/domain-dns/limitations-and-roadmap.mdx index 1736fa7..947552e 100644 --- a/domain-dns/limitations-and-roadmap.mdx +++ b/domain-dns/limitations-and-roadmap.mdx @@ -13,6 +13,7 @@ Domains and DNS are still being built. This page lists known limitations and whe - **A zone's domain name can't change.** To use a different domain, create a new zone. - **One zone for each domain.** A domain can belong to only one zone on Datum. - **`ALIAS` isn't portable.** `ALIAS` isn't a standard record type, so a zone file exported from Datum can't carry it to another provider. See [ALIAS](/domain-dns/dns#alias-/-cname-flattening). +- **Private DNS has no `datumctl dns` or Portal editing yet.** Manage private zones, associations, and records with `datumctl apply`. See [Manage private zones and records](/domain-dns/private-dns-zones). - **The DNS API is `v1alpha1`.** Field names in `DNSZone` and `DNSRecordSet` can change between releases. The `datumctl dns` plugin keeps up with those changes for you. ## Roadmap diff --git a/domain-dns/private-dns-quickstart.mdx b/domain-dns/private-dns-quickstart.mdx new file mode 100644 index 0000000..db708cb --- /dev/null +++ b/domain-dns/private-dns-quickstart.mdx @@ -0,0 +1,177 @@ +--- +title: "Private DNS quickstart" +sidebarTitle: "Private DNS quickstart" +description: "Deploy two workloads on a gVPC network and reach one from the other by its private name." +--- + +This quickstart deploys two small workloads on the same [gVPC](/galactic-vpc/overview) network. From the first, you look up the second by its [Private DNS](/domain-dns/private-dns) name and connect to it. Then you give it a name of your own. It takes about ten minutes. + +## Before you begin + +- Install `datumctl` and the `compute` plugin, and select a project. See [Compute commands](/datumctl/compute/overview). +- Your account needs the **Compute Admin** and **DNS Admin** roles in the project. + +## Reach an Instance by name + + + + Deploy an `nginx` workload named `web` to Dallas: + + ```bash + datumctl compute deploy web \ + --image=docker.io/library/nginx:1.27-alpine \ + --runtime-class=general-purpose \ + --location=us-central-1 + ``` + + If this is the first deploy in the project, the command asks whether to create a network named `default`. Enter `y`. Then enter `y` to apply the plan. + + + + Deploy a second workload named `client` on the same network. It uses the same image, because the image includes `nslookup` and `wget`: + + ```bash + datumctl compute deploy client \ + --image=docker.io/library/nginx:1.27-alpine \ + --runtime-class=general-purpose \ + --location=us-central-1 + ``` + + + + Compute publishes each Instance at `.instances.datum.internal`. Read the exact name from the Instance: + + ```bash + datumctl compute instances --workload=web + datumctl get instance web-default-us-central-1-0 \ + -o jsonpath='{.status.dns[0].hostnames[0]}' + ``` + + The output is similar to the following: + + ```text + web-default-us-central-1-0.instances.datum.internal + ``` + + If the output is empty, wait a few seconds and run the command again. The name appears once the Instance is ready. + + + + Open a shell in the client and look up the web server: + + ```bash + datumctl compute exec client-default-us-central-1-0 -- \ + nslookup -type=AAAA web-default-us-central-1-0.instances.datum.internal + ``` + + The answer is the web server's private IPv6 address. Now connect to it by name: + + ```bash + datumctl compute exec client-default-us-central-1-0 -- \ + wget -qO- http://web-default-us-central-1-0.instances.datum.internal/ + ``` + + The output is the `nginx` welcome page. + + + +## Give the web server your own name + +Instance names change when a workload replaces its Instances. To give the web server a name you choose, add a record to your own private zone. + + + + Each network has one DNS context. To list them with the network each one belongs to, run: + + ```bash + datumctl get dnsresolvercontexts \ + -o custom-columns='CONTEXT:.metadata.name,NETWORK:.metadata.ownerReferences[0].name' + ``` + + Note the `CONTEXT` for the `default` network, such as `dns-default-90c5adf1dd1c`. + + + + Save the following as `private-zone.yaml`. Replace `CONTEXT_NAME` with the context from the previous step: + + ```yaml + apiVersion: dns.networking.miloapis.com/v1alpha1 + kind: DNSZone + metadata: + name: corp-internal + spec: + domainName: corp.internal + visibility: Private + dnsZoneClassName: private-coredns + --- + apiVersion: dns.networking.miloapis.com/v1alpha1 + kind: DNSZoneAssociation + metadata: + name: corp-internal-default + spec: + dnsZoneRef: + name: corp-internal + resolverContextRef: + name: CONTEXT_NAME + --- + apiVersion: dns.networking.miloapis.com/v1alpha1 + kind: DNSRecordSet + metadata: + name: www + spec: + dnsZoneRef: + name: corp-internal + recordType: CNAME + records: + - name: www + ttl: 60 + cname: + content: web-default-us-central-1-0.instances.datum.internal. + ``` + + Apply it: + + ```bash + datumctl apply -f private-zone.yaml + ``` + + + + Check that the association and record are accepted, and that the zone is programmed: + + ```bash + datumctl get dnszoneassociation corp-internal-default \ + -o jsonpath='{.status.conditions[?(@.type=="Accepted")].status}' + datumctl get dnsrecordset www \ + -o jsonpath='{.status.conditions[?(@.type=="Accepted")].status}' + datumctl get dnszone corp-internal + ``` + + Continue when both commands print `True` and the zone's `PROGRAMMED` column shows `True`. This usually takes less than a minute. If something stays `False`, see [Troubleshooting](/domain-dns/troubleshooting#private-dns). + + + + ```bash + datumctl compute exec client-default-us-central-1-0 -- \ + wget -qO- http://www.corp.internal/ + ``` + + The output is the same welcome page. The name resolves only from the `default` network. + + + +## Clean up + +```bash +datumctl delete -f private-zone.yaml +datumctl compute destroy client +datumctl compute destroy web +``` + +Deleting the workloads removes their Instance names. + +## What's next + +- [Manage private zones and records](/domain-dns/private-dns-zones) +- [Private DNS limits and behavior](/domain-dns/private-dns-reference) +- [Private DNS for Compute](/compute/private-dns) diff --git a/domain-dns/private-dns-reference.mdx b/domain-dns/private-dns-reference.mdx new file mode 100644 index 0000000..df08818 --- /dev/null +++ b/domain-dns/private-dns-reference.mdx @@ -0,0 +1,70 @@ +--- +title: "Private DNS limits and behavior" +sidebarTitle: "Private DNS limits" +description: "Where Private DNS is available, naming rules, limits, and how long changes take." +--- + +This page lists the rules and limits for [Private DNS](/domain-dns/private-dns). + + + Private DNS is in preview, and the `v1alpha1` API can change. + + +## Locations and runtimes + +Private DNS works on every [gVPC](/galactic-vpc/overview) network, in these locations: + +| Location | City | +| --- | --- | +| `us-central-1` | Dallas | +| `us-east-1` | Ashburn | +| `us-west-1` | San Jose | + +A network that spans several locations has one set of names. An Instance in one location resolves the names of Instances in the other locations on the same network. + +Both [runtime classes](/compute/runtimes/overview), `general-purpose` and `unikernel`, get Private DNS. Instances use the resolvers that Datum configures for them. In a `general-purpose` Instance, they're in `/etc/resolv.conf`. An app that uses its own resolver, such as a hard-coded public resolver or DNS over HTTPS, can't resolve private names. + +## Names + +- Every network's managed zone is `datum.internal`. Each network has its own copy. +- Compute publishes Instances at `.instances.datum.internal`. Compute owns every name at or below `instances.datum.internal`, so you can't add records there or create a zone that covers it. +- Use full names. Don't rely on short names such as `web-default-us-central-1-0`: they depend on search domains that can differ between Instances. +- Zone domains use lowercase letters, digits, and hyphens, have at least two labels, and are at most 253 characters long. +- Record names are relative to their zone, such as `api`, or fully qualified. `@` means the zone's own domain. Names aren't case-sensitive. +- A private zone hides the public records of the same domain from the networks it's associated with. + +## Record rules + +- `ALIAS` and `SOA` records aren't supported. Datum manages each private zone's `SOA` record. +- A zone's own domain (`@`) can't be a `CNAME`. +- A `CNAME` can't share a name with another record type. +- An `SRV` target must be a name with `A` or `AAAA` records in the same zone. +- If two record sets have the same name and type, the older one keeps it, and the newer one isn't accepted. +- A record set is accepted or rejected as a whole. One invalid record keeps every name in it from resolving. + +## Limits + +| Limit | Value | +| --- | --- | +| Private zones a network can resolve, including `datum.internal` | 256 | +| Zone domain length | 253 characters | +| Default TTL of a record you create | 300 seconds | +| TTL of an Instance name | 30 seconds | + +## How long changes take + +| Change | When it takes effect | +| --- | --- | +| New Instance | Its name usually resolves within about 30 seconds of the Instance becoming ready. | +| New record, zone, or association | Usually within a minute of being accepted, and longer when a project changes many names at once. The zone's `Programmed` condition says when. | +| Changed or removed record | New lookups get the new answer once the zone is programmed. Clients that cached the old answer keep it until its TTL expires. | +| Deleted or unready Instance | Its addresses are withdrawn. Clients can keep the old answer for up to 30 seconds. | + +Publication is asynchronous. Wait for the status conditions described in [Check status](/domain-dns/private-dns-zones#check-status) rather than a fixed delay. + +## Privacy + +- A private name resolves only from networks that the zone is associated with. Other networks, other projects, and the internet get no answer. +- A network resolves another network's zone only through an association that you create. +- Resolving a name doesn't grant network access. See [Traffic and connecting](/galactic-vpc/networks/traffic). +- Public names keep resolving through the same resolvers. diff --git a/domain-dns/private-dns-zones.mdx b/domain-dns/private-dns-zones.mdx new file mode 100644 index 0000000..4772445 --- /dev/null +++ b/domain-dns/private-dns-zones.mdx @@ -0,0 +1,167 @@ +--- +title: "Manage private zones and records" +sidebarTitle: "Private zones and records" +description: "Add your own private names, choose which gVPC networks can resolve them, and find services by name." +--- + +[Private DNS](/domain-dns/private-dns) publishes Instance names for you. This page covers the names you manage yourself: records in a network's `datum.internal` zone, private zones of your own, and which networks can resolve them. + +Manage these resources with `datumctl apply`, `datumctl get`, and `datumctl delete`. Your account needs the **DNS Admin** role to change them, and **DNS Viewer** to read them. + +## How the pieces fit + +| Resource | What it is | +| --- | --- | +| DNS context | One per network, created for you. It decides which private zones the network can resolve. | +| Managed zone | The network's `datum.internal` zone, created for you. Compute publishes Instance names in it. | +| Private zone | A `DNSZone` with `visibility: Private` that you create, such as `corp.internal`. | +| Zone association | A `DNSZoneAssociation` that lets one network resolve one private zone. | +| Record set | A `DNSRecordSet` that holds records in a zone. | + +A private zone resolves only from the networks it's associated with. Networks never see each other's private zones, even when the zones have the same name. + +## Find a network's DNS context and zone + +To list each network's DNS context and managed zone, run: + +```bash +datumctl get dnsresolvercontexts \ + -o custom-columns='CONTEXT:.metadata.name,NETWORK:.metadata.ownerReferences[0].name,ZONE:.status.managedNamespace.dnsZoneRef.name' +``` + +The output is similar to the following: + +```text +CONTEXT NETWORK ZONE +dns-default-90c5adf1dd1c default managed-c2de65a88ad9 +``` + +Use `CONTEXT` when you associate a zone with the network, and `ZONE` when you add records to the network's `datum.internal` zone. + +## Add a record to datum.internal + +To add a name to one network without creating a zone, add a record set to the network's managed zone. Replace `MANAGED_ZONE` with the `ZONE` from the previous section: + +```yaml +apiVersion: dns.networking.miloapis.com/v1alpha1 +kind: DNSRecordSet +metadata: + name: db +spec: + dnsZoneRef: + name: MANAGED_ZONE + recordType: AAAA + records: + - name: db + ttl: 60 + aaaa: + content: fd20:1::20 +``` + +`db.datum.internal` now resolves on that network only. You can't add records at or below `instances.datum.internal`, which Compute owns. + +## Create a private zone + +Create a zone when you want a namespace of your own, or one that several networks share: + +```yaml +apiVersion: dns.networking.miloapis.com/v1alpha1 +kind: DNSZone +metadata: + name: corp-internal +spec: + domainName: corp.internal + visibility: Private + dnsZoneClassName: private-coredns +``` + +Keep the following points in mind: + +- You can't change a zone's domain or visibility after you create it. Create a new zone instead. +- A private zone resolves nowhere until you associate it with a network. +- Prefer a domain you control, or one under `.internal`. If you create a private zone for a public domain, such as `example.com`, associated networks get your private answers instead of the public ones for every name in that domain. + +## Choose which networks resolve a zone + +Associate the zone with each network that should resolve it: + +```yaml +apiVersion: dns.networking.miloapis.com/v1alpha1 +kind: DNSZoneAssociation +metadata: + name: corp-internal-default +spec: + dnsZoneRef: + name: corp-internal + resolverContextRef: + name: dns-default-90c5adf1dd1c +``` + +To share the zone with another network, create another association that names that network's context. To stop a network from resolving the zone, delete its association. The zone and its records stay. + +An association can't be edited to point at a different zone or network. Delete it and create a new one. If you delete and recreate a zone with the same name, also recreate its associations. + +A network can't have two associated zones with the same domain. A zone can sit below another associated zone, such as `db.corp.internal` below `corp.internal`. Lookups use the zone with the longest matching domain. + +## Add records to a private zone + +Records in a private zone use the same `DNSRecordSet` resource as [public DNS](/domain-dns/dns): + +```yaml +apiVersion: dns.networking.miloapis.com/v1alpha1 +kind: DNSRecordSet +metadata: + name: corp-api +spec: + dnsZoneRef: + name: corp-internal + recordType: AAAA + records: + - name: api + ttl: 60 + aaaa: + content: fd20:1::5 + - name: api + ttl: 60 + aaaa: + content: fd20:1::6 +``` + +Private zones support `A`, `AAAA`, `CNAME`, `TXT`, `MX`, `SRV`, `CAA`, `NS`, `PTR`, `TLSA`, `HTTPS`, and `SVCB` records. For the rules that apply, see [Private DNS limits and behavior](/domain-dns/private-dns-reference#record-rules). + + + Instances have IPv6 addresses only. Use `AAAA` records for addresses on your network. + + +## Find services by name + +Use one of the following, depending on what you need: + +- **One Instance:** use its Compute name, such as `web-default-us-central-1-0.instances.datum.internal`. Read it from the Instance's `status.dns`. See [Private DNS for Compute](/compute/private-dns). +- **A stable name for a service:** create a `CNAME` in your own zone that points at an Instance name, such as `api.corp.internal` → `web-default-us-central-1-0.instances.datum.internal.`. Update the `CNAME` when you replace the Instance. +- **Several Instances behind one name:** create `AAAA` records for each address under one name, as in the example above. Clients pick an address from the answer. DNS doesn't check whether each address is healthy. + +## Check status + +A change goes through two steps. First the record set or association is accepted. Then the zone is programmed, and the name resolves. + +```bash +datumctl get dnsrecordset corp-api \ + -o jsonpath='{.status.conditions[?(@.type=="Accepted")].reason}' +datumctl get dnszone corp-internal +``` + +| Where | Condition | What it means | +| --- | --- | --- | +| Record set, association | `Accepted` | The change is valid. If it's `False`, the reason says why. | +| Zone | `Programmed` | The zone's latest records resolve from at least one resolver. | +| Zone | `Published` | Every resolver the zone needs has the latest records. | + +For what each reason means, see [Troubleshooting](/domain-dns/troubleshooting#private-dns). + +## Delete names and zones + +- Delete a record set to remove its names. Clients can keep an old answer until its TTL expires. +- Delete a zone association to stop one network from resolving the zone. +- Delete a zone to remove it from every network. Delete its record sets and associations too. +- Don't delete a network's managed zone or its association. Instance names on that network stop resolving. If you deleted one by mistake, contact [support@datum.net](mailto:support@datum.net). diff --git a/domain-dns/troubleshooting.mdx b/domain-dns/troubleshooting.mdx index b42877d..b6911f8 100644 --- a/domain-dns/troubleshooting.mdx +++ b/domain-dns/troubleshooting.mdx @@ -75,3 +75,44 @@ If you use [ExternalDNS](/domain-dns/operations#automate-records-with-externaldn - The webhook needs permission to read `dnszones` and to manage `dnsrecordsets` in your project. For the webhook's metrics, see [Observability](/domain-dns/observability#externaldns-webhook-metrics). + +## Private DNS + +These checks apply to [Private DNS](/domain-dns/private-dns) names, which resolve only inside a gVPC network. + +### A private name doesn't resolve + +Work through these checks in order, from an Instance on the network: + +1. **Use the full name.** Look up `web-default-us-central-1-0.instances.datum.internal`, not `web-default-us-central-1-0`. Short names depend on search domains. +2. **Check the network.** A private name resolves only from networks its zone is associated with. An Instance name resolves only from the Instance's own network. +3. **Check the resolver.** Run `cat /etc/resolv.conf` in a `general-purpose` Instance. The app must use these resolvers. An app that uses its own resolver, such as DNS over HTTPS or a hard-coded public resolver, can't resolve private names. +4. **Check the Instance.** An Instance name exists only while the Instance is ready. Run `datumctl get instance INSTANCE_NAME -o yaml` and check `status.dns`. If its condition isn't `Ready`, the reason says why. +5. **Check the zone.** Run `datumctl get dnszone ZONE_NAME`. If `PROGRAMMED` isn't `True`, wait a minute and check again. A zone that isn't associated with any network shows no status. +6. **Check the record or association.** See the next section. + +### A record set or association isn't accepted + +Read the reason: + +```bash +datumctl get dnsrecordset RECORDSET_NAME \ + -o jsonpath='{.status.conditions[?(@.type=="Accepted")].reason}' +``` + +| Reason | What it means | What to do | +| --- | --- | --- | +| `UnsupportedPrivateRecord` | The record set uses a type or name that private zones don't support, such as `ALIAS`, `SOA`, or a `CNAME` at the zone's own domain. | Use a supported record. See [Record rules](/domain-dns/private-dns-reference#record-rules). | +| `ReservedScope` | The name is at or below a name that a Datum service owns, such as `instances.datum.internal`. | Use a different name. | +| `NameConflict` | An older record set already has this name and type, or a `CNAME` shares the name with another type. | Change the record in the record set that has it, or delete that record set first. | +| `MissingSRVTargetAddress` | An `SRV` target has no `A` or `AAAA` record in the same zone. | Add an address record for the target in the zone. | +| `ConflictingTTL` | One name in the record set has two different TTLs. | Use one TTL for each name. | +| `InvalidRecord`, `InvalidTTL` | A value doesn't match its record type, or the TTL is out of range. | Fix the value. | +| `ApexConflict` | Another zone with the same domain is already associated with this network. | Delete one of the two associations. | +| `InvalidReference` | The association's zone or network context was deleted and replaced. | Delete the association and create it again. | + +A request can also be refused when you apply it. A message that starts with `ReservedScope:` means the same as the reason above. `private zone lookup` or `resolver context lookup` errors mean the zone or DNS context you named doesn't exist in the project. + +### A name returns an old answer + +Clients and resolvers keep an answer until its TTL expires. Instance names have a 30 second TTL. Records you create default to 300 seconds. To make changes show up sooner, lower the record's TTL before you change it. diff --git a/galactic-vpc/limitations-and-roadmap.mdx b/galactic-vpc/limitations-and-roadmap.mdx index d0fd83c..7e65830 100644 --- a/galactic-vpc/limitations-and-roadmap.mdx +++ b/galactic-vpc/limitations-and-roadmap.mdx @@ -26,11 +26,9 @@ In addition to the [networking limitations that apply to all of Compute](/comput Datum is working on the following networking capabilities: - **Firewall rules:** Security groups that control the traffic to and from your Instances. -- **Private DNS:** Private DNS names, so Instances can find each other without IP addresses. - **Internet access setting:** A setting that turns internet access on or off for every Instance on a network. -- **Datum DNS resolver:** A DNS resolver that Datum runs for Instances, which also performs DNS64. -Outbound internet access, including IPv4 destinations, and viewing networks in the Datum Cloud portal are already available. For details, see [Reach the internet](/galactic-vpc/networks/traffic#reach-the-internet). +Outbound internet access, including IPv4 destinations, [Private DNS](/domain-dns/private-dns) names for Instances, and viewing networks in the Datum Cloud portal are already available. For details, see [Reach the internet](/galactic-vpc/networks/traffic#reach-the-internet). For what Datum is building next across the platform, see the [Platform roadmap](/platform/roadmap). diff --git a/galactic-vpc/networks/traffic.mdx b/galactic-vpc/networks/traffic.mdx index d436ee9..0b31e5a 100644 --- a/galactic-vpc/networks/traffic.mdx +++ b/galactic-vpc/networks/traffic.mdx @@ -21,7 +21,7 @@ The following table describes which kinds of traffic are possible: | To or from other addresses through an Instance | No | A network only delivers a packet addressed to an Instance's own address, and only accepts a packet from an Instance's own address. An Instance can't route traffic for other addresses, so a router or VPN appliance needs an overlay, such as VXLAN or a UDP tunnel, between Instances. | | Inbound from the internet | Through Datum's ALB | Instance addresses aren't reachable from the internet directly. Public HTTP and HTTPS traffic reaches Instances through Datum's [Application Load Balancer](/alb/overview). Raw TCP and UDP exposure is a preview limitation. | | Outbound to the internet | Yes | Instances can reach internet destinations over IPv6, and services that have only IPv4 addresses by hostname. See [Reach the internet](#reach-the-internet). | -| Lookups by name | Public names only | Instances resolve public DNS names. Private DNS names for Instances are on the [roadmap](/galactic-vpc/limitations-and-roadmap#planned-capabilities). Use IPv6 addresses to reach other Instances in the preview. | +| Lookups by name | Yes | Instances resolve public names, and the [Private DNS](/domain-dns/private-dns) names of other Instances on the same network, such as `web-default-us-central-1-0.instances.datum.internal`. | | Firewall filtering | Not in the preview | Firewall rules to filter traffic between Instances on a network are on the [roadmap](/galactic-vpc/limitations-and-roadmap#planned-capabilities). | The workload API accepts a `networkPolicy` field on each network interface, but the field has no effect. Don't rely on it to restrict traffic. To limit who can reach a service, check credentials in your app, or put services that must stay apart on different networks. Firewall rules for networks are on the [roadmap](/galactic-vpc/limitations-and-roadmap#planned-capabilities). @@ -32,7 +32,7 @@ To connect from one Instance to another on the same network, use the peer's priv - Make your app listen on IPv6 (`::`). An app that listens only on `0.0.0.0` accepts IPv4 connections only, and Instances don't have IPv4 addresses. - In a URL, put an IPv6 address in square brackets, such as `http://[fd20:4a3b:91c2:1::1:0:2]:8080/`. -- Private DNS names aren't available in the preview. To tell an app about its peers, pass their addresses in environment variables or a ConfigMap. +- Connect by name rather than by address. Each Instance has a [Private DNS](/compute/private-dns) name, such as `web-default-us-central-1-0.instances.datum.internal`, that follows the Instance's address. To try it, see the [Private DNS quickstart](/domain-dns/private-dns-quickstart). ## Reach the internet From bd21fce8aa52faa884253ac00316697ba564a24b Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Tue, 3 Nov 2026 20:28:42 -0600 Subject: [PATCH 2/5] docs: fix Private DNS guides after review Use an AAAA record in the quickstart, since CNAMEs into another zone are not followed yet. Add the Network Admin role, use made-up names in examples, and correct the troubleshooting reasons. --- domain-dns/private-dns-quickstart.mdx | 18 ++++++++++-------- domain-dns/private-dns-reference.mdx | 3 ++- domain-dns/private-dns-zones.mdx | 6 +++--- domain-dns/troubleshooting.mdx | 9 ++++++--- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/domain-dns/private-dns-quickstart.mdx b/domain-dns/private-dns-quickstart.mdx index db708cb..54f2ebd 100644 --- a/domain-dns/private-dns-quickstart.mdx +++ b/domain-dns/private-dns-quickstart.mdx @@ -9,7 +9,7 @@ This quickstart deploys two small workloads on the same [gVPC](/galactic-vpc/ove ## Before you begin - Install `datumctl` and the `compute` plugin, and select a project. See [Compute commands](/datumctl/compute/overview). -- Your account needs the **Compute Admin** and **DNS Admin** roles in the project. +- Your account needs the **Compute Admin**, **Network Admin**, and **DNS Admin** roles in the project. ## Reach an Instance by name @@ -64,7 +64,7 @@ This quickstart deploys two small workloads on the same [gVPC](/galactic-vpc/ove nslookup -type=AAAA web-default-us-central-1-0.instances.datum.internal ``` - The answer is the web server's private IPv6 address. Now connect to it by name: + The answer is the web server's private IPv6 address. Note it for later. Now connect to it by name: ```bash datumctl compute exec client-default-us-central-1-0 -- \ @@ -77,7 +77,7 @@ This quickstart deploys two small workloads on the same [gVPC](/galactic-vpc/ove ## Give the web server your own name -Instance names change when a workload replaces its Instances. To give the web server a name you choose, add a record to your own private zone. +To give the web server a name you choose, create a private zone of your own, attach it to the network, and add a record. @@ -88,11 +88,11 @@ Instance names change when a workload replaces its Instances. To give the web se -o custom-columns='CONTEXT:.metadata.name,NETWORK:.metadata.ownerReferences[0].name' ``` - Note the `CONTEXT` for the `default` network, such as `dns-default-90c5adf1dd1c`. + Note the `CONTEXT` for the `default` network, such as `dns-default-4f1c2a9e7b30`. - Save the following as `private-zone.yaml`. Replace `CONTEXT_NAME` with the context from the previous step: + Save the following as `private-zone.yaml`. Replace `CONTEXT_NAME` with the context from the previous step, and `WEB_ADDRESS` with the web server's address from the lookup: ```yaml apiVersion: dns.networking.miloapis.com/v1alpha1 @@ -121,12 +121,12 @@ Instance names change when a workload replaces its Instances. To give the web se spec: dnsZoneRef: name: corp-internal - recordType: CNAME + recordType: AAAA records: - name: www ttl: 60 - cname: - content: web-default-us-central-1-0.instances.datum.internal. + aaaa: + content: WEB_ADDRESS ``` Apply it: @@ -157,6 +157,8 @@ Instance names change when a workload replaces its Instances. To give the web se ``` The output is the same welcome page. The name resolves only from the `default` network. + + This record holds an address, so update it if Compute replaces the web server's Instance. For names that follow an Instance, see [Find services by name](/domain-dns/private-dns-zones#find-services-by-name). diff --git a/domain-dns/private-dns-reference.mdx b/domain-dns/private-dns-reference.mdx index df08818..0ecd755 100644 --- a/domain-dns/private-dns-reference.mdx +++ b/domain-dns/private-dns-reference.mdx @@ -27,7 +27,7 @@ Both [runtime classes](/compute/runtimes/overview), `general-purpose` and `unike ## Names - Every network's managed zone is `datum.internal`. Each network has its own copy. -- Compute publishes Instances at `.instances.datum.internal`. Compute owns every name at or below `instances.datum.internal`, so you can't add records there or create a zone that covers it. +- Compute publishes Instances at `.instances.datum.internal`. Long or unusual Instance names are shortened, so read the exact name from the Instance's `status.dns`. Compute owns every name at or below `instances.datum.internal`, so you can't add records there or create a zone that covers it. - Use full names. Don't rely on short names such as `web-default-us-central-1-0`: they depend on search domains that can differ between Instances. - Zone domains use lowercase letters, digits, and hyphens, have at least two labels, and are at most 253 characters long. - Record names are relative to their zone, such as `api`, or fully qualified. `@` means the zone's own domain. Names aren't case-sensitive. @@ -38,6 +38,7 @@ Both [runtime classes](/compute/runtimes/overview), `general-purpose` and `unike - `ALIAS` and `SOA` records aren't supported. Datum manages each private zone's `SOA` record. - A zone's own domain (`@`) can't be a `CNAME`. - A `CNAME` can't share a name with another record type. +- Point a `CNAME` at a name in the same zone. A `CNAME` that points into another zone, or at a public name, may return only the `CNAME`, which most apps can't follow. - An `SRV` target must be a name with `A` or `AAAA` records in the same zone. - If two record sets have the same name and type, the older one keeps it, and the newer one isn't accepted. - A record set is accepted or rejected as a whole. One invalid record keeps every name in it from resolving. diff --git a/domain-dns/private-dns-zones.mdx b/domain-dns/private-dns-zones.mdx index 4772445..c8d1cd5 100644 --- a/domain-dns/private-dns-zones.mdx +++ b/domain-dns/private-dns-zones.mdx @@ -33,7 +33,7 @@ The output is similar to the following: ```text CONTEXT NETWORK ZONE -dns-default-90c5adf1dd1c default managed-c2de65a88ad9 +dns-default-4f1c2a9e7b30 default managed-8d03b6e1f2a4 ``` Use `CONTEXT` when you associate a zone with the network, and `ZONE` when you add records to the network's `datum.internal` zone. @@ -94,7 +94,7 @@ spec: dnsZoneRef: name: corp-internal resolverContextRef: - name: dns-default-90c5adf1dd1c + name: dns-default-4f1c2a9e7b30 ``` To share the zone with another network, create another association that names that network's context. To stop a network from resolving the zone, delete its association. The zone and its records stay. @@ -138,7 +138,7 @@ Private zones support `A`, `AAAA`, `CNAME`, `TXT`, `MX`, `SRV`, `CAA`, `NS`, `PT Use one of the following, depending on what you need: - **One Instance:** use its Compute name, such as `web-default-us-central-1-0.instances.datum.internal`. Read it from the Instance's `status.dns`. See [Private DNS for Compute](/compute/private-dns). -- **A stable name for a service:** create a `CNAME` in your own zone that points at an Instance name, such as `api.corp.internal` → `web-default-us-central-1-0.instances.datum.internal.`. Update the `CNAME` when you replace the Instance. +- **A stable name for a service:** add a `CNAME` to the network's `datum.internal` zone that points at an Instance name, such as `api.datum.internal` → `web-default-us-central-1-0.instances.datum.internal.`. Update the `CNAME` when you replace the Instance. - **Several Instances behind one name:** create `AAAA` records for each address under one name, as in the example above. Clients pick an address from the answer. DNS doesn't check whether each address is healthy. ## Check status diff --git a/domain-dns/troubleshooting.mdx b/domain-dns/troubleshooting.mdx index b6911f8..39ada65 100644 --- a/domain-dns/troubleshooting.mdx +++ b/domain-dns/troubleshooting.mdx @@ -90,6 +90,7 @@ Work through these checks in order, from an Instance on the network: 4. **Check the Instance.** An Instance name exists only while the Instance is ready. Run `datumctl get instance INSTANCE_NAME -o yaml` and check `status.dns`. If its condition isn't `Ready`, the reason says why. 5. **Check the zone.** Run `datumctl get dnszone ZONE_NAME`. If `PROGRAMMED` isn't `True`, wait a minute and check again. A zone that isn't associated with any network shows no status. 6. **Check the record or association.** See the next section. +7. **Check `CNAME` targets.** A `CNAME` that points into another zone may return only the `CNAME`. Point it at a name in the same zone, or use `AAAA` records. ### A record set or association isn't accepted @@ -98,6 +99,8 @@ Read the reason: ```bash datumctl get dnsrecordset RECORDSET_NAME \ -o jsonpath='{.status.conditions[?(@.type=="Accepted")].reason}' +datumctl get dnszoneassociation ASSOCIATION_NAME \ + -o jsonpath='{.status.conditions[?(@.type=="Accepted")].reason}' ``` | Reason | What it means | What to do | @@ -108,10 +111,10 @@ datumctl get dnsrecordset RECORDSET_NAME \ | `MissingSRVTargetAddress` | An `SRV` target has no `A` or `AAAA` record in the same zone. | Add an address record for the target in the zone. | | `ConflictingTTL` | One name in the record set has two different TTLs. | Use one TTL for each name. | | `InvalidRecord`, `InvalidTTL` | A value doesn't match its record type, or the TTL is out of range. | Fix the value. | -| `ApexConflict` | Another zone with the same domain is already associated with this network. | Delete one of the two associations. | -| `InvalidReference` | The association's zone or network context was deleted and replaced. | Delete the association and create it again. | +| `ApexConflict` | Another zone with the same domain is associated with this network. The association whose name sorts first keeps the domain. | Delete the association you don't want. | +| `InvalidReference` | The association's zone or DNS context doesn't exist, was deleted, or was replaced by a new one with the same name. | Check both names. If they exist, delete the association and create it again. | -A request can also be refused when you apply it. A message that starts with `ReservedScope:` means the same as the reason above. `private zone lookup` or `resolver context lookup` errors mean the zone or DNS context you named doesn't exist in the project. +A request can also be refused when you apply it. A message that starts with `ReservedScope:` means the same as the reason above. A `not found` error, or one that mentions `private zone lookup` or `resolver context lookup`, means the zone or DNS context you named doesn't exist in the project. ### A name returns an old answer From ca3dd446eb6765a14535efafa15ed6701858caaf Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Tue, 3 Nov 2026 20:34:27 -0600 Subject: [PATCH 3/5] docs: drop the zone class from private zone examples Private zones no longer need a zone class. --- domain-dns/private-dns-quickstart.mdx | 1 - domain-dns/private-dns-zones.mdx | 1 - 2 files changed, 2 deletions(-) diff --git a/domain-dns/private-dns-quickstart.mdx b/domain-dns/private-dns-quickstart.mdx index 54f2ebd..19d2a69 100644 --- a/domain-dns/private-dns-quickstart.mdx +++ b/domain-dns/private-dns-quickstart.mdx @@ -102,7 +102,6 @@ To give the web server a name you choose, create a private zone of your own, att spec: domainName: corp.internal visibility: Private - dnsZoneClassName: private-coredns --- apiVersion: dns.networking.miloapis.com/v1alpha1 kind: DNSZoneAssociation diff --git a/domain-dns/private-dns-zones.mdx b/domain-dns/private-dns-zones.mdx index c8d1cd5..f9c0c76 100644 --- a/domain-dns/private-dns-zones.mdx +++ b/domain-dns/private-dns-zones.mdx @@ -72,7 +72,6 @@ metadata: spec: domainName: corp.internal visibility: Private - dnsZoneClassName: private-coredns ``` Keep the following points in mind: From 81d231abaf6b1477ea83b3c701115a2b1e3ecccb Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Wed, 4 Nov 2026 14:09:05 -0600 Subject: [PATCH 4/5] docs: soften Private DNS isolation and limit wording Describe private zone visibility only as association-based, and say what happens past 256 zones on a network, since that isn't refused yet. --- domain-dns/private-dns-reference.mdx | 4 ++-- domain-dns/private-dns-zones.mdx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/domain-dns/private-dns-reference.mdx b/domain-dns/private-dns-reference.mdx index 0ecd755..6102511 100644 --- a/domain-dns/private-dns-reference.mdx +++ b/domain-dns/private-dns-reference.mdx @@ -47,7 +47,7 @@ Both [runtime classes](/compute/runtimes/overview), `general-purpose` and `unike | Limit | Value | | --- | --- | -| Private zones a network can resolve, including `datum.internal` | 256 | +| Private zones a network can resolve, including `datum.internal` | 256. Keep to this or fewer: a 257th association isn't refused yet, but the network then stops picking up DNS changes. | | Zone domain length | 253 characters | | Default TTL of a record you create | 300 seconds | | TTL of an Instance name | 30 seconds | @@ -65,7 +65,7 @@ Publication is asynchronous. Wait for the status conditions described in [Check ## Privacy -- A private name resolves only from networks that the zone is associated with. Other networks, other projects, and the internet get no answer. +- A private name resolves only from networks that its zone is associated with. Private names aren't published to public DNS. - A network resolves another network's zone only through an association that you create. - Resolving a name doesn't grant network access. See [Traffic and connecting](/galactic-vpc/networks/traffic). - Public names keep resolving through the same resolvers. diff --git a/domain-dns/private-dns-zones.mdx b/domain-dns/private-dns-zones.mdx index f9c0c76..a21ca21 100644 --- a/domain-dns/private-dns-zones.mdx +++ b/domain-dns/private-dns-zones.mdx @@ -18,7 +18,7 @@ Manage these resources with `datumctl apply`, `datumctl get`, and `datumctl dele | Zone association | A `DNSZoneAssociation` that lets one network resolve one private zone. | | Record set | A `DNSRecordSet` that holds records in a zone. | -A private zone resolves only from the networks it's associated with. Networks never see each other's private zones, even when the zones have the same name. +A private zone resolves only from the networks it's associated with. Two networks can each have a zone with the same domain, and each gets its own answers. ## Find a network's DNS context and zone From 8f8f36914053fbe403aa9bb2e565fa199707bef7 Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Wed, 4 Nov 2026 14:14:04 -0600 Subject: [PATCH 5/5] docs: state the zone label rule exactly --- domain-dns/private-dns-reference.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-dns/private-dns-reference.mdx b/domain-dns/private-dns-reference.mdx index 6102511..ca659c4 100644 --- a/domain-dns/private-dns-reference.mdx +++ b/domain-dns/private-dns-reference.mdx @@ -29,7 +29,7 @@ Both [runtime classes](/compute/runtimes/overview), `general-purpose` and `unike - Every network's managed zone is `datum.internal`. Each network has its own copy. - Compute publishes Instances at `.instances.datum.internal`. Long or unusual Instance names are shortened, so read the exact name from the Instance's `status.dns`. Compute owns every name at or below `instances.datum.internal`, so you can't add records there or create a zone that covers it. - Use full names. Don't rely on short names such as `web-default-us-central-1-0`: they depend on search domains that can differ between Instances. -- Zone domains use lowercase letters, digits, and hyphens, have at least two labels, and are at most 253 characters long. +- Zone domains use lowercase letters, digits, and hyphens, with each label starting and ending with a letter or digit. They have at least two labels and are at most 253 characters long. - Record names are relative to their zone, such as `api`, or fully qualified. `@` means the zone's own domain. Names aren't case-sensitive. - A private zone hides the public records of the same domain from the networks it's associated with.