Incus/dnsmasq hostname not resolvable for static lease

incus 6.23 here. I have an issue where a container with a dynamic lease has a resolvable name under the “.incus” domain, but a static one doesn’t. I’m probably not understanding the full set of conditions required for a container name to be resolvable.

Here’s the network:

nsrc@brian-kit:~$ incus network show wifi0
config:
  bridge.external_interfaces: eno1
  ipv4.address: 100.126.0.1/22
  ipv4.dhcp.ranges: 100.126.1.0-100.126.3.254
  ipv4.nat: "true"
  ipv6.address: XXXX:XXX:XX:XXfc::1/64
  ipv6.dhcp.ranges: XXXX:XXX:XX:XXfc::1000-XXXX:XXX:XX:XXfc::1fff
  ipv6.dhcp.stateful: "true"
  ipv6.nat: "false"
  raw.dnsmasq: address=/local.nsrc.org/100.126.0.1
description: ""
name: wifi0
type: bridge
used_by:
- /1.0/instances/temp123
- /1.0/instances/unifi
- /1.0/profiles/wifi0
managed: true
status: Created
locations:
- none
project: default

There is no dns.mode configured - documentation says it should default to “managed”, and the default domain is “incus”.

On this network are two containers. unifi is a previously created container with static netplan configuration inside it, but I’ve also configured a static lease for it (outside of the DHCP pool):

nsrc@brian-kit:~$ incus config show unifi
...
devices:
  eth0:
    ipv4.address: 100.126.0.234
    name: eth0
    nictype: bridged
    parent: wifi0
    type: nic

temp123 is just incus launch images:ubuntu/24.04/cloud temp123 -p wifi0 so it has a dynamic lease.

The problem is that the containers cannot resolve unifi.incus, even though they can both resolve temp123.incus

nsrc@brian-kit:~$ incus exec -- temp123 ping -4 -c1 unifi.incus
ping: unifi.incus: Name or service not known
nsrc@brian-kit:~$ incus exec -- temp123 ping -4 -c1 temp123.incus
PING temp123.incus (100.126.2.110) 56(84) bytes of data.
64 bytes from temp123.incus (100.126.2.110): icmp_seq=1 ttl=64 time=0.024 ms
...

nsrc@brian-kit:~$ incus exec -- unifi ping -4 -c1 unifi.incus
ping: unifi.incus: Name or service not known
nsrc@brian-kit:~$ incus exec -- unifi ping -4 -c1 temp123.incus
PING temp123.incus (100.126.2.110) 56(84) bytes of data.
64 bytes from temp123.incus (100.126.2.110): icmp_seq=1 ttl=64 time=0.048 ms
...

nsrc@brian-kit:~$ dig @100.126.0.1 unifi.incus. | egrep -A1 'status:|ANSWER SECTION'
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 39778
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

nsrc@brian-kit:~$ dig @100.126.0.1 temp123.incus. | egrep -A1 'status:|ANSWER SECTION'
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7762
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
--
;; ANSWER SECTION:
temp123.incus.		0	IN	A	100.126.2.110

Both containers are running, and the leases for both are visible:

nsrc@brian-kit:~$ incus network list-leases wifi0
+----------+-------------------+------------------------+---------+
| HOSTNAME |    MAC ADDRESS    |       IP ADDRESS       |  TYPE   |
+----------+-------------------+------------------------+---------+
| *        |                   | XXXX:XXX:XX:XXfc::13b8 | DYNAMIC |
+----------+-------------------+------------------------+---------+
| temp123  | 10:66:6a:82:ac:d7 | 100.126.2.110          | DYNAMIC |
+----------+-------------------+------------------------+---------+
| temp123  |                   | XXXX:XXX:XX:XXfc::18ff | DYNAMIC |
+----------+-------------------+------------------------+---------+
| unifi    | 10:66:6a:49:c6:51 | 100.126.0.234          | STATIC  |
+----------+-------------------+------------------------+---------+
| wifi0.gw |                   | XXXX:XXX:XX:XXfc::1    | GATEWAY |
+----------+-------------------+------------------------+---------+
| wifi0.gw |                   | 100.126.0.1            | GATEWAY |
+----------+-------------------+------------------------+---------+

The dnsmasq command which incus spawns is:

dnsmasq --keep-in-foreground --strict-order --bind-interfaces --except-interface=lo \
  --pid-file= --no-ping --interface=wifi0 --dhcp-rapid-commit --no-negcache \
  --quiet-dhcp --quiet-dhcp6 --quiet-ra --listen-address=100.126.0.1 --dhcp-no-override \
  --dhcp-authoritative --dhcp-leasefile=/var/lib/incus/networks/wifi0/dnsmasq.leases \
  --dhcp-hostsfile=/var/lib/incus/networks/wifi0/dnsmasq.hosts \
  --dhcp-range 100.126.1.0,100.126.3.254,1h \
  --listen-address=XXXX:XXX:XX:XXfc::1 --enable-ra \
  --dhcp-range XXXX:XXX:XX:XXfc::1000,XXXX:XXX:XX:XXfc::1fff,64,1h \
  --dhcp-option-force=option6:dns-server,[XXXX:XXX:XX:XXfc::1] \
  -s incus --interface-name _gateway.incus,wifi0 -S /incus/ \
  --conf-file=/var/lib/incus/networks/wifi0/dnsmasq.raw -u incus -g incus

