How to change cluster.https_address ? -> Error: Changing cluster.https_address is currently not supported

I was planning to move my cluster interfaces to dedicated non-routable vlan and found that the cluster.http_address' value has been changed to the 'core.http_address value when I modified cluster node IPs using incus admin cluster edit. So this change blocked access to the API from remote clients. I tied to change config option cluster.http_address but got the error: Error: Changing cluster.https_address is currently not supported..

Comment in the source code says: We currently don't allow changing the cluster.https_address once it's set.. However, I did not intend to change cluster.http_address when I changed cluster nodes IPs, but rhe incus did this for me. Is there any way to safely move cluster nodes to another subnet without changing the cluster.http_address ?

Ok, this is just from observation so someone please correct me where I’m wrong …

As I understand it, the cluster.https_address is what the cluster nodes use to talk to each other, and the core.https_address is the address the UI / API will listen on. When you configure the cluster, you tell it which interface / address to listen on, which should set the cluster.https_address.

If you were to change this, you would need to change all three nodes as the same time, which in itself might prove problematic. After working with OVN recently, if Incus’s Raft is anything like OVN, it will not be happy with IP changes and break horribly. So even if you could, it might not be a good idea to try …

I recently was faced with the same issue. My solution was to copy all instances to an incus archive node, set up a new cluster from scratch on the desired IP addresses, then copy the instances from the archive node back to the new cluster. In reality I had two clusters running in parallel and subtracted a node from one then added to the other. Process worked Ok, albeit a little time consuming.

If it helps, I use this to backup all my nodes every hour or so, same principle to archive everything off to another node;

#!/usr/bin/env bash

self=borg # the node we're running on
project=cluster # the backup projec to take the copies

echo --- Storage --------- $(date) ------------

for i in `incus storage volume ls local -c nL -fcsv type=custom|grep -v "/"`
do
	inst=`echo $i|cut -d"," -f1`
	host=`echo $i|cut -d"," -f2`
	echo "Copying custom (docker) Image :: ${inst} from ${host}"
	incus storage volume copy local/${inst} ${self}:local/${inst} --target-project=${project} --refresh
done

echo --- Containers --------- $(date) ------------

for i in `incus list -c nL -f csv,noheader|tr ' ' '_'`
do
	inst=`echo $i|cut -d"," -f1`
	host=`echo $i|cut -d"," -f2`
	echo "Copying :: ${inst} from ${host} to ${self}"
	incus copy $inst ${self}:$inst --target-project=${project} --refresh --stateless
done

echo ------------ $(date) ------------

Yeah, that’s right.

It’s not impossible to change it, but it’s a bit involved…

You basically need to:

  • Completely stop Incus on all servers (both incus.service and incus.socket)
  • Write a /var/lib/incus/database/patch.global.sql to change the address in the nodes table
  • Modify /var/lib/incus/database/local.db on all servers for the new address
  • Run incus admin cluster edit on all servers to change the address
  • Start Incus back on all servers

I’ve done it before in some situations for some customers, but it’s definitely not a particularly smooth or downtime-free process which is why we don’t have a way to do it online through the API.

Wouldn’t this work as a (mostly, modulo the time needed to migrate an instance) downtime-free variant:

  • evacuate one cluster node in the old cluster
  • remove that node from the cluster and wipe the incus configuration on that node
  • create a new cluster on that node with the changed address
  • start the new cluster (even if it just has one node yet)
  • move the evacuated vms/containers in the old cluster over to the new cluster (should not overload the node because it is exactly the same load as before)
  • repeat until all nodes are migrated
    • evacuate another node in the old cluster
    • stop incus, wipe config, add node to the new cluster
    • move the evacuated instances of the old cluster to the new node

Fortunately, this happened on staged cluster and I did not lost any data, except my time :slight_smile:
I agree with @oddjobz and @rkunze that the safest way to changing cluster nodes IP is the removing node form the cluster->changing IP->joining node to the cluster operation.

However, the real problem is the unexpected behavior. This part is not clearly documented and it does not say that the cluster.http_address is always the node address defined in incus admin init and changing the node address will automatically change API listener on this node.

