HOWTO for setting up secure/private multi-tenant Incus/IncusOS

I’ve seen posts that indicate folks are setting up secure multi-tenant Incus/IncusOS installations but the posts only give hints for for setting up pieces of one. (I’ve gleaned that I need to use OVN for networking inside a tenant’s project, for example.) It would be very useful to have a howto on setting up all the needed pieces such that the end result is secure and isolated Incus for multiple tenants.

NB:

  • I am familiar with Incus for my own use but would like to set things up for others to safely use without enabling security and privacy issues.
  • I realize that actual configuration depends upon the environment in which it would operate but it should be possible to have a getting started howto which covers the basics. (Later howtos can go into more advanced topics.)
  • I am willing to collaborate on producing a howto if one does not exist but do not yet have the knowledge to create it by myself.

For something properly isolated and somewhat self-service for tenants, you want an environment with OIDC + OpenFGA + OVN and then use restricted projects, giving at most operator access to the various tenants.

On top of that, you’ll likely want to put project limits in place too to avoid running out of resources in your environment.

I can’t figure out how to enable OVN:

$ incus network create ovntest --type=ovn network=incusbr0
Error: Failed loading network: IncusOS service "ovn" isn't currently enabled

$ incus admin os service show ovn
WARNING: The IncusOS API and configuration is subject to change

config:
  database: ""
  enabled: false
  tunnel_address: ""
  tunnel_protocol: ""
state: {}

There is documentation on how to set up OVS and OVN on Incus, but all I can find for IncusOS is the OVN reference page which is not sufficient to know how to enable OVN. It would really help if there were a tutorial. I’m willing to help write one but first I have to understand how to do it.

The only other page I’ve found is IncusOS OVN Issues. It appears that ovn-central needs to be running but where? How is it done on IncusOS? (A later post in the same topic says that Operations Center creates a meshbr0 with an internal project but meshbr0 appears to be needed when OVN is running on a cluster. Right now I am running a single node so that seems overkill even if I knew how to enable Operations Center.)

It looks like I am stuck without a little explicit hand holding.

In my understanding, it is planned that IncusOS is going to provide those services for you:

The linked tutorial cannot be used directly with IncusOS because it is an immutable OS and does not allow manually installing packages. Instead, one needs to run the additional services in containers. In my understanding, the IncusOS OVN service runs ovn-host. You need to install ovn-central in a separate container and ensure that they can talk to each other using the configuration settings.

If you have never setup OVN, I would suggest to first follow the linked tutorial on Incus (not IncusOS). If you already have IncusOS, you could install Incus inside a VM (or an Incus cluster using several VMs) and try this tutorial.

I followed instructions for the standalone guide on Debian Linux with Incus installed on top (I have a single node), and it worked for me.

It looks like IncusOS works differently.

Hey,

I got an email since you referenced my post, so I figured I’d jump in here.

I’m going to misuse a few words here but that’s because I want to phrase my response in a certain way.

IncusOS only runs the “client” OVN services. You need to set up the “server” OVN services. There are two ways to do this, I have done both and there isn’t very many pros/cons to either one.


Option 1: OVN “servers” in LXCs
Essentially what you want to do here is make sure each of your IncusOS servers has two NICs. I wasn’t able to get it to work with just one NIC.

What I did is I set up a “bridge network”, called incusbr0 and a “physical network”. The reason you need both is that one of them exposes your LXCs directly to the “outside” and the other one gives each host an IP and performs NAT.

What I did is in my “default” project, I created an LXC for each IncusOS host and pinned it to that host, that way if a node goes down, I dont risk losing my networking with it. (split-brain databases are manageable, but losing your database entirely is not so manageable).

For each LXC in the default project, I named them ovn-central-2, ovn-central-3, ovn-central-4, but that doesn’t really matter. You need to assign each one of those LXCs to the PHYSICAL network. This allow for them to use your home router’s DHCP reservations/routing. The physical network is like a Layer 2 switch.

On those LXCs, you can follow any normal tutorial on installing the “server” part of OVN, which is only the databases. For Debian, the package is just ovn-central.

Then when you create a new Project, inside of that project, you create a new OVN network. This OVN network will uplink to the Bridge network, which provides NAT and OVN DHCP. From here, you should be able to hop back into the IncusOS Documentation and finish up your network.

Option 2: Dedicated OVN “servers”

The process is largely the same and this is what I am running right now. I got 3 IncusOS hosts and I have 3 mini PCs. The Mini PCs are running debian with ovn-central. and I use Cockpit and Cockpit-pcp to remotely manage them.

This setup requires you to have some spare endpoints laying around, but your IncusOS hosts dont need the Physical network, they only need the Bridge network.


I hope that helped fill in some gaps. I actually broke my entire home network today and I think I need to rebuilt my servers from scratch, so if I end up doing that, I can write a detailed blog post about each step in rebuilding the cluster with multi-tenant OVN. If that happens, Ill let you know.

You could probably get option 1 to work with 1 NIC. What I did on a similar software that wanted 2 NIC’s, Openstack (similar to Incus in some ways, also uses OVN) is that you can convert the physical interface into a bridge interface that also has an IP address. I would do this with NetworkManager.

