I run Incus on my homelab as the provider for Cluster API workloads.

The host is a mini PC we rarely move, so I treated its LAN IP as settled until it was not. Another network gives it a new address every time we plug in. At home, a router reboot can do the same thing through DHCP. Incus does not follow either change on its own.

That address lives in its config on disk, not whatever IP the host has right now. When the IP changes, the old one may be gone, but incusd still tries to bind to it. It fails at startup with a bind error instead of moving on to the new one. I hit this after a router reboot gave the mini PC a new IP, but the same recovery applies when it lands on a new network entirely.

Set core.https_address

If the daemon is still up, the documented fix is straightforward. Set core.https_address, the server config key that controls where the API listens:

incus config set core.https_address 192.168.1.37:8443

The command goes through the running daemon’s API and updates Incus’s global config. incusd picks up the new bind address on the next restart.

Recover with incusd cluster edit

incus config set only works if the daemon is already listening. When Incus is still configured for a dead address, incusd never gets far enough to accept the change:

systemctl status incus.service
Active: activating (start-post) (Result: exit-code)

Aug 16 22:08:58 incusd[458837]: level=error msg="Failed to start the daemon" err="Bind network address: listen tcp 192.168.1.14:8443: bind: cannot assign requested address"

cannot assign requested address means the IP in the bind call is not configured on any local interface anymore.

Even on a single node install, Incus keeps config and state in a one-member cowsql database, Canonical’s dqlite fork. That member has a raft address of its own, read from local disk before the API comes up. incus config set has nothing to talk to yet, so the fix has to happen offline, on disk, before incusd tries to start again.

Incus ships a recovery command for exactly this: incusd cluster edit. We must stop the service first:

systemctl stop incus.service incus.socket
incusd cluster show
members:
  - id: 1
    name: 192.168.1.14
    address: 192.168.1.14:8443
    role: voter

That is the stored raft member address. incusd cluster edit opens it as YAML, either in $EDITOR on a TTY or read straight from stdin.

printf 'members:\n  - id: 1\n    name: 192.168.1.14\n    address: 192.168.1.37:8443\n    role: voter\n' \
  | incusd cluster edit

incusd cluster show
members:
  - id: 1
    name: 192.168.1.14
    address: 192.168.1.37:8443
    role: voter

The name field is just a raft label, not something resolved, so it does not need to match address. I left name alone and only changed address.

Then bring the service back:

systemctl start incus.socket incus.service
systemctl is-active incus.service
active

Update the incus remote URL

Once the daemon is back, incus list can still fail even though the server is healthy. I ran into this right after fixing the raft address:

incus list
Error: Get "https://192.168.1.14:8443/1.0": no route to host

The incus CLI keeps its own copy of the address in a remote entry under ~/.config/incus. Nothing on the server side updates that for us. Point it at the new address separately:

incus remote set-url local-https https://192.168.1.37:8443
incus list
+-------------------------+---------+
|          NAME           |  STATE  |
+-------------------------+---------+
| timbernetes-svqb9-6vlt6 | STOPPED |
+-------------------------+---------+

Updating the IP is not one knob. We had to fix the raft member address on disk, core.https_address in the config database, and the remote URL the incus CLI keeps under ~/.config/incus.

While incusd is running, incus config set is enough for core.https_address. When the daemon will not start, the raft address has to be patched with incusd cluster edit first. That is what I ran into after DHCP handed the mini PC a new lease.

Notes

  • core.https_address is the normal knob when the daemon is running. incus config set updates the config database and survives restart.
  • When incusd will not bind, check the raft member address with incusd cluster show and patch it offline via incusd cluster edit. incus config set has no API to talk to yet.
  • The incus CLI remote under ~/.config/incus is separate from server config. Update it with incus remote set-url or incus list keeps hitting the old IP.
  • To avoid a repeat, reserve a static DHCP mapping on the router, or bind core.https_address to 0.0.0.0:8443 (or a stable loopback or Tailscale address if remote access is already tunneled) so the next lease renewal does not pin a dead IP to disk again.