This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| manuals:vps:kvm [2016/10/30 14:44] – created toms | manuals:vps:kvm [2026/08/17 11:09] (current) – Link source and virtualization tests and correct the shared translation ID aither | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Using KVM on vpsFree.cz | + | < |
| - | This information page contains | + | < |
| + | source=" | ||
| + | test=" | ||
| + | /> | ||
| - | First, go the details of your VPS in vpsAdmin | + | ====== KVM and libvirt inside a VPS ====== |
| - | * Bridge – enables the creation of a network bridge that the KVM virtual machines will be connected to later, | + | ===== Installing libvirt ===== |
| - | * iptables – enables iptables, an option necessary to configure the IP masquerade, | + | |
| - | * KVM – enables KVM (for hardware support of virtualization). | + | |
| + | vpsAdminOS runs VPSes as containers, not as virtual machines. KVM inside a VPS | ||
| + | is therefore not nested virtualization: | ||
| + | node's hardware virtualization directly. This guide uses the current **Debian | ||
| + | (latest)** template, libvirt and the '' | ||
| - | ===== KVM on Alpine Linux ===== | + | < |
| + | The **KVM** feature is required for virtualization. **TUN/TAP** is required for | ||
| + | TAP-based virtual-machine network interfaces. Both features are enabled by | ||
| + | default for new VPSes in the **Features** section of the VPS detail in | ||
| + | vpsAdmin. | ||
| + | </ | ||
| - | Install the required packages (ip6tables is optional): | + | {{:en: |
| - | <code shell> | + | Install QEMU, libvirt and '' |
| - | apk update | + | |
| - | apk add qemu-system-x86_64 qemu-openrc qemu-img bridge iptables ip6tables | + | |
| - | </ | + | |
| - | Configure the bridge for Qemu/KVM virtual machines – create the /// | + | <code bash> |
| + | #!/usr/bin/env bash | ||
| + | set -euo pipefail | ||
| - | <code shell> | + | apt-get update |
| - | auto br0 | + | apt-get install --yes \ |
| - | iface br0 inet static | + | |
| - | pre-up brctl addbr br0 | + | |
| - | address 172.17.1.1 | + | |
| - | netmask 255.255.255.0 | + | |
| - | post-down brctl delbr br0 | + | |
| + | virtinst | ||
| + | |||
| + | virsh --connect qemu:/// | ||
| </ | </ | ||
| - | Since OpenVZ rewrites | + | Use the '' |
| + | libvirt over SSH, for example '' | ||
| + | with a key. Do not expose an unencrypted libvirt TCP socket | ||
| - | Give the user in the //qemu// group permissions to manage the newly-created bridge: | + | ===== Disk images and ZFS ===== |
| - | <code shell> | + | Use a separate [[manuals: |
| - | echo "allow br0" > / | + | you change ZFS properties for the workload later without affecting the VPS root |
| - | chown root:qemu / | + | dataset. Prepare the storage as follows: |
| - | chmod 0640 / | + | |
| - | </ | + | |
| - | Configure | + | * < |
| + | * < | ||
| + | * < | ||
| - | If you have configured iptables, all you need to add is this rule: | + | For example, from the standard 120 GiB allocation |
| + | root dataset and assign 100 GiB to '' | ||
| + | virtual-machine disk 80 GiB and leave 20 GiB free for growth and snapshots. | ||
| - | < | + | The subdataset is [[manuals: |
| - | iptables | + | root dataset. |
| + | |||
| + | {{: | ||
| + | |||
| + | Add the mounted directory to libvirt as a persistent storage pool: | ||
| + | |||
| + | < | ||
| + | # | ||
| + | set -euo pipefail | ||
| + | |||
| + | virsh --connect qemu:///system pool-define-as \ | ||
| + | vm-images dir --target / | ||
| + | virsh --connect qemu:/// | ||
| + | virsh --connect qemu:/// | ||
| + | virsh --connect qemu:/// | ||
| </ | </ | ||
| - | If not, you can follow our paragraph on [[# | + | Then create disks through libvirt, '' |
| + | '' | ||
| + | Leave the subdataset' | ||
| + | show a reason to change them. Compression is enabled by default. The default | ||
| + | '' | ||
| + | records when appropriate. A '' | ||
| + | files. | ||
| - | ==== Creating and Running | + | For images on ZFS, '' |
| + | copy-on-write layer. Choose '' | ||
| + | snapshots, or other image-format features. | ||
| - | This manual presupposes that you will be using [[https:// | + | See [[https://openzfs.github.io/openzfs-docs/ |
| - | Creating a new virtual | + | ===== Virtual-machine |
| - | Prepare | + | The VPS network interface is connected to the node through |
| + | link. Do not add the VPS interface to a bridge managed by libvirt. Two setups | ||
| + | are suitable for connecting virtual machines: | ||
| - | <code shell> | + | ^ Setup ^ Addresses in the domain ^ Advantages ^ Disadvantages ^ |
| - | mkdir -p / | + | | libvirt NAT | Private IPv4 and private ULA IPv6 | Works with the ordinary VPS addresses and easily serves several domains. | Inbound services need port forwarding and traffic passes through NAT44 and NAT66. | |
| - | qemu-img create -f raw / | + | | Routed public addresses | A public IPv4 '' |
| - | chown qemu:qemu / | + | |
| - | chmod 0600 /var/lib/ | + | |
| - | </ | + | |
| - | Copy the default configuration file /etc/conf.d/ | + | NAT is usually simpler for one or a few domains with a small number of exposed |
| + | services. Use routed addresses when a domain needs its own public addresses, | ||
| + | all ports, or protocols that are awkward | ||
| + | backend-only communication between domains, you can attach another isolated | ||
| + | libvirt network; outbound access still uses one of the setups above. | ||
| - | <code shell> | + | ==== Public addresses on the VPS with dual-stack NAT ==== |
| - | cd /etc/conf.d | + | |
| - | cp qemu qemu.jarvis | + | The public IPv4 ''/ |
| - | vi qemu.jarvis | + | '' |
| + | '' | ||
| + | with NAT66. The '' | ||
| + | normally IPv4-only and uses '' | ||
| + | explicit dual-stack network with a distinct IPv4 subnet. NAT66 is enabled with | ||
| + | '' | ||
| + | [[https://libvirt.org/ | ||
| + | details. | ||
| + | |||
| + | The ULA '' | ||
| + | If you later join several private networks through a VPN or routing, generate a | ||
| + | different random ULA ''/ | ||
| + | '' | ||
| + | |||
| + | The essential part of the network definition is: | ||
| + | |||
| + | <code xml> | ||
| + | < | ||
| + | < | ||
| + | <forward mode=' | ||
| + | <nat ipv6=' | ||
| + | </ | ||
| + | <bridge name=' | ||
| + | <ip address=' | ||
| + | <ip family=' | ||
| + | </ | ||
| </ | </ | ||
| - | Most importantly, add the prepared image: | + | Attach the created domain to '' |
| + | '' | ||
| + | stable addresses and gateways in the domain: | ||
| + | |||
| + | <code conf> | ||
| + | auto ens3 | ||
| + | iface ens3 inet static | ||
| + | address 192.168.124.10/ | ||
| + | gateway 192.168.124.1 | ||
| - | <code shell> | + | iface ens3 inet6 static |
| - | disk1_file=" | + | |
| - | disk1_format=" | + | |
| </ | </ | ||
| - | You will probably also need to add the installation CD of a distribution that you have already downloaded: | + | A port forward consists of DNAT to the domain address and permission for the |
| + | corresponding forwarded traffic. For an IPv4 web service, these are the | ||
| + | essential commands: | ||
| - | < | + | < |
| - | cdrom0_file="/ | + | iptables |
| + | -j DNAT --to-destination 192.168.124.10:80 | ||
| + | iptables -t filter -I FORWARD | ||
| + | -j ACCEPT | ||
| </ | </ | ||
| - | Create a symlink | + | Equivalent '' |
| + | however, are not preserved across libvirt network changes. The complete | ||
| + | below therefore creates the network, a forwarding configuration file, and a | ||
| + | hook that restores the rules when the network starts or reconnects. By default, | ||
| + | it exposes the domain' | ||
| + | IPv4 and IPv6. | ||
| - | <code shell> | + | Save it as, for example, '' |
| - | cd /etc/init.d | + | with one public IPv4 and one address from the VPS IPv6 ''/ |
| - | ln -s qemu qemu.myvirt | + | VPS: |
| - | rc-service qemu.myvirt start | + | <code bash> |
| + | PUBLIC_IPV4=VPS_IPV4 PUBLIC_IPV6=VPS_IPV6 \ | ||
| + | / | ||
| </ | </ | ||
| + | Run the script when creating the network and again when changing its input | ||
| + | values, not after every VPS reboot. The network is set to autostart and its hook | ||
| + | loads the rules when it starts. | ||
| - | ==== Configuring iptables ==== | + | <code bash> |
| + | # | ||
| + | set -euo pipefail | ||
| - | If you aren’t using any tool to generate iptables rules (like e.g. [[https:// | + | : " |
| + | : " | ||
| - | Download the modified rule template with the added masquerade for our bridge to /etc/iptables: | + | guest_ipv4=${GUEST_IPV4: |
| + | guest_ipv6=${GUEST_IPV6: | ||
| + | host_ipv4=${HOST_IPV4: | ||
| + | host_ipv6=${HOST_IPV6: | ||
| + | connection=qemu:/// | ||
| + | network=dualstack-nat | ||
| + | hook=/etc/libvirt/ | ||
| + | config=/ | ||
| + | xml=$(mktemp) | ||
| + | trap 'rm -f " | ||
| - | <code shell> | + | network_uuid=$(virsh --connect " |
| - | rmdir /etc/iptables | + | uuid_element= |
| - | wget -O /etc/iptables http://haste.fit.cvut.cz/raw/iwuqoso | + | if [[ -n $network_uuid ]]; then |
| - | </code> | + | uuid_element=" |
| + | fi | ||
| + | cat >" | ||
| + | < | ||
| + | < | ||
| + | $uuid_element | ||
| + | <forward mode=' | ||
| + | <nat ipv6=' | ||
| + | </forward> | ||
| + | <bridge name=' | ||
| + | <ip address=' | ||
| + | < | ||
| + | <range start=' | ||
| + | </dhcp> | ||
| + | </ip> | ||
| + | <ip family=' | ||
| + | </ | ||
| + | EOF | ||
| - | Modify the /etc/conf.d/iptables configuration file (IPv4): | + | if virsh --connect " |
| + | | grep -Fx " | ||
| + | printf '%s is active. Shut down its attached domains, run ' " | ||
| + | printf 'virsh net-destroy %s, then rerun this script.\n' | ||
| + | exit 1 | ||
| + | fi | ||
| - | <code shell> | + | install -d -m 0755 /etc/libvirt/ |
| - | # /etc/conf.d/iptables | + | install -d -m 0755 /etc/ |
| + | cat >" | ||
| + | # | ||
| + | set -euo pipefail | ||
| - | IPTABLES_SAVE="/ | + | PATH=/ |
| - | # | + | network=${1-} |
| - | SAVE_ON_STOP="no" | + | action=${2-} |
| - | IPFORWARD="yes" | + | config=/etc/libvirt/ |
| - | </code> | + | nat_chain=VPSFREE_KVM_DNAT |
| + | filter_chain=VPSFREE_KVM_FWD | ||
| + | |||
| + | [[ $network == dualstack-nat ]] || exit 0 | ||
| + | exec 9>/ | ||
| + | flock 9 | ||
| + | |||
| + | delete_jumps() { | ||
| + | local tool | ||
| + | for tool in iptables | ||
| + | while "$tool" -w -t nat -C PREROUTING -j " | ||
| + | " | ||
| + | done | ||
| + | while " | ||
| + | " | ||
| + | done | ||
| + | done | ||
| + | } | ||
| + | |||
| + | delete_chains() { | ||
| + | local tool | ||
| + | for tool in iptables ip6tables; do | ||
| + | if " | ||
| + | " | ||
| + | " | ||
| + | fi | ||
| + | if " | ||
| + | " | ||
| + | " | ||
| + | fi | ||
| + | done | ||
| + | } | ||
| + | |||
| + | cleanup() { | ||
| + | delete_jumps | ||
| + | delete_chains | ||
| + | } | ||
| + | |||
| + | valid_ip_address() { | ||
| + | local family=$1 address=$2 | ||
| + | |||
| + | perl -MSocket=AF_INET, | ||
| + | my ($family, $address) = @ARGV; | ||
| + | my $af = $family eq "ipv4" ? AF_INET : AF_INET6; | ||
| + | exit(defined(inet_pton($af, | ||
| + | ' " | ||
| + | } | ||
| + | |||
| + | if [[ $action == stopped ]]; then | ||
| + | cleanup | ||
| + | exit 0 | ||
| + | fi | ||
| + | [[ $action == started || $action == reconnect ]] || exit 0 | ||
| + | |||
| + | entries=() | ||
| + | while read -r family protocol public_ip public_port guest_ip guest_port extra \ | ||
| + | || [[ -n ${family:-} ]]; do | ||
| + | [[ -n ${family:-} && $family != \#* ]] || continue | ||
| + | [[ $family == ipv4 || $family == ipv6 ]] || { | ||
| + | printf ' | ||
| + | exit 1 | ||
| + | } | ||
| + | [[ $protocol | ||
| + | printf ' | ||
| + | exit 1 | ||
| + | } | ||
| + | for address in " | ||
| + | valid_ip_address " | ||
| + | printf ' | ||
| + | " | ||
| + | exit 1 | ||
| + | } | ||
| + | done | ||
| + | [[ $public_port | ||
| + | printf ' | ||
| + | exit 1 | ||
| + | } | ||
| + | public_port=$((10# | ||
| + | guest_port=$((10# | ||
| + | ((public_port >= 1 && public_port | ||
| + | ((guest_port | ||
| + | [[ -z ${extra:-} ]] || { | ||
| + | printf 'Too many fields in %s\n' " | ||
| + | exit 1 | ||
| + | } | ||
| + | entries+=(" | ||
| + | done <" | ||
| - | …and the / | + | cleanup |
| + | for tool in iptables | ||
| + | " | ||
| + | " | ||
| + | done | ||
| + | for entry in " | ||
| + | IFS=' | ||
| + | <<<" | ||
| + | tool=iptables | ||
| + | destination=" | ||
| + | if [[ $family == ipv6 ]]; then | ||
| + | tool=ip6tables | ||
| + | destination=" | ||
| + | fi | ||
| + | " | ||
| + | --dport " | ||
| + | " | ||
| + | --dport " | ||
| + | done | ||
| + | for tool in iptables ip6tables; do | ||
| + | " | ||
| + | " | ||
| + | done | ||
| + | HOOK | ||
| + | chmod 0755 " | ||
| - | <code shell> | + | cat >" |
| - | # / | + | # FAMILY PROTOCOL PUBLIC_IP PUBLIC_PORT GUEST_IP GUEST_PORT |
| + | ipv4 tcp $PUBLIC_IPV4 80 $guest_ipv4 80 | ||
| + | ipv4 tcp $PUBLIC_IPV4 2222 $guest_ipv4 22 | ||
| + | ipv6 tcp $PUBLIC_IPV6 80 $guest_ipv6 80 | ||
| + | ipv6 tcp $PUBLIC_IPV6 2222 $guest_ipv6 22 | ||
| + | EOF | ||
| - | IP6TABLES_SAVE="/etc/iptables" | + | virsh --connect " |
| - | SAVE_RESTORE_OPTIONS="-T filter" | + | virsh --connect " |
| - | SAVE_ON_STOP="no" | + | systemctl restart libvirtd.service |
| - | IPFORWARD="yes" | + | if ! virsh --connect " |
| + | | grep -Fx " | ||
| + | virsh --connect | ||
| + | fi | ||
| + | "$hook" | ||
| + | virsh --connect " | ||
| </ | </ | ||
| - | Run iptables and ip6tables and add them to the runlevel boot: | + | Add another port to ''/ |
| + | '' | ||
| + | expose HTTPS on port 8443 over both protocols with: | ||
| - | < | + | < |
| - | rc-service iptables start | + | ipv4 tcp VPS_IPV4 8443 192.168.124.10 443 |
| - | rc-service ip6tables start | + | ipv6 tcp VPS_IPV6 8443 fd5f: |
| - | rc-update add iptables boot | + | |
| - | rc-update add ip6tables boot | + | |
| </ | </ | ||
| - | ==== Contacts ==== | + | The supported protocols are '' |
| + | is rejected before the active rules are changed. Reload the rules after editing | ||
| + | the file: | ||
| - | * [[jakub@jirutka.cz|Jakub Jirůtka]] (on [[irc://chat.freenode.net/vpsfree|# | + | <code bash> |
| + | /etc/libvirt/ | ||
| + | </code> | ||
| - | ===== KVM on CentOS 7 ===== | + | Port 2222 keeps the domain' |
| + | Restrict the VPS firewall further according to who should reach each service. | ||
| - | <note warning> | + | ==== Private IPv4 on the VPS with public addresses in the domain ==== |
| - | This manual can only be used for CentOS 7.1. The internal network between VPS and VM currently isn’t working | + | |
| - | </ | + | |
| - | I use KVM using libvirt on an updated CentOS 7. | + | In this setup, the VPS has a private IPv4 ''/ |
| + | ''/ | ||
| + | separate IPv6 ''/ | ||
| + | private VPS IPv4 works on the internal vpsFree.cz network and reaches the | ||
| + | Internet through NAT on our routers; libvirt does not translate the domain' | ||
| + | public addresses. | ||
| - | I recommend fully updating CentOS 7, configuring it and installing the required software. Because of iptables permissions, | + | For IPv4, first ask support |
| - | <code shell> | + | <vpsadmin-nav id=" |
| - | yum group install virtualization-host-environment | + | Under **Networking → Routable addresses**, |
| - | yum install virt-manager xauth | + | **Address**, |
| - | systemctl enable libvirtd | + | The public address is not configured on any interface inside the VPS; vpsAdminOS |
| - | systemctl disable firewalld | + | routes it to the VPS via the private address. |
| - | reboot | + | </ |
| + | |||
| + | < | ||
| + | For IPv6, use another ''/ | ||
| + | addresses** and route it through an address in the VPS's ordinary ''/ | ||
| + | you already have a larger routed network such as a ''/ | ||
| + | ''/ | ||
| + | the gateway on the libvirt bridge; for example, '' | ||
| + | '' | ||
| + | </ | ||
| + | |||
| + | IPv4 uses a small transit network only between the VPS and the domain: | ||
| + | |||
| + | < | ||
| + | Internet → public /32 → VPS → 192.168.123.1/ | ||
| + | ↓ | ||
| + | 192.168.123.2/ | ||
| </ | </ | ||
| - | ==== Creating | + | In the libvirt XML, the public IPv4 is a separate route through the domain' |
| + | transit address. The routed IPv6 ''/ | ||
| + | bridge: | ||
| - | Motivation: When you’re working on a slow connection (which the ADSL that the O2 provides in villages definitely is), you will need to minimize the data flow through your primary computer. The local virt-manager would download at least the kernel and initramdisk using the relatively slow download speed. The typically extremely slow upload speed would be used to upload to the host container. | + | <code xml> |
| + | < | ||
| + | < | ||
| + | <forward mode=' | ||
| + | <bridge name=' | ||
| + | <ip address=' | ||
| + | <ip family=' | ||
| + | <route family=' | ||
| + | | ||
| + | </ | ||
| + | </ | ||
| - | < | + | The VPS must forward both protocols: |
| - | ssh root@your-host-name -Y virt-manager | + | |
| + | < | ||
| + | sysctl | ||
| + | sysctl | ||
| </ | </ | ||
| - | You can start the installation on the remote running instance. However, displaying the installer using the default SPICE doesn’t work for me. Since I think switching libvirt to VNC is extremely impractical, I recommend running virt-manager locally | + | The following script creates this network and persists forwarding in |
| + | ''/ | ||
| + | ''/ | ||
| + | first address of the routed IPv6 ''/ | ||
| + | <code bash> | ||
| + | PUBLIC_IPV4=PUBLIC_IPV4 \ | ||
| + | IPV6_GATEWAY=ROUTED_IPV6_PREFIX:: | ||
| + | / | ||
| + | </ | ||
| - | ===== KVM on Debian 8 ===== | + | As with NAT, run the script when creating the network or changing its |
| + | addresses. Network autostart and the settings in ''/ | ||
| + | reboots. | ||
| - | This manual was made using Debian 8. It should function without issues on CentOS as well (with different commands, see above). I have tried to run KVM on Ubuntu 14.04, but sadly, to no avail. | + | <code bash> |
| + | # | ||
| + | set -euo pipefail | ||
| - | If you’re using Windows, you need to install and turn on Xming (if you at least want to install and configure | + | : " |
| + | : " | ||
| - | We’ll start with the usual: | + | host_transit_ipv4=${HOST_TRANSIT_IPV4:-192.168.123.1} |
| + | guest_transit_ipv4=${GUEST_TRANSIT_IPV4: | ||
| + | connection=qemu:/// | ||
| + | network=public-routed | ||
| + | forwarding_config=/ | ||
| + | xml=$(mktemp) | ||
| + | trap 'rm -f " | ||
| - | <code shell> | + | export LC_ALL=C |
| - | apt-get update | + | network_uuid=$(virsh --connect " |
| - | apt-get upgrade | + | uuid_element= |
| + | if [[ -n $network_uuid ]]; then | ||
| + | uuid_element=" | ||
| + | fi | ||
| + | cat >" | ||
| + | < | ||
| + | < | ||
| + | $uuid_element | ||
| + | <forward mode=' | ||
| + | <bridge name=' | ||
| + | < | ||
| + | <ip family=' | ||
| + | <route family=' | ||
| + | | ||
| + | </ | ||
| + | EOF | ||
| + | |||
| + | if virsh --connect " | ||
| + | | grep -Fx " | ||
| + | printf '%s is active. Shut down its attached domains, run ' " | ||
| + | printf 'virsh net-destroy %s, then rerun this script.\n' | ||
| + | exit 1 | ||
| + | fi | ||
| + | |||
| + | install -d -m 0755 " | ||
| + | cat >" | ||
| + | net.ipv4.ip_forward = 1 | ||
| + | net.ipv6.conf.all.forwarding = 1 | ||
| + | EOF | ||
| + | sysctl --quiet --load " | ||
| + | |||
| + | virsh --connect " | ||
| + | virsh --connect " | ||
| + | virsh --connect " | ||
| + | virsh --connect " | ||
| </ | </ | ||
| - | Then we install | + | If the network is already active, the script leaves it unchanged. Shut down its |
| - | < | + | attached domains, stop it with '' |
| - | apt-get install qemu-kvm libvirt-bin | + | script, and start the domains again. The script preserves the existing network |
| - | apt-get install virt-manager | + | UUID. |
| + | |||
| + | Attach the domain to '' | ||
| + | following as ''/ | ||
| + | '' | ||
| + | |||
| + | < | ||
| + | auto ens3 | ||
| + | iface ens3 inet static | ||
| + | address 192.168.123.2/ | ||
| + | up ip address add PUBLIC_IPV4/ | ||
| + | up ip route replace default via 192.168.123.1 src PUBLIC_IPV4 | ||
| + | down ip address del PUBLIC_IPV4/ | ||
| + | |||
| + | iface ens3 inet6 static | ||
| + | address GUEST_IPV6/ | ||
| + | gateway IPV6_GATEWAY | ||
| </ | </ | ||
| - | Altogether, these two packages should be around 320 MB. The next step is downloading the image of the distribution that we want to install on the virtual machine. I installed Ubuntu Server 14.04. Choose the folder where you want to download the image and download the ISO image using “wget”. | ||
| - | <code shell> | ||
| - | cd /home | ||
| - | wget http:// | ||
| - | </ | ||
| - | Run virt-manager. | ||
| - | <code shell> | ||
| - | virt-manager | ||
| - | </ | ||
| - | Using Xming, you can now use Windows to configure and start the installation of the virtual server on your VPS remotely. Note that the SPICE display doesn’t work under X11, so if you want to use a graphical interface to install the virtual machine, you need to select the “Display VNC” option in the settings and restart the virtual machine. If you do this and restart the server, it can lose the information that the .iso image for the installation has been connected, so you will have to go back to the settings of the “CD-ROM” tab and in “Source-path” connect the .iso image for the installation again. | ||
| + | '' | ||
| + | firewall. Allow only traffic that the domain should receive, and remember rules | ||
| + | for both IPv4 and IPv6. The public IPv4 is configured only in the domain. The | ||
| + | IPv6 gateway and domain addresses come from the second, routed ''/ | ||
| + | address from the VPS's ordinary ''/ | ||
| + | protocol uses NAT in this setup. | ||
| + | |||
| + | ===== Installer ISO on NFS ===== | ||
| + | |||
| + | It is safer to copy an installer ISO to a local dataset first. On an older NFSv3 | ||
| + | export without working network locking, QEMU can hang while waiting for a lock | ||
| + | or fail with an error such as '' | ||
| + | '' | ||
| + | |||
| + | For one client and a read-only installer ISO only, you can use '' | ||
| + | |||
| + | <code bash> | ||
| + | # | ||
| + | set -euo pipefail | ||
| + | |||
| + | : " | ||
| + | : " | ||
| + | |||
| + | mountpoint=${ISO_MOUNTPOINT: | ||
| + | install -d -m 0755 " | ||
| + | mount -t nfs -o ro, | ||
| + | " | ||
| + | |||
| + | findmnt --noheadings --output FSTYPE, | ||
| + | </ | ||
| - | ==== Contacts ==== | + | '' |
| + | locks. Never use it for a writable or shared virtual-machine disk. Locking is | ||
| + | part of the NFSv4 protocol and this option is not a solution there. See | ||
| + | [[https:// | ||
| - | * [[: | ||