Build an installation process using QEMU. Depending on your use case, this can do anything from testing the install process to automating real mass-installations. For other installation topics, see DebianInstaller.

Contents

  1. Basic usage
    1. Basic usage for i386
    2. Basic usage for ppc64el
    3. Basic usage for mips64el, mipsel, arm64, armhf and riscv64
  2. Install to a physical disk
    1. Install directly to a target device
    2. Install indirectly with a backing file
  3. Advanced techniques
    1. Configure UEFI manually
    2. Netboot without a CD image
    3. Cache downloads
    4. Preseed your answers
    5. Install to an in-memory disk
    6. Use the qemu monitor
    7. Communicate between host and VM
      1. Connect from the host to the VM
      2. Communicate using a device
      3. Other possible solutions
    8. Configure partman
  4. See Also

Basic usage

To install the amd64 version of Debian in a virtual machine ("VM"), install qemu-system-x86, download the stable amd64 netinst CD image, then do:

# Create a disk image:
qemu-img create -f qcow2 hd_image.qcow2 10G

# run with minimal options:
qemu-system-x86_64 \
 -m 1G \
 -drive file=hd_image.qcow2 \
 -cdrom <your-iso-image>

The installer should start in a window. You can install a Debian system in hd_image.qcow2 now, but it will be very slow, and will assume you're using an older PC that uses BIOS instead of UEFI. Close the window and try this instead:

# Delete and recreate your disk image:
rm hd_image.qcow2
qemu-img create -f qcow2 hd_image.qcow2 10G

# Create a file to store UEFI variables in:
cp /usr/share/OVMF/OVMF_VARS_4M.fd ./

# run with some new options:
qemu-system-x86_64 \
 -m 1G -cpu max -enable-kvm \
 -no-reboot \
 -device virtio-net-pci,netdev=net0 -netdev user,id=net0 \
 -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
 -drive if=pflash,format=raw,file=./OVMF_VARS_4M.fd \
 -drive if=virtio,cache=unsafe,file=hd_image.qcow2 \
 -device ide-cd,drive=cdrom,bootindex=0 \
 -drive if=none,id=cdrom,format=raw,file=<your-iso-image>

Here's what all the options mean:

-m 1G

give the VM one gigabyte of memory (slightly more than the minimum memory requirement)

-cpu max
emulate the fastest available CPU
-enable-kvm

use KVM virtualisation (you may need to enable virtualization in your BIOS)

-no-reboot
exit when the installer tries to reboot
-device virtio-net-pci,netdev=net0 -netdev user,id=net0
use a fast network device
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd
boot with UEFI instead of BIOS
-drive if=pflash,format=raw,file=./OVMF_VARS_4M.fd
store UEFI variables in the specified file
-drive if=virtio,cache=unsafe,file=hd_image.qcow2
use a fast interface and cache settings (risks corrupting the disk on unclean exit, but you can just restart the installer)
-device ide-cd,drive=cdrom,bootindex=0
pretend the CD-ROM is attached to an IDE bus, and boot from it
-drive if=none,id=cdrom,format=raw,file=<your-iso-image>

use your CD-ROM as a raw disk (not a .qcow2), attached to the IDE bus from the previous line

Finally, try the following advanced technique:

# Extract the Linux kernel and the initrd for the text-based installer:
sudo mount -o loop,ro <your-iso-image> /mnt
cp /mnt/install.amd/{initrd.gz,vmlinuz} ./
sudo umount /mnt

# Delete and recreate your disk image:
rm hd_image.qcow2
qemu-img create -f qcow2 hd_image.qcow2 10G

# run with more new options:
qemu-system-x86_64 \
 -m 1G -cpu max -enable-kvm \
 -no-reboot \
 -device virtio-net-pci,netdev=net0 -netdev user,id=net0 \
 -nographic -serial mon:stdio -echr 8 \
 -kernel vmlinuz -initrd initrd.gz \
 -append "console=ttyS0,115200n8" \
 -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
 -drive if=pflash,format=raw,file=./OVMF_VARS_4M.fd \
 -drive if=virtio,cache=unsafe,file=hd_image.qcow2 \
 -drive if=virtio,format=raw,file=<your-iso-image>

The installer should run in a terminal without showing a boot menu. It will start a screen session, so you can e.g. type ctrl-a <space> to cycle between screens. You can also toggle between normal mode and the QEMU monitor with ctrl-h c. Here's what the new options mean:

-nographic
do not display QEMU's graphical interface
-serial mon:stdio
redirect the VM's serial port to the QEMU monitor, and display the monitor on standard input/output
-echr 8

change QEMU's escape character from ctrl-a to ctrl-h (h is the 8th letter of the alphabet - change the number to your preferred letter)

