# Virsh Virtual Machine Management - [Virsh](#virsh) - [Before you Begin](#before-you-begin) - [Connecting to External Servers via SSH](#connecting-to-external-servers-via-ssh) - [Configuring Aliases](#configuring-aliases) - [One-off Connections](#one-off-connections) - [Useful Virsh Commands](#useful-virsh-commands) - [Virsh Networking](#virsh-networking) - [Create a Virtual Network](#create-a-virtual-network) - [Attach a New Virtual Network](#attach-a-new-virtual-network) - [Detach a Virtual Network](#detach-a-virtual-network) - [Destroy a Virtual Network](#destroy-a-virtual-network) - [Set a Static IP](#set-a-static-ip) - [Creating VMs](#creating-vms) - [Create VM with No Graphics and use an Existing QCOW2 Disk](#create-vm-with-no-graphics-and-use-an-existing-qcow2-disk) - [Create a Cloud Init Compatible VM](#create-a-cloud-init-compatible-vm) - [Create VM with Graphics using an ISO Installation Disk](#create-vm-with-graphics-using-an-iso-installation-disk) - [Create VM using Host Device as Disk](#create-vm-using-host-device-as-disk) - [Snapshots](#snapshots) - [Virt Builder](#virt-builder) ## Before you Begin 1. Add yourself to the `qemu` and `libvirt` groups: `usermod -aG libvirt,qemu ducoterra` 2. Change the images ownership to qemu: `chown -R qemu:qemu /var/lib/libvirt/images` 3. Change the iso ownership to qemu: `chown -R qemu:qemu /var/lib/libvirt/iso` 4. Allow group write access to images: `chmod 770 /var/lib/libvirt/images` 5. Allow group write access to iso: `chmod 770 /var/lib/libvirt/iso` 6. Tell virsh to connect to your root system rather than your user: `export LIBVIRT_DEFAULT_URI='qemu:///system'` 7. Export your editor so virsh knows what to use: `export EDITOR=vim` ## Connecting to External Servers via SSH ### Configuring Aliases 1. Edit `~/.config/libvirt/libvirt.conf` 2. Add your aliases ```bash uri_aliases = [ "3dserver=qemu+ssh://3dserver/system", ] ``` 3. Export the alias: `export LIBVIRT_DEFAULT_URI=3dserver` ### One-off Connections ```bash export LIBVIRT_DEFAULT_URI='qemu+ssh://user@server/system' ``` ## Useful Virsh Commands ```bash # Show node info virsh nodeinfo # List OS variants osinfo-query os # List all current machines virsh list --all # Connect to console VM virsh console fedora42-test # Connect to graphical VM virt-viewer --wait fedora42-test # Get leased IP Addresses for the default network virsh net-dhcp-leases default # Reboot a VM virsh reboot # Shutdown a VM virsh shutdown # Force shutdown a VM virsh destroy # Remove a VM virsh undefine --nvram # Remove a VM including storage virsh undefine --nvram --remove-all-storage ``` ## Virsh Networking ### Create a Virtual Network Creating a new network will require an XML configuration file. To see the default network's configuration, use ```bash virsh net-dumpxml default > virbr0.xml ``` To create a dual-stack network, use the following. (Note, I generated a unique local ipv6 address [here](https://www.unique-local-ipv6.com/)). ```xml dual-stack ``` I've already defined this network in `active/software_virsh/dual-stack-dhcp.xml`. Install it with ```bash # Define and autostart the network virsh net-define active/software_virsh/dual-stack-dhcp.xml virsh net-start dual-stack-dhcp virsh net-autostart dual-stack-dhcp # List networks to ensure it created virsh net-list --all # Get the UUID of the created network virsh net-uuid dual-stack-dhcp ``` ### Attach a New Virtual Network ```bash export VM_NAME=my_vm virsh attach-interface \ --type bridge \ --source virbr1 \ --model virtio \ --config \ --live \ --domain ${VM_NAME} ``` ### Detach a Virtual Network ```bash # List mac addresses of connected interfaces' export VM_NAME=my_vm virsh domiflist --domain $VM_NAME virsh detach-interface --domain k0s-worker0 --type bridge --mac "52:54:00:f6:b9:83" --live ``` ### Destroy a Virtual Network ```bash export NETWORK_NAME=mynetwork virsh net-undefine --network $NETWORK_NAME virsh net-destroy --network $NETWORK_NAME ``` ### Set a Static IP To set a static IP, run `virsh net-edit default` and add the following between `` and `` ```bash # Add a host virsh net-update default add-last ip-dhcp-host \ '' \ --live --config --parent-index 0 # Modify a host virsh net-update default modify ip-dhcp-host \ '' \ --live --config --parent-index 0 # Delete a host virsh net-update default delete ip-dhcp-host \ '' \ --live --config --parent-index 0 ``` ## Creating VMs If you have [an osbuild image](/active/software_osbuild/image_builder.md#installing) you can run the following to generate a qcow2 disk image. Then you can [create a VM with an existing qcow2 disk](#create-vm-with-no-graphics-and-use-an-existing-qcow2-disk) and skip the installation process altogether. ```bash sudo systemctl start osbuild-composer.socket composer-cli compose list export IMAGE_UUID= export VM_DISK_PATH=/var/lib/libvirt/images/fedora43-test.qcow2 composer-cli compose image --filename ${VM_DISK_PATH} ${IMAGE_UUID} ``` ### Create VM with No Graphics and use an Existing QCOW2 Disk ```bash # Start the default network if it isn't already virsh net-start --network default export VM_NAME="fedora43-test" export VM_DISK_PATH=/var/lib/libvirt/images/fedora43-test.qcow2 # OPTIONAL: export your qcow2 disk now if using osbuild export IMAGE_UUID= composer-cli compose image --filename ${VM_DISK_PATH} ${IMAGE_UUID} # Install # `--location /path/to/image.iso` supplies a disk installer. (Remove `--import`) # `--import` skips the installation process. # `--graphics spice --video qxl,model.ram=131072,model.vram=131072,model.vgamem=131072 --channel spicevmc` installs graphics # `--console pty,target.type=virtio` adds a console connection # For any command, use `virt-install --arg=?` to see all available options virt-install \ --name "${VM_NAME}" \ --boot uefi,firmware.feature0.name=secure-boot,firmware.feature0.enabled=no \ --cpu host-passthrough --vcpus sockets=1,cores=8,threads=2 \ --ram=8192 \ --os-variant=fedora41 \ --network bridge:virbr0 \ --graphics none \ --console pty,target.type=virtio \ --import --disk "path=${VM_DISK_PATH},bus=virtio" ``` #### Create a Cloud Init Compatible VM ```bash # Fedora # https://fedoraproject.org/cloud/download export VM_NAME="cloud-init-test-fedora" export VM_DISK_PATH=/var/lib/libvirt/images/Fedora-Cloud-Base-Generic-43-1.6.x86_64.qcow2 # Rocky # https://rockylinux.org/download export VM_NAME="cloud-init-test-rocky" export VM_DISK_PATH=/var/lib/libvirt/images/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2 # Ubuntu # https://cloud-images.ubuntu.com/noble/current/ export VM_NAME="cloud-init-test-ubuntu" export VM_DISK_PATH=/var/lib/libvirt/images/noble-server-cloudimg-amd64.img # Debian # https://cloud.debian.org/images/cloud/trixie/20251117-2299/ export VM_NAME="cloud-init-test-debian" export VM_DISK_PATH=/var/lib/libvirt/images/debian-13-generic-amd64-20251117-2299.qcow2 # Set --cloud-init disable=no to allow cloud-init to run again after first boot virt-install \ --name "${VM_NAME}" \ --boot uefi,firmware.feature0.name=secure-boot,firmware.feature0.enabled=no \ --cpu host-passthrough --vcpus sockets=1,cores=8,threads=2 \ --ram=8192 \ --os-variant=fedora41 \ --network bridge:virbr0 \ --graphics none \ --import --disk "path=${VM_DISK_PATH},bus=virtio" \ --cloud-init disable=yes,user-data="active/software_virsh/cloud-init/user-data,meta-data=active/software_virsh/cloud-init/meta-data" ``` ### Create VM with Graphics using an ISO Installation Disk ```bash # `--cdrom /path/to/image.iso` supplies a disk installer. (Remove `--import`) # `--import` skips the installation process. # `--graphics spice --video qxl --channel spicevmc` installs graphics # `--console pty,target.type=virtio` adds a console connection # For any command, use `virt-install --arg=?` to see all available options export VM_NAME="fedora43-kinoite-test" export VM_ISO_PATH=/var/lib/libvirt/iso/fedora43.iso export VM_DISK_PATH=/var/lib/libvirt/images/fedora43.qcow2 virt-install \ --name "${VM_NAME}" \ --boot uefi,firmware.feature0.name=secure-boot,firmware.feature0.enabled=no \ --cpu host-passthrough --vcpus sockets=1,cores=8,threads=2 \ --ram=8192 \ --os-variant=fedora41 \ --network bridge:virbr0 \ --graphics spice --video virtio --channel spicevmc \ --cdrom ${VM_ISO_PATH} \ --disk "path=${VM_DISK_PATH},size=64,bus=virtio,format=qcow2" ``` ### Create VM using Host Device as Disk ```bash # `--cdrom /path/to/image.iso` supplies a disk installer. (Remove `--import`) # `--import` skips the installation process. # `--graphics spice --video qxl --channel spicevmc` installs graphics # `--console pty,target.type=virtio` adds a console connection # `--hostdev 0x1234:0x5678` adds a block storage device # For any command, use `virt-install --arg=?` to see all available options export VM_NAME="usb-linux" virt-install \ --name "${VM_NAME}" \ --boot uefi,firmware.feature0.name=secure-boot,firmware.feature0.enabled=no \ --import \ --cpu host-passthrough --vcpus sockets=1,cores=8,threads=2 \ --ram=8192 \ --os-variant=fedora41 \ --network bridge:virbr0 \ --graphics spice --video qxl --channel spicevmc \ --hostdev 0x13fe:0x6500,boot.order=1 \ --disk none ``` ## Snapshots See [qemu qcow2 snapshots](/active/software_qemu/qemu.md#qcow2-snapshots) ## Virt Builder You can use virt-builder to build vm images ```bash export VM_NAME=fedora42-vb export VM_DISK_PATH=/var/lib/libvirt/images/fedora42-vb.qcow2 # Build the image virt-builder fedora-42 \ --format qcow2 --output ${VM_DISK_PATH} \ --root-password locked:disabled \ --hostname ${VM_NAME} \ --selinux-relabel \ --firstboot-command 'useradd -m -G wheel -p "" ducoterra ; chage -d 0 ducoterra' # Run the built image virt-install \ --name "${VM_NAME}" \ --cpu host-passthrough --vcpus sockets=1,cores=8,threads=2 \ --ram=8192 \ --os-variant=fedora41 \ --network bridge:virbr0 \ --graphics none \ --console pty,target.type=virtio \ --import --disk "path=${VM_DISK_PATH},bus=virtio" ```