This page looks best with JavaScript enabled

Graphical Access to KVM LXD Virtual Machines from FreeBSD

 ·  🎃 kr0m

If we virtualize using Linux/LXD, we know that it supports both containers and KVM virtual machines. Access to the latter can be through serial port or VNC. The problem with the VNC option is that a client LXD server is required to launch the graphical session. In other words, we can’t access the VNC interface unless we have a Linux system with locally installed LXD acting as a VNC client.

In this tutorial, we will install an Ubuntu server under Bhyve , where we will install LXD, and using SSH forwarding, we will launch the graphical session on our FreeBSD system.

The tutorial is composed of the following sections:


The first step will be to install vm-bhyve, the virtual machine manager, as indicated in this [article earlier]](../vm_bhyve).

We download the Ubuntu server cloud image:

We can see the available images;

vm img

default             ubuntu-22.04-server-cloudimg-amd64.img

We create the VM by importing my ours SSH key:

vm create -c 4 -m 8G -s 40G -t linux-zvol -i ubuntu-22.04-server-cloudimg-amd64.img -C -k /home/kr0m/.ssh/ ubuntu-cloud

We start the VM and check that it has started correctly:

vm start ubuntu-cloud
vm list

ubuntu-cloud  default    grub    4    8G      -    No    Running (78823)

Cloud images do not allow assigning a static IP, so we need to determine the IP assigned by DHCP based on its MAC address:

MAC=$(vm info ubuntu-cloud|grep 'fixed-mac-address'|tr -d " "|awk -F "fixed-mac-address:" '{print$2}')
fping -ag
arp -a |grep $MAC

? ( at 58:9c:fc:07:fd:05 on em0 expires in 1200 seconds [ethernet]

We access the VM:

We assign a password to the root and ubuntu users:

sudo su -l
passwd ubuntu

Now that the users have passwords, we can also access via console if desired:

vm console ubuntu-cloud

We disable cloud networking configuration:

vi /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg

network: {config: disabled}

We assign a static IP and configure a bridge with the same MAC address as shown in the VM configuration. This way, if we need to debug problems and locate the VM by MAC, it will be easier:

vi /etc/netplan/50-cloud-init.yaml

  version: 2
      dhcp4: false
      interfaces: [enp0s5]
      addresses: []
      - to: default
        search: []
        addresses: [,]

We reboot:


We access again:

We install the base utilities and virt-viewer, x11-apps:

apt update
apt install net-tools bridge-utils virt-viewer x11-apps

LXD is installed by default in the cloud image.

snap list

Name    Version        Rev    Tracking       Publisher   Notes
core20  20230622       1974   latest/stable  canonical✓  base
lxd     5.0.2-838e1b2  24322  5.0/stable/…   canonical✓  -
snapd   2.59.5         19457  latest/stable  canonical✓  snapd

We perform the initial LXD configuration:

lxd init

Would you like to use LXD clustering? (yes/no) [default=no]: 
Do you want to configure a new storage pool? (yes/no) [default=yes]: 
Name of the new storage pool [default=default]: 
Name of the storage backend to use (ceph, cephobject, dir, lvm, zfs, btrfs) [default=zfs]: dir
Would you like to connect to a MAAS server? (yes/no) [default=no]: 
Would you like to create a new local network bridge? (yes/no) [default=yes]: no
Would you like to configure LXD to use an existing bridge or host interface? (yes/no) [default=no]: yes
Name of the existing bridge or host interface: br0
Would you like the LXD server to be available over the network? (yes/no) [default=no]: no
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]: 
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: 

We add the access user to some administrative groups:

usermod -aG adm,sudo,lxd ubuntu

We add the LXD remote “hostkr0m” using the access user:

lxc remote add hostkr0m https://X.X.X.X:8443

We check the list of remotes:

lxc remote list

|      NAME       |                   URL                    |   PROTOCOL    |  AUTH TYPE  | PUBLIC | STATIC | GLOBAL |
| hostkr0m        | https://X.X.X.X:8443                     | lxd           | tls         | NO     | NO     | NO     |
| images          |       | simplestreams | none        | YES    | NO     | NO     |
| local (current) | unix://                                  | lxd           | file access | NO     | YES    | NO     |
| ubuntu          | | simplestreams | none        | YES    | YES    | NO     |
| ubuntu-daily    |    | simplestreams | none        | YES    | YES    | NO     |

We verify that we can see the VMs from the remote:

lxc list hostkr0m:

|          NAME           |  STATE  |          IPV4           | IPV6 |      TYPE       | SNAPSHOTS |
| ubuntu-desktop-test     | RUNNING | (enp5s0) |      | VIRTUAL-MACHINE | 0         |


On our PC, we need to authorize the VM’s IP address in order to receive forwarded X traffic:

xhost +inet:

We start xclock to verify that everything is working correctly:

ssh -vYC ubuntu@ "xclock"

We start the graphical session of the VM hosted on hostkr0m:

ssh -vYC ubuntu@ "lxc console hostkr0m:ubuntu-desktop-test --type=vga"


If we are conducting tests and reinstallations, we must ensure that there are no conflicts in the SSH known hosts, or X11 forwarding will not work.

If you liked the article, you can treat me to a RedBull here