Install Home Assistant OS with KVM on Ubuntu headless (CLI only)

Tags: #<Tag:0x00007fc41527d4f0>

Installing KVM and deploying Home Assistant OS

Since the install page only describes how to use KVM via a GUI, and I am using KVM in a server setting, I thought I’d share a quick guide to how to do this from CLI only.

NOTE: This is for Ubuntu Server LTS 20.04, but I suspect it will work for many Debian-based systems. Please report both is you have success on other systems, and if not.
NOTE: I know very little about VMs. The reason I made this guide is because I could have used one :slight_smile: If there is something wrong, or something that could be done smarter, please let me know!

1 - Preparation

I’ll assume you have your OS installed, and have sudo rights. I’ll also assume you are working as root. If not, add a sudo to commands as needed.

First, check that your CPU can run VMs.

egrep -c '(vmx|svm)' /proc/cpuinfo

As long as it doesn’t return 0, then you’re good. Next, check that it can use KVM acceleration

kvm-ok

1.1 - Install KVM and dependencies

KVM might be installed already, the same with OVMF (needed to start a VM with UEFI) but just in case.

apt install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils ovmf

1.2 - Add user to libvirt and kvm groups

adduser [USER] libvirt
adduser [USER] kvm

where [USER] is the user that will deploy VMs

1.3 - Check that install was successful

systemctl status libvirtd

It should say that it is running. If not, then

systemctl enable --now libvirtd

1.4 - Create directories and download image

The image shown to download here is the one available when I tried. Find the current qcow2 image from the Home Assistant install page, and replace the link after the wget command. Also change the file name used in the gunzip and mv commands to the name of the file downloaded.

mkdir -vp /var/lib/libvirt/images/hassos-vm && cd /var/lib/libvirt/images/hassos-vm
wget https://github.com/home-assistant/operating-system/releases/download/4.16/hassos_ova-4.16.qcow2.gz
gunzip hassos_ova-4.16.qcow2.gz
mv hassos_ova-4.16.qcow2 hassos.qcow2

1.5 - Set up a storage pool

virsh pool-create-as --name [NAME] --type dir --target /var/lib/libvirt/images/hassos-vm

where [NAME] is the name you want (for this guide, I used hassos).

2 - Set up network

From what I’ve seen, unless you know you have a specific reason not to, you should create a new network bridge (that we’ll call br0 here).

Start by finding the network interface you are using for network access.

ifconfig

The one you’re looking for is the that has a line that starts with inet followed by the local IP of the machine. For me, it was eno1, so this one (with inet 192.168.0.100):

eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.100  netmask 255.255.255.0  broadcast 192.168.0.255
        inet6 fe80::bc0a:58ff:fef3:d16a  prefixlen 64  scopeid 0x20<link>
        ether 00:21:5e:c6:43:04  txqueuelen 1000  (Ethernet)
        RX packets 6569964  bytes 18454336926 (18.4 GB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 6806881  bytes 721261424 (721.2 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

You can create bridges in different ways, if you’re familiar with others, that shouldn’t make a difference. I used Netplan. Edit the Netplan config:

nano /etc/netplan/00-installer-config.yaml

Before editing, it looked like this:

# This is the network config written by 'subiquity'
network:
  ethernets:
    eno1:
      dhcp4: true
  version: 2

Add lines for the bridge, so the file looks like this afterwards:

# This is the network config written by 'subiquity'
network:
  ethernets:
    eno1:
      dhcp4: true
  version: 2
  bridges:
    br0:
      dhcp4: yes
      interfaces:
             - eno1
      parameters:
        stp: true

Of course replacing eno1 with your interface.

Apply change, and check:

netplan apply
brctl show

The last command should output a list of bridges, and the newly created br0 should be among them.

bridge name	bridge id			STP enabled	interfaces
br0			8000.00215ec64304	yes		eno1

After starting the VM (see below), this command will also show vnet0 under interfaces.

2.1 - Disable netfilter on the bridge(s)

NOTE! Be very careful about this, and read the section thoroughly before attempting!

For performance (and security?) reasons, you can choose to disable netfilter on the bridge. However, this can mess up your network so the bridge (and hence physical network interface) no longer has network. So be sure to (1) know what you are doing, (2) have physical keyboard/monitor access, or (3) have multiple network connections to the machine.

Thus, be VERY careful. It worked for me, but I’ll give no guarantees.

Create a file

sudo nano /etc/sysctl.d/bridge.conf

And insert the following:

net.bridge.bridge-nf-call-ip6tables=0
net.bridge.bridge-nf-call-iptables=0
net.bridge.bridge-nf-call-arptables=0

Create a file

sudo nano /etc/udev/rules.d/99-bridge.rules

And enter:

ACTION=="add", SUBSYSTEM=="module", KERNEL=="br_netfilter", RUN+="/sbin/sysctl -p /etc/sysctl.d/bridge.conf"

Reboot the machine.

3 - Install HassOS VM

Install the downloaded qcow2 image. The file type you download has varied, but you’ll likely need to unzip. Last I checked (HassOS v5.9), it was a .xz file, so unzip by::

xz -d -v hassos_ova-5.9.qcow2.xz

Assuming you’ve used the same names as in this guide:

cd /var/lib/libvirt/images/hassos-vm
virt-install --import --name hassos \
--memory 4096 --vcpus 4 --cpu host \
--disk hassos.qcow2,format=qcow2,bus=virtio \
--network bridge=br0,model=virtio \
--os-variant=rhel8.1 \
--graphics none \
--noautoconsole \
--boot uefi

After a few moments, you should see a new homeassistant show up on your network. Navigate to the IP it shows up at, port 8123, just like any other Home Assistant instance.
NOTE: Any VM will have its own MAC address, which is different from the host, and will thus be assigned its own IP, that is different from the host IP. If you have access to your router config, you can usually see it under “connected devices” with the name “homeassistant”. You can then assign a static IP in your DHCP (probably done in your router, highly recommended). You can assign a MAC address under network with --network mac=52:54:00:XX:XX:XX. Keep in mind it has to start with 52:54:00.
If you don’t specify this, and need to find the MAC address via CLI in ubuntu, use:

virsh dumpxml hassos | grep "mac address" | awk -F\' '{ print $2}'

NOTE: I assigned a DHCP of 192.168.1.115, the same IP I set for the br0. I’m not certain that it is required to do both. If anyone tries this, could you please report back so I can update?

4 - After install

In case you want to stop (or start) the VM (e.g. for doing backups), you can do that using the virsh command.

virsh help

shows you what you can do. Particularly useful might be the following:

To list VMs

virsh list

Or to view all (also stopped):

virsh list --all

If [VM_NAME] is the name of the VM (hassos in our example):

To start:

virsh start [VM_NAME]

To gracefully stop, and leave inactive

virsh destroy [VM_NAME] --graceful

Remove the --graceful to forcibly stop instead.

To shutdown VM:

virsh shutdown [VM_NAME]

Credits

Thanks to @oldrobot for reminding me to mention unzipping the downloaded HassOS qcow2 file.
Thanks to @wmaker, @oldrobot, as well as an answer on askubuntu for the update to network configuration.
Thanks to @sebkouba for pointing out that I should emphasize that a DHCP reservation is a very good idea.
Thanks to @uSlackr for typo editing.

5 Likes

I thought I had an update to create a vlan, and a bridge on the vlan so you could continue using your default ethernet network interface.

I will continue trying to figure this out, but the solution I had didn’t work. If anyone has a guide to do this, please share, and I’ll update the guide accordingly.

Stop point for me. How can I check it?
I have Ubuntu 20.04.1 and can’t connect to ip:8123.

[email protected]:~$ virsh list
 Id   Name   State
----------------------
 3    ha     running

[email protected]:~$ virsh net-list --all
 Name          State    Autostart   Persistent
------------------------------------------------
 host-bridge   active   yes         yes

[email protected]:~$ brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.309c2302eb56       yes             enp2s0
                                                        vnet0
                                                        vnet1
docker0         8000.0242e9d4eb09       no              vethde55014

enp2s0 - my physical interface, has IP 192.168.7.2
br0 - 192.168.7.3
vnet0, vnet1 - no IP addresses. “master br0 state UNKNOWN”

virsh console 3

has no output.

What I doing wrong?

I’m certainly not an expert, I just made this guide because I spent time trying to figure it out. But which IP are you trying to connect to? The IP of the VM will not be the same as the IP for Ubuntu machine. It makes a new MAC address for your VM, and your DHCP (probably router) assigns an IP.

Did you look at “connected devices” in router settings? My Ubuntu computer has 192.168.0.100, but home assistant popped up on my first free DHCP (192.168.0.12).

Otherwise, you can install nmap and run nmap -sP 192.168.7.1/24 (assuming you have a /24 subnet). If you do it while the VM is shut off, and after starting it, you can see if a new devices appears?

Thank you for your manual.
I found another problem on my server - network is configured in /etc/network/interfaces, not by netplan. I remove ifupdown by this answer. So, now my physical interface has no IP.

[email protected]:/var/lib/libvirt/images/hassos-vm# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000
    link/ether 30:9c:23:02:eb:56 brd ff:ff:ff:ff:ff:ff
3: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 30:9c:23:02:eb:56 brd ff:ff:ff:ff:ff:ff
    inet 192.168.7.2/24 brd 192.168.7.255 scope global br0
       valid_lft forever preferred_lft forever
    inet6 fe80::329c:23ff:fe02:eb56/64 scope link
       valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:73:f3:80:64 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:73ff:fef3:8064/64 scope link
       valid_lft forever preferred_lft forever
6: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 02:16:65:bb:db:7d brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::16:65ff:febb:db7d/64 scope link
       valid_lft forever preferred_lft forever

When I create VM, I see new interfaces like vnet0 also without IP. I have Routerbord and didn’t see in log on it any lines about trying to get IP via DHCP. Nobody ask. And no new leases.

Can you connect to your VM via console? Like

virsh console hassos

Can you show ip interfaces in your system? What is the name of VM interfaces?

ip a
brctl show
virsh net-list --all

(Sorry for my English, I’m from Russia)

I think if you have a preferred way of configuring networks, it’s probably better to use that. Netplan was just the first guide I found where it worked for me.

I can’t get the console to work. Whenever I try virsh console hassos, I get the following:

[email protected]:~$ virsh console hassos
Connected to domain hassos
Escape character is ^]

and it just hangs there. I have to close the terminal window to get any responsiveness, and even then when I log back in, it’s still ongoing, and I have to runsudo systemctl restart libvirt-guests.service, and the virsh start hassos. So no, the console certainly doesn’t work for me.

The output of the following commands (now that you mention it, I don’t have network on my physical interface eno1 anymore either, but everything I’ve tried works fine, system updates, ssh into host, etc.):

[email protected]:~$ virsh net-list --all
 Name      State    Autostart   Persistent
--------------------------------------------
 default   active   yes         yes
[email protected]:~$ brctl show
bridge name	bridge id		STP enabled	interfaces
br-5125a8eeecf5		8000.024263dd0351	no		veth09ebe62
							veth3bab36b
							veth5d12887
							vethc31a10f
br-bae6ce10431b		8000.0242b44bc34b	no		veth6052c48
							vetha661d8c
br0		8000.00215ec64304	yes		eno1
							vnet0
docker0		8000.0242b1c3bb1c	no
virbr0		8000.5254002797d6	yes		virbr0-nic
[email protected]:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master br0 state UP group default qlen 1000
    link/ether 00:21:5e:c6:43:04 brd ff:ff:ff:ff:ff:ff
3: eno2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 00:21:5e:c6:43:06 brd ff:ff:ff:ff:ff:ff
4: enx02215eca3baf: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 02:21:5e:ca:3b:af brd ff:ff:ff:ff:ff:ff
5: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 52:54:00:27:97:d6 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
       valid_lft forever preferred_lft forever
6: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc fq_codel master virbr0 state DOWN group default qlen 1000
    link/ether 52:54:00:27:97:d6 brd ff:ff:ff:ff:ff:ff
7: br-5125a8eeecf5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:63:dd:03:51 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global br-5125a8eeecf5
       valid_lft forever preferred_lft forever
    inet6 fe80::42:63ff:fedd:351/64 scope link
       valid_lft forever preferred_lft forever
8: br-bae6ce10431b: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:b4:4b:c3:4b brd ff:ff:ff:ff:ff:ff
    inet 172.11.0.1/24 brd 172.11.0.255 scope global br-bae6ce10431b
       valid_lft forever preferred_lft forever
    inet6 fe80::42:b4ff:fe4b:c34b/64 scope link
       valid_lft forever preferred_lft forever
9: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:b1:c3:bb:1c brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
11: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-5125a8eeecf5 state UP group default
    link/ether da:de:e3:13:94:29 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::d8de:e3ff:fe13:9429/64 scope link
       valid_lft forever preferred_lft forever
13: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-bae6ce10431b state UP group default
    link/ether 6a:bb:f1:5d:cc:5f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::68bb:f1ff:fe5d:cc5f/64 scope link
       valid_lft forever preferred_lft forever
15: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-5125a8eeecf5 state UP group default
    link/ether b6:53:62:c2:9d:fd brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::b453:62ff:fec2:9dfd/64 scope link
       valid_lft forever preferred_lft forever
17: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-5125a8eeecf5 state UP group default
    link/ether 6e:43:25:e9:6d:4a brd ff:ff:ff:ff:ff:ff link-netnsid 3
    inet6 fe80::6c43:25ff:fee9:6d4a/64 scope link
       valid_lft forever preferred_lft forever
19: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-5125a8eeecf5 state UP group default
    link/ether 42:59:40:66:95:89 brd ff:ff:ff:ff:ff:ff link-netnsid 4
    inet6 fe80::4059:40ff:fe66:9589/64 scope link
       valid_lft forever preferred_lft forever
21: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-bae6ce10431b state UP group default
    link/ether 62:24:e1:d0:cd:5a brd ff:ff:ff:ff:ff:ff link-netnsid 5
    inet6 fe80::6024:e1ff:fed0:cd5a/64 scope link
       valid_lft forever preferred_lft forever
22: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:21:5e:c6:43:04 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.100/24 brd 192.168.0.255 scope global dynamic br0
       valid_lft 85565sec preferred_lft 85565sec
    inet6 fe80::bc0a:58ff:fef3:d16a/64 scope link
       valid_lft forever preferred_lft forever
25: vnet0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UNKNOWN group default qlen 1000
    link/ether fe:54:00:26:69:40 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::fc54:ff:fe26:6940/64 scope link
       valid_lft forever preferred_lft forever

In case you want to delete the br0 again

sudo ip link set enp2s0 up
sudo ip link set br0 down
sudo brctl delbr br0

You might also need to remove the part in the 00-installer-config.yaml, I’m actually not sure.

Sorry, you can press Ctrl+] to quit.