P.S.
In fact I have to rebuild my staged cluster due to lost incus synchronization with the OVN. The Incus daemons did stuck on start and ovs-central flooded with warnings about non-exist bridges created by the incus in the past.

P.S. … ovn …

Yeah, I’ve seen a lot of this over the last month or so. Once your OVN raft is broken the only obvious way to recover is delete the data and start again. Then you’ve got the problem of Incus ovn objects being out of sync.

However :slight_smile:

In many cases I’ve found that the ovn database “would” be fine if only the nodes would sync with each other, and doing things like changing the node address, or maybe reinstalling a couple of nodes without deleting them first hence leaving phantoms and losing quorum, it’s very easy to do in testing. I did eventually discover it’s relatively easy to recover this (!)

(make backups, adjust paths, ports etc for your distro, this is debian)

  • Close down all but one node
  • Delete /var/lib/ovn/* and /var/lib/openvswitch/* # on all but one node
  • cd /var/lib/ovn # (on the remaining node)
  • ovsdb-tool cluster-to-standalone ovndb_db.db nb.standalone.db
  • ovsdb-tool cluster-to-standalone ovnsb_db.db sb.standalone.db
  • (same for ic db’s if you have them)
  • ovsdb-tool create-cluster ovnnb_db.db OVN_Northbound tcp:ip:6641 nb.standalone.db
  • ovsdb-tool create-cluster ovnsb_db.db OVN_Southbound tcp:ip:6642 sb.standalone.db
  • (same for ic db’s if you have them)
  • Restart other nodes, assuming you supply host and port params on the command line rather than relying on the DB, they should just connect up as they would on a new db

In this instance you don’t lose sync with Incus which makes recovery Soooo much easier.
:slight_smile:

Thank you for your suggestion. :slight_smile: However, I use microovn as ovn provider. I will do experiment later to check whether I can reset it :).

I did reproduce this issue again in different manner.

  • I built the cluster in the network 192.168.182.0/23. So cluster nodes have IP values in core.http_address and cluster.http_address from that subnet.
  • I changed core.http_address to IP from 172.16.120/24 subnet on each node.
  • incus automatically updated cluster node IPs in the config (and in the raft_nodes table as well) to the new values.
members:
- id: 1
  name: vm-01
  address: 172.16.120.1:8443
  role: voter
members:
- id: 2
  name: vm-02
  address: 172.16.120.2:8443
  role: voter
members:
- id: 3
  name: vm-03
  address: 172.16.120.3:8443
  role: voter

Everything looks good, however, nodes lost communication to each other.
Running the incus daemon in debug mode I see that the node tries to use use cluster.http_address instead of core.http_address as a node IP to handover member role and fails.

INFO   [2025-06-12T18:04:44Z] Starting up                                   mode=normal path=/var/lib/incus version=6.13
INFO   [2025-06-12T18:04:44Z] Current process idmap:
INFO   [2025-06-12T18:04:44Z]  - u 0 0 4294967295
INFO   [2025-06-12T18:04:44Z]  - g 0 0 4294967295
INFO   [2025-06-12T18:04:44Z] Selected idmap:
INFO   [2025-06-12T18:04:44Z]  - u 0 1000000 1000000000
INFO   [2025-06-12T18:04:44Z]  - g 0 1000000 1000000000
INFO   [2025-06-12T18:04:44Z] Kernel features:
INFO   [2025-06-12T18:04:44Z]  - closing multiple file descriptors efficiently: yes
INFO   [2025-06-12T18:04:44Z]  - netnsid-based network retrieval: yes
INFO   [2025-06-12T18:04:44Z]  - pidfds: yes
INFO   [2025-06-12T18:04:44Z]  - pidfds for threads: no
INFO   [2025-06-12T18:04:44Z]  - core scheduling: yes
INFO   [2025-06-12T18:04:44Z]  - uevent injection: yes
INFO   [2025-06-12T18:04:44Z]  - seccomp listener: yes
INFO   [2025-06-12T18:04:44Z]  - seccomp listener continue syscalls: yes
INFO   [2025-06-12T18:04:44Z]  - seccomp listener add file descriptors: yes
INFO   [2025-06-12T18:04:44Z]  - attach to namespaces via pidfds: yes
INFO   [2025-06-12T18:04:44Z]  - safe native terminal allocation: yes
INFO   [2025-06-12T18:04:44Z]  - unprivileged binfmt_misc: yes
INFO   [2025-06-12T18:04:44Z]  - unprivileged file capabilities: yes
INFO   [2025-06-12T18:04:44Z]  - cgroup layout: cgroup2
INFO   [2025-06-12T18:04:44Z]  - idmapped mounts kernel support: yes
INFO   [2025-06-12T18:04:44Z] Instance type operational                     driver=lxc type=container
WARNING[2025-06-12T18:04:44Z] Instance type not operational                 driver=qemu err="KVM support is missing (no /dev/kvm)" type=virtual-machine
INFO   [2025-06-12T18:04:44Z] Initializing local database
DEBUG  [2025-06-12T18:04:44Z] Refreshing local trusted certificate cache
INFO   [2025-06-12T18:04:44Z] Set client certificate to server certificate  fingerprint=ebfde364a490746bf66cddc0d7b3709b105a7cc07edd61e4f03141281ddea67e
DEBUG  [2025-06-12T18:04:44Z] Initializing database gateway
INFO   [2025-06-12T18:04:44Z] Loading daemon configuration
INFO   [2025-06-12T18:04:44Z] Binding socket                                socket="192.168.182.33:8443" type="cluster socket"
INFO   [2025-06-12T18:04:44Z] Binding socket                                socket=/var/lib/incus/guestapi/sock type="devIncus socket"
INFO   [2025-06-12T18:04:44Z] Binding socket                                socket="172.16.120.3:8443" type="REST API TCP socket"
INFO   [2025-06-12T18:04:44Z] Binding socket                                socket="192.168.182.33:8443" type="cluster socket"
INFO   [2025-06-12T18:04:44Z] Binding socket                                socket=/var/lib/incus/unix.socket type="REST API Unix socket"
INFO   [2025-06-12T18:04:44Z] Initializing global database
INFO   [2025-06-12T18:04:44Z] Connecting to global database
DEBUG  [2025-06-12T18:04:44Z] Dqlite connected outbound                     local="172.16.120.3:33864" name=dqlite remote="172.16.120.1:8443"
DEBUG  [2025-06-12T18:04:44Z] Dqlite: attempt 1: server 172.16.120.1:8443: connected
INFO   [2025-06-12T18:04:44Z] Connected to global database
DEBUG  [2025-06-12T18:04:44Z] Database error                                err="Failed to update cluster member version info: updated 0 rows instead of 1"
ERROR  [2025-06-12T18:04:44Z] Failed to start the daemon                    err="Failed to initialize global database: failed to ensure schema: Failed to update cluster member version info: updated 0 rows instead of 1"
INFO   [2025-06-12T18:04:44Z] Starting shutdown sequence                    signal=interrupt
INFO   [2025-06-12T18:04:44Z] Handing over cluster member role              address="192.168.182.33:8443"
DEBUG  [2025-06-12T18:04:44Z] Connecting to a remote Incus over HTTPS       url="https://172.16.120.1:8443"
DEBUG  [2025-06-12T18:04:44Z] Sending request to Incus                      etag= method=POST url="https://172.16.120.1:8443/internal/cluster/handover"
DEBUG  [2025-06-12T18:04:44Z]
        {
                "address": "192.168.182.33:8443"
        }
WARNING[2025-06-12T18:04:44Z] Could not handover member's responsibilities  err="No dqlite node has address 192.168.182.33:8443: %!w(<nil>)"
DEBUG  [2025-06-12T18:04:44Z] Cancel ongoing or future gRPC connection attempts
DEBUG  [2025-06-12T18:04:44Z] Cancel ongoing or future gRPC connection attempts
DEBUG  [2025-06-12T18:04:44Z] Stop database gateway```