Docs / Networking

DNS and the WireGuard mesh

How guests get names without anyone writing a record, how those names reach across hosts, and the two settings on a network that decide whether either happens.

Two pieces of machinery that are usually described together because they are only useful together: the mesh makes a guest on one host reachable from another, and DNS makes it reachable by name.

Both are derived rather than configured. You do not write DNS records or exchange WireGuard keys; you declare a couple of properties on a network and the rest follows.

Names come from the bridge

A guest’s name is not a DNS setting on the guest. It comes from the network the guest is attached to: each bridge carries a DNS search domain, and a guest with an address on that bridge gets guestname.thatdomain.

hoster network create --name hoster-internal --type internal \
  --subnet 10.0.5.0/24 --bridge-address 10.0.5.254 \
  --dns-search-domain hoster.lan
hoster network list

So a VM called web-01 on a bridge whose domain is hoster.lan answers to web-01.hoster.lan, and the record appears without anyone creating it.

A bridge with no search domain produces no records at all. Record generation skips any interface whose bridge has no domain — silently, because there is nothing to build a name from. If guests are not resolving, this is the first thing to check.

It is also the thing you cannot fix from the CLI: the search domain is a create-time property, and hoster network set only changes STP. See changing a network afterwards.

Records are built per enabled interface with an address, for both VMs and microVMs, and they carry the bridge they came from. A guest with two NICs on two bridges gets a name in each domain.

Replicas do not get names

Guests marked as backups are skipped. That is deliberate and worth understanding: a replicated copy sitting on another host is not the machine, and giving it the same name would mean traffic could land on a copy that is not being written to.

What the DNS server does

HosterDNS runs on every node, serving the records above and forwarding everything else. Its configuration lives at /opt/hoster/conf/dns_config.json:

Default
Listen 0.0.0.0:53
Upstream 1.1.1.1:53, 8.8.8.8:53
Cache TTL 120 seconds
Records generated, not hand-written

Because it listens on port 53 on all addresses, you can point your own resolvers or a company DNS server at a node and get name resolution for every guest — without creating a record per VM anywhere else.

On Ubuntu the installer disables systemd-resolved’s stub listener specifically so HosterDNS can bind port 53. If you re-enable it, DNS stops working, and the symptom looks like a Hoster problem rather than a resolver conflict.

Regenerating and restarting, when you have changed something underneath it:

hoster debug generate-config hoster_dns   # rebuild the record set
hoster debug generate-config resolv-conf  # rebuild the host's own resolv.conf
hoster service reload hoster_dns          # pick it up
hoster service restart hoster_dns         # or restart outright

Both files are also regenerated automatically on every boot — see Quick start, stage 2.

The mesh

Each host keeps its own guest subnet. On a single node that is the whole story. In a cluster, a full WireGuard mesh is built between every pair of nodes and routes between those subnets, so a guest on 10.0.1.0/24 reaches a guest on 10.0.2.0/24 as if they were adjacent.

The mesh regenerates itself when membership or configuration changes. There is no key exchange to perform, no peer list to maintain, and no configuration file to edit.

hoster cluster get wireguards                     # the mesh as the cluster sees it
hoster cluster debug wireguard-ping [hostname]    # reachability from here (--all for all-to-all)
hoster cluster debug wireguard-reload [hostname]  # regenerate and reload

wireguard-ping --all is the one to run when something cannot reach something else: a full matrix tells you whether you are looking at one broken link or one broken node.

A network has to opt in to being routed

Marking a network as routed over the mesh is a property of the network:

hoster network create --name hoster-internal --type internal \
  --subnet 10.0.5.0/24 --bridge-address 10.0.5.254 --route-over-wg

Without it, a network is local to its host and the mesh will not carry it. This is the second thing to check when a guest on one host cannot reach a guest on another and the mesh itself looks healthy.

Like the search domain, this is set when the network is createdhoster network set cannot add it later. See Networks.

Advertised endpoints

Each node advertises how its peers should reach it. Usually that is inferred correctly. When it is not — a node behind NAT, or one with several addresses where the wrong one is chosen — override it:

hoster host wireguard settings get
hoster host wireguard settings set --endpoint-address 198.51.100.20 --listen-port 51820
hoster host wireguard settings clear-endpoint
hoster host wireguard settings clear-port

The clear- commands drop the override and return to the inferred value, which is what you want after the underlying reason goes away. get takes --json.

Cluster-wide names

In a cluster, records are built from the cluster’s own view rather than only from the local host, so every node can resolve every guest — including guests it is not running. The cluster’s search domain comes from the host configuration.

This is what turns the mesh from a routing detail into something usable: a guest moves between hosts and its record follows it, so nothing that referred to it by name has to be told.

Screenshot

Take a screenshot of a `dig` or `nslookup` against a node resolving a guest on a *different* host by name, with the answer visible — the shortest possible demonstration that cluster DNS works.

When names do not resolve

In order, because each one is cheap to check and rules out the next:

  1. Does the bridge have a search domain? No domain, no records.
  2. Does the guest have an enabled interface with an address? Records need both.
  3. Is the guest a backup? Replicas are skipped deliberately.
  4. Is systemd-resolved holding port 53? On Ubuntu, only if it was re-enabled.
  5. Has the config been regenerated since the guest changed? hoster debug generate-config hoster_dns.
  6. For cross-host names, is the mesh up and is the network marked --route-over-wg?

There is no hoster dns command. DNS is operated through hoster debug generate-config hoster_dns and hoster service reload|restart hoster_dns — a generation command under debug and a service command elsewhere, for something that is neither debugging nor unusual to touch. Worth knowing when you go looking for it under an obvious name and find nothing. See Troubleshooting a node.

Next

  • Networks — where the search domain and --route-over-wg on this page are actually set.
  • Firewall — the other half of what a network does, including publishing a guest to the outside.
  • Clustering — where the mesh and cluster-wide names come from.

Something unclear on this page?

Ask about this specific page and we will come back to you. Your question arrives with a link to it, so you do not have to describe where you were.