DHCP & DNS
Stratum runs its own DHCP server and authoritative DNS inside the agent process — there are no external daemons to configure or keep in sync. Both serve whatever scopes and zones you configure; creating a network does not configure them for you. Addresses are handed to a network's endpoints (an endpoint is an IP ↔ MAC profile). This page covers pool configuration, static leases, DHCP relay, and DNS zone management.
How a workload gets its address
Four things are consulted, in this order, and the first that applies wins:
- An address it already holds. A workload that comes back before its lease
- A reservation for its hardware address, if you have made one.
- An offer it was already given, so a client that asks repeatedly is not
- The next free address in the network's range.
Adding a reservation does not move a workload that already has a lease. Step 1 is checked first, so the workload keeps its current address until that lease expires or you release it. If you need the reservation to take effect immediately, release the existing lease after making it.
Offering and committing are separate: the address is held tentatively when offered and only becomes a lease once the client accepts it. An offer a client never takes up is returned to the pool automatically rather than leaking.
Reservations require a DHCP scope covering that subnet. Creating a network does not create one — see DHCP pools below. If you see *"no address pool configured for this network"* when reserving, either no scope covers that address or none is configured at all.
How a name is resolved
A query is answered by the first of these that applies:
- Blocked names are refused outright, whatever else would have answered.
- Your own zones. If the name is in a zone this node is authoritative for,
- Upstream resolvers, for everything else — but only if the asking client is
- With no upstreams configured and no local answer, the reply is "no such name".
The practical consequence of step 2: once you create a zone, this node owns
every name under it. A name you have not added returns "does not exist"
rather than falling through to the public internet. That is what makes an
internal zone trustworthy — but it means creating example.com internally
stops you resolving the real one.
Records can also be scoped to a source subnet, so the same name answers differently depending on who is asking — an internal client gets the internal address while everyone else gets the public one, from one zone.
DHCP pools
network create does create the address pool for the subnet. What it does not
create is the DHCP scope that binds serving to that pool — and until a scope
exists the DHCP server has no address to offer for that subnet, pool or no pool.
That failure is quiet by design. RFC 2131 says a server with nothing to offer must not reply, so the client simply retries: its interface shows DHCP going out and nothing coming back, which looks identical to a broken L2 path. The agent logs a warning naming the client — *"no address pool for this client's network"* — the first time it happens, then holds it down so a fresh node is not buried in noise. If a workload never gets an address, search the agent log for that line before you go looking at the bridge.
Once a pool exists and a scope is bound to it, leases are allocated from the IPAM pool and released back to it on expiry.
sudo cenvero-str-ctl network create \
--name db-net \
--cidr 10.30.0.0/24 \
--gateway 10.30.0.1
network create accepts --name, --cidr, and optionally --gateway, --vlan, and --tenant. (Only IPv4 CIDRs are supported.)
The DHCP server tells clients:
| Option | Value |
|---|---|
| Subnet mask | derived from --cidr |
| Default gateway | --gateway |
| DNS server | the agent's gateway IP (same as --gateway) |
| Lease time | the server's lease TTL |
No domain-name or domain-search option is offered, so configure a search domain on the workload itself if you need short names to resolve.
Reservations (static leases)
Pin a specific IP to a MAC address so an endpoint always gets the same address. The address is matched to its pool from the IP itself, so the reservation does not name a network:
sudo cenvero-str-ctl dhcp reserve \
--mac 52:54:00:de:ad:01 \
--ip 10.30.0.10 \
--hostname db-primary
The MAC and IP may also be given positionally, which is handy for one-liners:
sudo cenvero-str-ctl dhcp reserve 52:54:00:de:ad:01 10.30.0.10 --hostname db-primary
A reservation pins the address only — it does not create a DNS record. To make the hostname resolve, add a DNS record with dns record add (or the API); see Adding manual DNS records below.
List the reservations you have made:
cenvero-str-ctl dhcp reservations
Inspecting leases
dhcp leases dumps every active lease the server has handed out:
cenvero-str-ctl dhcp leases
To release a reservation early (e.g. before re-provisioning a workload), identify it by MAC:
sudo cenvero-str-ctl dhcp release --mac 52:54:00:ab:01:02
The MAC may be positional here too — dhcp release 52:54:00:ab:01:02 is equivalent.
Relayed clients
Clients on a segment that has no directly-attached agent — for example a physical VLAN reached through an external DHCP relay — are supported. Point your existing relay agent at the node, and the relayed request is matched to the right scope by the relay's giaddr (the gateway/relay address the relay stamps into the request). Add a scope whose subnet covers that segment (see Network Services Control → Scopes) and the built-in server answers the relayed request from that scope's pool.
Authoritative DNS
The agent runs a DNS server bound to the bridge addresses (the management and user bridge IPv4 addresses) rather than every interface, so the resolver is never exposed on an untrusted NIC.
Zones are explicit: creating a network does not create a zone for it, so you choose the domain and create it yourself. Pick any name you like — a .internal suffix is a good convention for names that should never resolve publicly.
sudo cenvero-str-ctl dns zone add app-net.internal
{
"data": {
"id": 1,
"name": "app-net.internal.",
"serial": 1,
"status": "created"
},
"status": "ok"
}
The id in that response is the zone id, and every record command refers to the zone by that numeric id rather than by name. List your zones at any time to look one up:
cenvero-str-ctl dns zones
A DHCP lease does not by itself create a DNS record. To make a hostname resolve to a leased (or reserved) address, add an A record for it — it then resolves inside the zone:
web-01.app-net.internal → 10.20.0.50
db-primary.db-net.internal → 10.30.0.10
Adding manual DNS records
dns record add takes positional arguments: the zone id, the record name, its type, its value, and optionally a TTL and a source subnet.
# dns record add <zone_id> <name> <type> <value> [ttl] [source_subnet]
sudo cenvero-str-ctl dns record add 1 api A 10.20.0.55 300
sudo cenvero-str-ctl dns record add 1 services CNAME api.app-net.internal
The name is relative to the zone, so api in zone app-net.internal becomes api.app-net.internal. When you omit the TTL a default is applied. dns add is a shorthand alias for dns record add and takes exactly the same arguments.
List records for a zone by its id:
cenvero-str-ctl dns list 1
{
"data": {
"records": [
{
"id": 3,
"zone_id": 1,
"name": "api.app-net.internal.",
"type": "A",
"value": "10.20.0.55",
"ttl": 300
}
]
},
"status": "ok"
}
Omit the zone id (dns list), or use dns records, to list records across every zone.
Remove a record by its record id — the id field above, not its name:
sudo cenvero-str-ctl dns record delete 3
Delete a whole zone by its zone id with dns zone delete 1.
Upstream forwarders
Queries for names outside the local zones are forwarded to configurable upstream resolvers. Manage them at runtime with the dns forwarder commands:
# Show the active forwarders
cenvero-str-ctl dns forwarder list
# Replace the whole list
sudo cenvero-str-ctl dns forwarder set 1.1.1.1 8.8.8.8
# Add or remove one
sudo cenvero-str-ctl dns forwarder add 9.9.9.9
sudo cenvero-str-ctl dns forwarder remove 8.8.8.8
Each forwarder is an IP or IP:port (a bare IP defaults to :53). The agent tries forwarders in order and falls back to the next on timeout. dns forwarder changes take effect on the running agent; to persist them across restarts, set the dns_upstreams key:
sudo cenvero-str-ctl config set dns_upstreams "1.1.1.1,8.8.8.8"
The agent falls back to public defaults when the list is empty.
Not an open resolver. Recursion (forwarding a query upstream) is gated by a client ACL. When the ACL is unset it defaults to the private and loopback ranges (127.0.0.0/8,::1/128,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fc00::/7), so only clients inside those ranges have their queries forwarded; a query from outside the allowed set is refused rather than silently forwarded. Authoritative answers for local zones are always returned regardless of the ACL.
See also
- Networking Overview — how DHCP and DNS fit into the data plane.
- Zero-Trust Firewall — DNS traffic on port 53 must be explicitly allowed for cross-network queries.
- Configuration — how settings reach a node and what lives in the node config.