-kernel vmlinuz -initrd initrd.gz

boot using the previously-extracted kernel and initrd files

-append "console=ttyS0,115200n8"
run the installer on the VM's serial port, with the fastest settings
-drive if=virtio,format=raw,file=<your-iso-image>
use a fast interface for your CD-ROM, and don't boot from it

To exit, type ctrl-h c then quit <enter>. If your terminal thinks it's only 25 lines tall after exiting qemu, run reset to fix it

Running the installer in a terminal means you can copy and paste between the VM and the host. Adding -kernel, -initrd and -append lets you change the initial root filesystem and kernel command-line without having to edit the ISO image. For more initrd and command-line options, see boot/grub/grub.cfg in your ISO image.

Basic usage for i386

{i} i386 support was removed in in trixie, so this is only available in bookworm and earlier.

Just change qemu-system-x86_64 to qemu-system-i386; and get an i386 image, kernel and initrd. Everything else should work as normal.

If your i386 device uses BIOS instead of UEFI, you will also need to remove the two OVMF_VARS lines.

Basic usage for ppc64el

First, change qemu-system-x86_64 to qemu-system-ppc64el; remove the two OVMF_VARS lines; and get a ppc64el image, kernel and initrd.

Then find this line:

 -m 1G -cpu max -enable-kvm \

... and remove -enable-kvm:

 -m 1G -cpu max

Basic usage for mips64el, mipsel, arm64, armhf and riscv64

{i} riscv64 support was added in trixie, so was only available in testing at the time of writing.

As of trixie, the CD images for these architectures don't work with qemu - use netboot instead.

First, change qemu-system-x86_64 to qemu-system-<architecture>; remove the two OVMF_VARS lines; and get an appropriate kernel and initrd.

Next, find this line:

 -m 1G -cpu max -enable-kvm \

... for mips64el, change -cpu max to -cpu 5KEc and replace -enable-kvm with e.g. -M malta:

 -m 1G -cpu 5KEc -M malta \

... or for mipsel, remove -cpu and replace -enable-kvm with e.g. -M malta:

 -m 1G -M malta \

... or for arm64, armhf and riscv64, just replace -enable-kvm with -M virt:

 -m 1G -cpu max -M virt \

Finally, unless you're using riscv64, remove this line:

 -append "console=ttyS0,115200n8" \

Install to a physical disk

QEMU can install install directly to a target device, or install indirectly with a backing file. The former is simpler and may be a little faster, the latter queues up changes to be committed when you're ready.

Typing the wrong device name can destroy all information on your hard disk, so follow these instructions to select the right device name:

# Make sure your device is disconnected or turned off, then do:
ls /dev/disk/by-id/* | grep -v -- '-part[0-9]*$' | tee /tmp/disks.txt

# Make sure your device is connected and powered up, then do:
ls /dev/disk/by-id/* | grep -v -- '-part[0-9]*$' | diff /tmp/disks.txt -
# You should see something like:
# 10a11
# > /dev/disk/by-id/<identifier-of-your-disk>

# Make a variable with the device from the previous command:
TARGET_DEVICE=/dev/disk/by-id/<identifier-of-your-disk>

# Check you typed it correctly by reading one byte from the disk:
sudo head -c1 "$TARGET_DEVICE" > /dev/null
# Disconnect your device, then try again:
sudo head -c1 "$TARGET_DEVICE" > /dev/null

If you did everything right, the head command should only work when the device is plugged in. You can now use $TARGET_DEVICE instead of the name of your device in future commands.

The instructions below often refer to $TARGET_DEVICE. If you create a variable as described above, you can paste those exact instructions. Otherwise, change $TARGET_DEVICE to your actual device name.

Install directly to a target device

The simplest method is just to make your target device available in the VM. Find this line:

 -drive if=virtio,cache=unsafe,file=hd_image.qcow2 \

... and change file=hd_image.qcow2 to format=raw,file="$TARGET_DEVICE":

 -drive if=virtio,cache=unsafe,format=raw,file="$TARGET_DEVICE" \

... then install Debian in the usual way.

{i} You may also need to run qemu with sudo so it can access the disk.

Install indirectly with a backing file

Qemu lets you create a .qcow2 image that's backed by another file. That means it writes to the .qcow2 image as normal, but when it tries to read from a part of the image that hasn't been written to yet, the read falls through to the equivalent part of the backing file. So the installer sees something that looks like the target disk, but can only queue up changes until you explicitly commit them.

{i} You may need to run the commands below with sudo so they can access the disk.

First, create a .qcow2 image backed by your target disk:

qemu-img create -f qcow2 hd_image.qcow2 -F raw -b "$TARGET_DEVICE"

Then install Debian on hd_image.qcow2 in the usual way.

Once the install is complete, Debian should be installed on hd_image.qcow2, but $TARGET_DEVICE should be unchanged. If you'd like to try out your Debian system in a VM, do:

qemu-system-x86_64 \
 -m 1G -cpu max -enable-kvm \
 -no-reboot \
 -device virtio-net-pci,netdev=net0 -netdev user,id=net0 \
 -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
 -drive if=pflash,format=raw,file=./OVMF_VARS_4M.fd \
 -drive if=virtio,file=hd_image.qcow2

If anything went wrong, just delete hd_image.qcow2 and try again. Otherwise, commit your changes to $TARGET_DEVICE:

qemu-img commit -p -d hd_image.qcow2
rm hd_image.qcow2

(-p shows a progress bar, -d skips some work that isn't needed if you're about to delete the file)

{i} The progress bar might pause for several minutes at a time, as data is quickly queued up then slowly written to disk

Advanced techniques

The basic usage lets you install a system using a VM, advanced techniques can be more efficient and more widely useful.

Configure UEFI manually

If you delete your ./OVMF_VARS_4M.fd file after installing your system, you'll need to reconfigure it manually before you can boot into your VM again:

  1. do cp /usr/share/OVMF/OVMF_VARS_4M.fd ./ again

  2. run qemu with the normal settings

  3. when you get to a Shell> prompt, type exit and press <enter>

  4. go to Boot Maintenance Manager > Boot Options > Add Boot Option

  5. select EFI, > EFI > debian > grubx64.efi

  6. press <enter> to input the description, type Debian, and press <enter>

  7. Commit Changes and Exit

  8. Change Boot Order

  9. press <enter> to change the order

  10. select Debian

  11. press + until Debian is at the top

  12. press <enter>

  13. Commit Changes and Exit

  14. press <escape> twice

  15. Reset

Your system should now boot normally.

Netboot without a CD image

The normal installation method ("netinst") requires a CD image, the netboot method downloads everything instead. This might be more convenient if you're testing the installer against recently-updated packages, or you're installing from a system without much disk space.

First, download the netboot kernel and initrd:

  1. go to the installation page

  2. click the "other images" section for your architecture
  3. if there is a netboot/ directory (most architectures)...

    1. if you want a graphical installer, click gtk/

    2. if there is a debian-installer/ directory (most architectures)...

      1. click debian-installer/

      2. click your architecture
    3. download initrd.gz and either linux, vmlinuz or vmlinux (whichever is present)

  4. otherwise, if there are directories with different machine types (e.g. mips64el)...

    1. click your machine type
    2. click netboot/

    3. download vmlinuz-* and initrd.gz

Then find the following lines in your qemu command:

 -kernel vmlinuz -initrd initrd.gz \
 -drive if=virtio,format=raw,file=<your-iso-image>

... change vmlinuz and initrd.gz to the name of the kernel and initrd files you downloaded, and remove the -drive line altogether:

 -kernel linux -initrd initrd.gz \

Running your qemu command should now start the netboot installer.

Cache downloads

The installer can download packages through a proxy server. This can save time and bandwidth if you run the installer frequently.

First, install apt-cacher-ng on your host (or some other device on your network).

Next, find the following line in your qemu command:

 -append "console=ttyS0,115200n8" \

... and add mirror/http/proxy=<url>

 -append "console=ttyS0,115200n8 mirror/http/proxy=<url>" \

qemu assigns the address 10.0.2.2 to the host machine by default, and apt-cacher-ng uses port 3142 by default, so you would usually use mirror/http/proxy=http://10.0.2.2:3142/.

{i} If you use preseeding, you can add mirror/http/proxy=<url> there instead.

Preseed your answers

You can use preseeding to define default answers to questions.

First, install cpio and create a preseed file called preseed.cfg.

Next, merge it into a new initrd:

# Add your preseed file to the initrd:
{ cat initrd.gz; echo preseed.cfg | cpio --format=newc --create | gzip -c; } > initrd-custom.gz

{i} You can add other files the same way, such as udeb files to use during installation, scripts to call from preseed/early_command and preseed/late_command, or installer hooks.

To use your preseed file, find these lines:

 -kernel vmlinuz -initrd initrd.gz \
 -append "console=ttyS0,115200n8" \

... and change them to something like:

 -kernel vmlinuz -initrd initrd-custom \
 -append "console=ttyS0,115200n8 auto" \

This will use your modified initrd-custom file and run the installer in automated mode.

Install to an in-memory disk

If you have enough memory on your computer and want the installer to run faster, you can install to a device that only exists in memory.

First, create your device (note: /tmp has been a tmpfs since trixie):

mkdir -p /tmp/installer

# not required in trixie and later:
sudo mount -t tmpfs tmpfs /tmp/installer

TARGET_DEVICE=/tmp/installer/installer.img
truncate -s 10G "$TARGET_DEVICE"

Then install using the Direct install instructions, above.

{i} truncate -s creates a sparse file. This should be slightly faster than a .qcow2 file, but doesn't support snapshots.

Use the qemu monitor

You can toggle between normal mode and the QEMU monitor with ctrl-h c. Some commands are particularly useful when testing the installer:

To debug the installer kernel, run gdbserver in the monitor, then call gdb --eval-command='target remote localhost:1234' on the host.

To rerun a problematic section of the installer, run savevm, loadvm and delvm in the monitor to store and roll back to specific snaphots.

{i} Snapshots require all attached devices to use .qcow2. If you want to install to an in-memory disk, you'll have to use an in-memory .qcow2 disk.

Communicate between host and VM

To make host files available in the installer's filesystem (e.g. udebs), copy them the same way as preseed.cfg.

You can just copy and paste data in the terminal. The installer includes base64, which makes it easier to copy and paste binary files.

How do I copy d-i logfiles to a remote host? provides general information about communicating with the installer, but here are some qemu-specific notes:

  • switch to a shell with ctrl-a <space> instead of ctrl-alt-F2

  • by default, the VM thinks your host has IP address 10.0.2.2

  • by default, the host cannot connect to the VM (see below)

Connect from the host to the VM

The host can only connect to the VM on ports that you explicitly forwarded.

Find the following line in your qemu command:

 -device virtio-net-pci,netdev=net0 -netdev user,id=net0 \

... and append e.g. ,hostfwd=tcp::5555-:80 to forward port 5555 on the host to port 80 on the VM:

 -device virtio-net-pci,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:80 \

Now anything in the VM that listens on 10.0.2.2:80 will be accessible from the host on 127.0.0.1:5555.

Communicate using a device

QEMU lets you use arbitrary files as disks, but the installer only populates /dev/disk when it detects disks. In practice, that means it's only useful in late_command and installer hooks that run after the directory is created. This section provides some examples for how you might use this.

First, create the file with an initial value, for example:

# Create a small file the VM can modify:
echo -n 0 > shared-device

# Or create a tarball with files you don't want to put in your `initrd`:
tar cf shared-device my-file1 my-file2 ...

Then add an option like this to your qemu command-line:

 -drive file=shared-device,format=raw \

Finally, add a line to e.g. your late_command script:

# Modify the small file:
echo -n 1 > /dev/disk/by-id/ata-QEMU_HARDDISK_QM00002

# Or extract your tarball into the installed system:
tar x -C /target -f /dev/disk/by-id/ata-QEMU_HARDDISK_QM00002

The first example stores 1 in shared-device if installation succeeds, or 0 on error. The second example extracts the contents of shared-device into hd_image.qcow2.

Other possible solutions

The following could theoretically be used to communicate between host and guest, but have been found not to be useful.

virtiofs lets you mount a host directory as a filesystem in a VM. But as of bookworm, the installer uses BusyBox's mount command, which doesn't seem to support -t virtiofs.

QEMU lets you bind /dev/ttyS1 in the VM to a socket on the host, but it's limited to 11,5200 bytes per second, data needs to be base64-encoded to avoid sending special characters, buffering causes files to be truncated, and it's hard to mark end-of-file. If you want to try it, add something like this to your qemu command-line:

-chardev socket,path=installer.sock,server=on,wait=off,id=installer -serial chardev:installer

You can then communicate with the guest using e.g.:

# In the host:
socat - unix:installer.sock

# Read data into the guest:
cat /dev/ttyS1
# Write data from the guest:
cat > /dev/ttyS1

Configure partman

Preseeding uses partman to configure partitions, which can be hard to test because it runs in the middle of the installation process. Here is an easy way to test it:

  1. use the partman-auto/expert_recipe_file setting to store your partman recipe in /partman-recipe.txt

  2. comment out the setting for the last question before partitioning
    • usually either confirming the ordinary user's password or setting up the clock

  3. go through the installation process until you reach that question
  4. save a VM snapshot

  5. On the host, do: socat tcp-listen:12345 - < /path/to/your/partman-recipe.txt

  6. back in the VM...
    1. use ctrl-a <space> to cycle to a shell

    2. do: nc 10.0.2.2:12345 > /partman-recipe.txt

      • this should overwrite the previous recipe
    3. cycle back to the installer with ctrl-a <space>

  7. continue the installer
  8. when partman finishes (or fails), reload your snapshot and return to step 5

See Also


CategoryDebianInstaller

AltStyle によって変換されたページ (->オリジナル) /