Then, I would create a virtual ethernet (veth) device. One end would be attached to the bridge, and the other end would act as if it was a normal ethernet interface… or you could attach it to another bridge. And that bridge is what I would feed openstack/OVN, and the main network interface the host would use would be that bridge that has an ip address and is also pushing traffic normally.

NetworkManager is a bit of a problem since IncusOS is immutable.

If you ran Debian with Incus on it, you would have more freedom, but if I am going to virtualize something, I don’t want to waste overhead on a type-2 hypervisor. Thats part of the reason why I almost never use Docker.

In OP’s position, it might be worth it for them to just use Incus instead of IncusOS… Or spin up Proxmox so they can rebuild and tear down different IncusOS configurations. I work in tech and I’m relatively solid in systems and networking, and I had to tear down and rebuild my IncusOS cluster about a million times (mainly because this was before IncusOS has the automatic network configuration rollback setting and I’d lock myself out of the OS) before I got a grasp of the networking concepts PLUS the immutable aspect of it… it wasnt very forgiving for minor errors lol.

What I did was used some cheap USB NICs for my Northbound/Southbound Database traffic and used the main NICs for cluster and internet traffic. Its also nice to get both NICs set up for management traffic AND make sure you have a TLS way to access them, so if one goes down for some reason, you have a backdoor to use… (but as a cybersecurity person, my professional recommendation is to keep your management interface separate from your cluster/internet traffic)

One thing for OP that I forgot to mention is that the WebUI is very friendly compared to the CLI. The CLI is powerful but I want to give major props to the IncusOS team on their WebUI, there aren’t many CLI-to-WebUI ports that are solid and the IncusOS WebUI seems to have almost complete feature parity with the CLI tools.

Just for the sake of correctness, IncusOS is as much a Type-X hypervisor as Incus installed on Debian. The Xen and IBM folk will tell you IncusOS is Type-2, and the KVM folk will tell you it is Type-1. And the other people will be confused because that distinction is very blurry these days :slight_smile:

1 Like

Don’t pay attention to any of that talk. Only pay attention to performance benchmarks. If it can consistently hit above 90% performance in many/most categories then it’s a type 1 hypervisor. [1]

If it’s below that it’s a type 2.

Many of the modern type 1 hypervisors like kvm or hyper v are really convenient to manage from within the hosts themselves, causing people who have this perspective of “Type 1 has to be managed from outside the host” to get confused, which is usually where the misconception lies in my experience. But really, type 1 is not about how you manage it, it’s really about the way it’s architectured, and that shows in the performance.

[1] See benchmarks like this: How fast is KVM? Host vs virtual machine performance! - Linux - Level1Techs Forums . There are many floating around, here we have 96% performance with KVM. Most type 1 hypervisors hit similar numbers. Type 2, like virtualbox or Vmware workstation, hit 80% or below.

1 Like

Well, no, but that doesn’t shouldn’t really matter anyway.
Except that it’s slowly getting its way into European standards, cf. this one draft that I recently helped review: https://docbox.etsi.org/CYBER/EUSR/Open/EN_304-635_V0.0.15_2026-04-20_Virtualization-Container_Mature-draft.pdf

1 Like

May be back to the topic?

IncusOS creates bridges on top of all physical devices. You should be able to get your ovn-central containers using it for their cluster communication and tell each IncusOS client to use the local container central database. From there you should be able to create an OVN network across all IncusOS nodes.

That’s basically how I understand how @stgraber explained it in one of the posts or youtube videos?

Thanks everyone for the suggestions. Here is what I tried based on what I understood needs to be done for a standalone (not clustered) IncusOS. It gets me a little closer.

First, I created a container to run ovn-central per the standalone documentation.

$ incus launch images:debian/13 ovnctrl0
$ incus shell ovnctrl0
# export TERM=xterm-256color
# apt install ovn-host ovn-central less zile
# for opt in \
      external_ids:ovn-remote=unix:/run/ovn/ovnsb_db.sock \
      external_ids:ovn-encap-type=geneve \
      external_ids:ovn-encap-ip=127.0.0.1 ; do \
    ovs-vsctl set open_vswitch . $opt ; \
  done
# for opt in \
      external_ids:ovn-remote \
      external_ids:ovn-encap-type \
      external_ids:ovn-encap-ip ; do \
    ovs-vsctl get open_vswitch . $opt ; \
  done
"unix:/run/ovn/ovnsb_db.sock"
geneve
"127.0.0.1"
# exit
$ incus network list incusbr0 -c ntm4
+----------+--------+---------+----------------+
|   NAME   |  TYPE  | MANAGED |      IPV4      |
+----------+--------+---------+----------------+
| incusbr0 | bridge | YES     | 10.20.136.1/24 |
+----------+--------+---------+----------------+
$ incus list ovnctrl0 -c ns4
+----------+---------+---------------------+
|   NAME   |  STATE  |        IPV4         |
+----------+---------+---------------------+
| ovnctrl0 | RUNNING | 10.20.136.89 (eth0) |
+----------+---------+---------------------+