Yes, br0 - is your new main interface.

So, this state on vnet0 is normal. And no IP, looks like your address 192.168.0.12 is inside VM and don’t showing in “ip a”.

Another question - why you use old version hassos_ova-4.16? You know something? :slight_smile: There is 5.8 now.

So it seems you are helping me more than I’m helping you. Sorry about that :slight_smile: Does any of this help? Are you closer to accessing you Home Assistant VM (via web UI)?

I used hassos qcow2 image that was available on the install page when I tried this. So no reason other than that 5.8 was not available back then (might have been when I finally posted this, but not while I was actually doing it). But of course it’s just a “update” via UI to get 5.8

I am a network engineer, so this is what I understand :slightly_smiling_face:
I tried your old version and new 5.8, but nothing new yet. I don’t see any attempts to get an address on my mikrotik DHCP server. As I know there is no another possibilites to access VM without IP? May be it have some secondary IP for managment?
Anyway I keep trying.
All I know about VM - it work and doing something)

Now I see attemps, but with the same mac-address as br0

I don’t know if you can set up a secondary IP. I know that for oVirt (that I was looking to use before the CentOS announcement), you can set up management networks. So I’d guess that it’s possible, but I wouldn’t know how to do it.

Quick question: You did ensure that it is setting up the VM with boot=UEFI right? So you have installed OVMF? The first VM I tried (using oVirt) never booted, because I hadn’t set the boot to UEFI, it was trying to use BIOS mode, and the HassOS image can’t do that.