There are hosts files for both:

nsrc@brian-kit:~$ ls /var/lib/incus/networks/wifi0/dnsmasq.hosts
temp123.eth0  unifi.eth0
nsrc@brian-kit:~$ head -100 /var/lib/incus/networks/wifi0/dnsmasq.hosts/*
==> /var/lib/incus/networks/wifi0/dnsmasq.hosts/temp123.eth0 <==
10:66:6a:82:ac:d7,temp123

==> /var/lib/incus/networks/wifi0/dnsmasq.hosts/unifi.eth0 <==
10:66:6a:49:c6:51,100.126.0.234,unifi

I’m scratching my head now. Any clues as to why this isn’t resolvable?

On further searching I’ve found other projects have a similar issue, and the answer seems to be that dnsmasq won’t use these configured entries for DNS replies unless there’s an associated active client lease.

If I stop the temp123 container, I see temp123.incus still resolves, but presumably this will stop when the lease expires. (EDIT: yes, this happened)

I can confirm this explicitly: if I create a container with static allocation but don’t start it, it doesn’t resolve. But if I start it (and it is a DHCP client), then it resolves.

nsrc@brian-kit:~$ incus create images:ubuntu/24.04/cloud temp456 -p wifi0 -d eth0,ipv4.address=100.126.0.145
Creating temp456
nsrc@brian-kit:~$ dig +short @100.126.0.1 temp456.incus
nsrc@brian-kit:~$ incus start temp456
nsrc@brian-kit:~$ dig +short @100.126.0.1 temp456.incus
100.126.0.145

Therefore I think this is solved, although not the answer I was hoping for.

1 Like

You’ve correctly identified the root cause. dnsmasq in Incus only registers container hostnames in DNS when they hold an active DHCP lease and have negotiated a hostname via the DHCP protocol. Static IP assignments at the Incus NIC level bypass this handshake entirely, so no lease record exists for dnsmasq to turn into a DNS entry.

The fix is to add explicit host records via raw.dnsmasq using the host-record directive, which gives you forward and reverse DNS without requiring any active lease:

incus network set wifi0 raw.dnsmasq "address=/local.nsrc.org/100.126.0.1
host-record=mycontainer.incus,100.126.0.x
host-record=mycontainer.incus,XXXX:XXX:XX:XXfc::y"

The host-record directive creates both the A/AAAA record and the PTR record, so reverse lookups work too. You can stack multiple host-record lines in the raw.dnsmasq value using newlines.

One thing to watch: if you have both a raw.dnsmasq host-record and the container is also running and getting a DHCP lease, dnsmasq will have two entries for the same name. Usually fine, but if the IPs differ (static assignment vs lease) you can get unexpected behaviour. The cleanest approach is to use host-record only for containers that have static IPs defined at the NIC level and won’t be dynamic.

Alternatively, if you want the container to get its IP via DHCP but always the same address, set the static allocation in dnsmasq itself rather than in the NIC config. Remove the ipv4.address from the instance NIC and add a dhcp-host entry in raw.dnsmasq instead:

dhcp-host=aa:bb:cc:dd:ee:ff,mycontainer,100.126.0.x,infinite

This way the container still does DHCP, dnsmasq issues the lease, and the hostname registration happens automatically as it does for dynamic leases. Sound.

You’ve correctly identified the root cause: dnsmasq only populates its DNS records from active DHCP leases, so a statically-assigned address that never goes through a DHCP exchange won’t appear in DNS regardless of how it’s configured in the NIC profile.

The fix is to add a permanent host record via the network’s raw.dnsmasq config. This creates a DNS entry that exists independently of any lease:

incus network set wifi0 raw.dnsmasq "host-record=temp456.incus,100.126.0.145"

For multiple static containers, separate them with newlines:

incus network set wifi0 raw.dnsmasq "host-record=temp456.incus,100.126.0.145
host-record=temp789.incus,100.126.0.146"

After setting this, restart the network or the container to reload dnsmasq config:

incus network edit wifi0
# or simply reload dnsmasq by restarting the bridge

You can verify it’s working with:

dig +short @100.126.0.1 temp456.incus

The host-record directive in dnsmasq handles both forward (A) and reverse (PTR) lookups, so you’ll get bidirectional resolution without needing separate address= and ptr-record= lines.

One caveat: if a container ever requests a different IP via DHCP than what’s in raw.dnsmasq, you may end up with conflicting entries. Keeping static assignment consistent between the NIC config and the raw.dnsmasq record avoids that.