Aside: I wasn’t able to set all the options in one go as per the documentation but instead had to set them individually. I verified that they are set using that method.

Next, I edited the OVN service configuration:

$ incus admin os service edit ovn
$ incus admin os service show ovn
config:
  database: "tcp:10.20.136.89:6642"
  enabled: true
  tunnel_address: "10.20.136.89"
  tunnel_protocol: "geneve"
state: {}

It looks like some things worked but not all:

$ incus admin os debug log | grep ovn | less
[2026/06/01 16:27:08 EDT] ovs-vsctl: ovs|00001|vsctl|INFO|Called as ovs-vsctl set open_vswitch . external_ids:hostname=a3caa480-73cc-11e9-9cd3-5c26ea895600 external_ids:ovn-remote=tcp:10.20.136.89:6642 external_ids:ovn-encap-type=geneve external_ids:ovn-encap-ip=10.20.136.89 external_ids:ovn-is-interconn=false
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00001|reconnect|INFO|unix:/run/openvswitch/db.sock: connecting...
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00002|reconnect|INFO|unix:/run/openvswitch/db.sock: connected
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00001|reconnect|INFO|unix:/run/openvswitch/db.sock: connecting...
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00002|reconnect|INFO|unix:/run/openvswitch/db.sock: connected
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00003|main|INFO|OVN internal version is : [25.09.2-21.5.0-81.10]
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00003|main|INFO|OVN internal version is : [25.09.2-21.5.0-81.10]
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00004|main|INFO|OVS IDL reconnected, force recompute.
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00005|reconnect|INFO|tcp:10.20.136.89:6642: connecting...
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00006|main|INFO|OVNSB IDL reconnected, force recompute.
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00004|main|INFO|OVS IDL reconnected, force recompute.
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00005|reconnect|INFO|tcp:10.20.136.89:6642: connecting...
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00006|main|INFO|OVNSB IDL reconnected, force recompute.
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00007|main|INFO|Setting flow table prefixes: ip_src, ip_dst, ipv6_src, ipv6_dst.
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00008|ovn_util|INFO|statctrl: connecting to switch: "unix://var/run/openvswitch/br-int.mgmt"
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00009|rconn|INFO|unix://var/run/openvswitch/br-int.mgmt: connecting...
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00007|main|INFO|Setting flow table prefixes: ip_src, ip_dst, ipv6_src, ipv6_dst.
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00010|rconn|WARN|unix://var/run/openvswitch/br-int.mgmt: connection failed (No such file or directory)
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00011|rconn|INFO|unix://var/run/openvswitch/br-int.mgmt: waiting 1 seconds before reconnect
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00012|ovn_util|INFO|pinctrl: connecting to switch: "unix://var/run/openvswitch/br-int.mgmt"
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00013|rconn|INFO|unix://var/run/openvswitch/br-int.mgmt: connecting...
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00014|rconn|WARN|unix://var/run/openvswitch/br-int.mgmt: connection failed (No such file or directory)
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00015|rconn|INFO|unix://var/run/openvswitch/br-int.mgmt: waiting 1 seconds before reconnect
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00008|ovn_util|INFO|statctrl: connecting to switch: "unix://var/run/openvswitch/br-int.mgmt"
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00009|rconn|INFO|unix://var/run/openvswitch/br-int.mgmt: connecting...
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00010|rconn|WARN|unix://var/run/openvswitch/br-int.mgmt: connection failed (No such file or directory)
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00011|rconn|INFO|unix://var/run/openvswitch/br-int.mgmt: waiting 1 seconds before reconnect
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00012|ovn_util|INFO|pinctrl: connecting to switch: "unix://var/run/openvswitch/br-int.mgmt"
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00013|rconn|INFO|unix://var/run/openvswitch/br-int.mgmt: connecting...
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00014|rconn|WARN|unix://var/run/openvswitch/br-int.mgmt: connection failed (No such file or directory)
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00015|rconn|INFO|unix://var/run/openvswitch/br-int.mgmt: waiting 1 seconds before reconnect
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00016|reconnect|INFO|tcp:10.20.136.89:6642: connection attempt failed (Connection refused)
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00016|reconnect|INFO|tcp:10.20.136.89:6642: connection attempt failed (Connection refused)
[2026/06/01 16:27:08 EDT] ovn-controller: ovs|00001|fatal_signal(urcu1)|WARN|terminating with signal 15 (Terminated)
[2026/06/01 16:27:08 EDT] ovn-controller: 2026-06-01T20:27:08Z|00001|fatal_signal(urcu1)|WARN|terminating with signal 15 (Terminated)

It looks to me like it was able to connect to the OVS switch but the OVN controller was unable to connect to br-int.mgmt. Not sure what to do next.

I looked at my notes for setting up a (clustered) OVN network on IncusOS, and in my case I set tunnel_address to the IncusOS host IP address, not the container IP address. This IP address should be shown here:

incus admin os system network show

You should check if you can ping host using this address from your ovnctrl0 container.

However, I am not sure if tunnel_address is used in a standalone setup, so you might also try omitting this setting completely.