I tried testing with a centOS qcow2 image, perhaps you could try that (https://cloud.centos.org/centos/7/images/). Just to see if it is HassOS specific, or KVM/system specific?

I did anything to use entire your command

virt-install --import --name hassos \
--memory 4096 --vcpus 4 --cpu host \
--disk hassos.qcow2,format=qcow2,bus=virtio \
--network bridge=br0,model=virtio \
--os-variant=rhel8.1 \
--graphics none \
--noautoconsole \
--boot uefi

I downloaded and try CentOS:

virt-install --import --name centos \
--memory 4096 --vcpus 4 --cpu host \
--disk CentOS-7-x86_64-GenericCloud.qcow2,format=qcow2,bus=virtio \
--network bridge=br0,model=virtio \
--os-variant=rhel8.1 \
--graphics none \
--noautoconsole \
--boot uefi

At least it have a console. But I have no idea what to do next.

And the centOS VM does show up on your network (DHCP)? I mean, is it assigned a local IP that you can ssh into, right? If the CentOS VM does show up on the network, it would seem like issue is something specifically with the HassOS.

(Well, I actually don’t think you can ssh directly into the CentOS VM, since I think you’d need to inject SSH keys into the image before running it, but is it getting an IP at least?)

How about (I’m kind of reaching for straws here) trying to start the HassOS VM and running (from the Ubuntu CLI) nmap -p 8123 192.168.7.0/24 (again, assuming you have a /24 netmask, and that DHCP assigns IPs in that range). I see something like:

PORT     STATE  SERVICE
8123/tcp closed polipo

Nmap scan report for 192.168.0.4
Host is up (0.00059s latency).

PORT     STATE  SERVICE
8123/tcp closed polipo

Nmap scan report for 192.168.0.11
Host is up (0.085s latency).

PORT     STATE  SERVICE
8123/tcp closed polipo

Nmap scan report for 192.168.0.12
Host is up (0.00096s latency).

PORT     STATE SERVICE
8123/tcp open  polipo

... etc.

Where only the IPs with a Home Assistant running has an open port 8123. In case it’s “hiding” somewhere?

Where to start?

I did all the same mistakes you guys did! Well, actually one mistake and one not knowing better. I’ll show you in a minute!

First of all! The 5.9 image obviously comes as qcow2.xz <- Please note the *.xz at the end! You have to unzip that with:

xz -d -v hassos_ova-5.9.qcow2.xz

If you do that your file will grow to a 32Gb qcow2 file.

After this, redo all your vm creation process, you’ll notice that now you really boot to home assistant.

What you show as console is in fact the UEFI console of QEMU/KVM. It has nothing to do with home assistant at all. That was my first mistake! I hope this helps.

Now on to our second problem:
No IP even though the bridge is up. I am using an Ubuntu Server 20.10 install an have Docker as well as QEMU/KVM installed. I did setup the bridge like explained before but: it seems Ubuntu likes to restrict things without my knowledge, hence:

Howto - disable netfilter

You need to disable the netfilter on your bridge or nothing will go in or out, even if everything looks absolutely fine.

In short what I did was: (in simple steps and assuming you already created your bridge as shown above with netplan).

-> Important <-
You first need to follow the tutorial/how-to above!
Create your bridge and then, when your traffic goes in + out of your host without any problem move on to the next problem.

-> Even more important! <-
This has been tested with Ubuntu Server 20.10, I can not guarantee it to work on any other Distro / Version!

-> !!!Absolutely, incredibly important!!! <-
Understand that you will be messing with udev, startup rules and such. If your server starts screaming at your wife and kids I will not be held responsible! If you do not have
!!! physical !!!
access to your server don’t do it! You could end up with a networkless machine!

-> Lets get it on <-

  1. Make sure your brigdge is in place and works so far!

  2. Create a file called /etc/sysctl.d/bridge.conf and fill it in with this:

net.bridge.bridge-nf-call-ip6tables=0
net.bridge.bridge-nf-call-iptables=0
net.bridge.bridge-nf-call-arptables=0
  1. Then create a file called /etc/udev/rules.d/99-bridge.rules
    Just one line needed inside, not more, not less:
ACTION=="add", SUBSYSTEM=="module", KERNEL=="br_netfilter", RUN+="/sbin/sysctl -p /etc/sysctl.d/bridge.conf"
  1. [Optional] Disable and remove the default virtual network for QEMU, use sudo if you get a permission denied error:
virsh net-destroy default
virsh net-undefine default
  1. Cross your fingers and reboot your server!

Of course all credits for my guide go to the author of the forementioned article. I’m just summing it up here.

I hope this helps you in any way!

1 Like

My 2 cents worth…(Caveat, I am using Ubuntu 20.0.4 Desktop and virt-manager; so things may differ from the Server version and of course using virt-install differs from virt-manager).

One observation is that it appears the netplan config gives the Ubuntu Server 'host" two IP addresses, one for eno1 (dhcp4 true), and the other for br0 (dhcp4 true). I would think it should be eno1 has dhcp4 false. Anyway an $ip addr should reveal that eno1 has no ipaddress but br0 does.

Another observation, is that even though br0 was configured, it was not activated (see the output of $ virsh net-list --all). Only the QEMU/KVM default bridge was active.
In a separate thread, I suggested a way to activate br0:


With br0 active, I would think you can specify br0 in the virt-install using --network=bridge:br0 (I am not sure about the model=virtio as my setup uses e1000 which models an Ethernet NIC card). Should then be able to run fine and not have to deal with destroying the default bridge (in my case, both br0 and default bridges are active).

As for the netfilter comment, I can’t say definitively, but I’ve gone through several other QEMU/KVM installation guides and there was no mention of needing to disable netfilter, so wondering if disabling it is only in regards to a performance issue (at least that’s all the Howto-disable netfilter guide says about it)? I did check my system doing $ lsmod and the br_filter kernel module is not installed, so for sure it is not in the way in my case. I would be curious if br_filter is installed in the Ubuntu 20.0.4 Server case?

Anyway, hope this may further help those having networking issues.

Hey wmaker!

You are probably right. I also see no need to assign eno1 an IP address, since all traffic in + out the host should be routed through the bridge. But that’s also my 2 cent.

And most definitely, you have to create the xml file for kvm and make your bridge known! I missed this (I would say fundamental step) is missing in the above guide! This is crucial.

As for netfilter all I can tell you is my virtual machines (and I tried to setup a couple) would not get any IP traffic at all until I did this. Maybe it’s the other way round and you need netfilter to do be loaded. But then again, this is where Ubuntu does it’s magic and I really don’t have the time to go that deep. I’d prefer I had to proactively do my configuration, than “magic” happen under the hood.

I hope we can help with our comments :smiley:

Hey @oldrobot & @wmaker, thanks for the inputs.

I noticed that indeed virsh net-list --all doesn’t show the br0. It shows up with brctl show though, and the VM has internet when using the virt-install command in the guide. My physical interface has no IP, but the bridge does. Regardless, all my VMs having internet by using the br0. Is it still advisable to create a host-bridge.xml file and add the br0? What will this mean (what will the actual difference in behavior be)?

Good point, I’ll clarify in the guide. Thanks! (though the qcow2 image came as a .gz file back when I made this guide, so if that changes every now and then, perhaps I’ll just note that it should be unzipped).

Is this what you see by ufw status verbose? If so, mine shows Status: inactive, which is probably why I didn’t need to do this step (and thus why I didn’t include in the guide). Could this be a change in Ubuntu 20.10 v 20.04?

Hi @Aephir

I’m looking at your brctl show and it shows that vnet0 is associated with br0 and as well, en01 is also associated with br0. To me this explains how the traffic flows successfully.

My observation has been that when qemu/kvm starts up a VM it creates an interface vnetX (X being 0, 1,2, etc, one for each VM being started). qemu/kvm virtualization has to know which bridge to assign vnetX to and my presumption is it knows this using --network bridge=br0. But since br0 is not in the netlist, it apparently takes --network bridge=br0 at face value. So… I’m not really sure know how to answer your question, but I would suggest as a best practice, that the xml file should at least be mentioned, maybe a pointer to another web page that shows how to do it.
EDIT: Having thought some more, I’m speculating that the net-list is only needed by virt-manager as its GUI is used show the user what bridges are available to use and for the user to select one. So with that, I now think it is not necessary to include the creation of the xml file.

I don’t think ufw status is an adequate indicator. Instead, run the comand $lsmod. There will be lots of output, but if you see the lines something like:

br_netfilter           24576  0
bridge                155648  1 br_netfilter

Then the netfilter is running on the bridge. If it is not, then disabling the filter would have no affect.

NOTE: Disregard update for now. The Hass I could access in this way was the instance I have running in Docker. I’m working on finding the update to give network to the VM with the updated approach. I’ve reverted the guide, since it is taking longer than I thought.

@wmaker, @seventh: I have updated the “network setup” part based on your (and other) input, thanks for the suggestions. Hopefully it’s better this way.

Now we create a vlan on the eth interface, a bridge on the vlan, and set up the VM on that.

@seventh: If you do end up trying this approach, please let me know if you try without setting DHCP reservation before first HassOS boot, I’d like to know if that step is necessary :slight_smile:

This is great! I need to map my usb z-wave stick into the VM. It’s mounted as:

/dev/serial/by-id/usb-Silicon_Labs....

if someone could point me to the correct incantation to get that inside my VM somehow… I would be grateful