assuming the host is up and running already
this is almost a script
as root
guest=bookworm
mkdir -p /data/guests/$guest/lala/
cd /data/guests/$guest/
qemu-img create -f qcow2 $guest.qcow2 25G
modprobe nbd max_part=8
qemu-nbd --connect=/dev/nbd0 $guest.qcow2
device=/dev/nbd0
manual partitioning
fdisk $device n (new) default primary default partition 1 default 2048 +24G n (new) default primary default partition 2 default start default end t 82 (Linux swap) a (bootable) 1 (partition 1) w
formatting
mkswap ${device}p2
mkfs.ext4 ${device}p1
mount ${device}p1 lala/
from an ubuntu system
apt install debian-archive-keyring ls -lF /usr/share/keyrings/debian-archive-keyring.gpg # debian-keyring #ls -lF /usr/share/keyrings/debian-keyring.gpg
make sure you’ve got the relevant pubkey in there
gpg1 < /usr/share/keyrings/debian-archive-keyring.gpg
in case you don’t have the distro release you’re looking for, get it manually
apt purge debian-keyring debian-archive-keyring wget http://ftp.de.debian.org/debian/pool/main/d/debian-archive-keyring/debian-archive-keyring_2025.1_all.deb dpkg -i debian-archive-keyring_2025.1_all.deb
country_code=ru
cat /etc/apt/sources.list
curl -I http://ftp.$country_code.debian.org/debian/
df -hT | grep -E 'loop|nbd'
time debootstrap --arch=amd64 --force-check-gpg \
$guest lala http://ftp.$country_code.debian.org/debian/
# 2m17.567s
du -sh lala/
# 361M bookworm
cat lala/etc/fstab cat > lala/etc/fstab <<EOF # override /dev/vda1 / ext4 defaults,noatime,nodiratime 0 0 /dev/vda2 none swap sw 0 0 proc /proc proc defaults 0 0 tmpfs /tmp tmpfs rw,nodev,nosuid,noatime,relatime 0 0 devpts /dev/pts devpts gid=5,mode=620 0 0 EOF
cat lala/etc/hostname
echo $guest > lala/etc/hostname
echo "127.0.0.1 $guest" >> lala/etc/hosts
rmdir lala/etc/network/interfaces.d/
vi lala/etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.122.9/24
gateway 192.168.122.1
cat lala/etc/resolv.conf
echo nameserver 192.168.122.1 > lala/etc/resolv.conf
we need a kernel in there as this is KVM, not XEN
mount -o bind /dev lala/dev
mount -o bind /dev/pts lala/dev/pts
mount -o bind /sys lala/sys
mount -o bind /proc lala/proc
chroot lala/ bash
apt update
apt install locales
dpkg-reconfigure locales
==> en_US.UTF-8
perl -e exit
apt install mlocate net-tools openssh-server manpages man-db
# ifupdown
apt install linux-image-cloud-amd64
# btrfs-progs
# linux-image-kvm
# linux-image-amd64
those symlinks should have been created by the linux-image package already
ls -lF /vmlinuz /initrd.img
make sure whatever additional module you need at boot-time is in there
#grep -i btrfs /boot/config-*
grep -i virtio_console /boot/config-*
cd /boot/
for f in initrd.img-*; do $f $f.dist; done
cd /etc/initramfs-tools/
cp -pi modules modules.dist
vi modules
#btrfs
virtio_console
update-initramfs -k all -u
apt install initramfs-tools
#lsinitramfs /boot/initrd.img-* | grep -i btrfs
lsinitramfs /boot/initrd.img-* | grep -i console
make sure the serial console prompt goes serial
systemctl list-unit-files | grep tty systemctl disable getty@tty1.service systemctl enable serial-getty@tty1.service ls -lF /etc/systemd/system/getty.target.wants/
eventually reach the guest console without a password
passwd -d root
ready to leave the chroot
^D
now setup the kvm guest boot-loader and kernel-time serial console
ls -lh /usr/lib/syslinux/mbr/mbr.bin
dd if=/usr/lib/syslinux/mbr/mbr.bin of=$device
mkdir lala/boot/syslinux/
extlinux --install lala/boot/syslinux --device ${device}p1
cp -f /usr/lib/syslinux/modules/bios/mboot.c32 lala/boot/syslinux/
cp -f /usr/lib/syslinux/modules/bios/libcom32.c32 lala/boot/syslinux/
vi lala/boot/syslinux/syslinux.cfg
serial 0 115200
console 1
nohalt 1
default linux
prompt 1
timeout 50
label linux
linux /vmlinuz
initrd /initrd.img
append root=/dev/vda1 ro console=ttyS0,115200n8 net.ifnames=0 biosdevname=0 mitigations=off
umount -R lala/
rmdir lala/
#btrfsck ${device}p1
fsck.ext4 ${device}p1
#losetup --detach $device
qemu-nbd -d $device
ls -lhF $guest.qcow2
du -sh $guest.qcow2
# 1.5G bookworm
ls -lhF /usr/bin/qemu-system-x86_64
cat > $guest.xml <<EOF
<domain type='kvm'>
<name>$guest</name>
<memory unit='GiB'>1</memory>
<currentMemory unit='GiB'>1</currentMemory>
<vcpu placement='static'>2</vcpu>
<os>
<type arch='x86_64' machine='q35'>hvm</type>
<boot dev='hd'/>
</os>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/data/guests/$guest/$guest.qcow2'/>
<target dev='vda' bus='virtio'/>
</disk>
<interface type='bridge'>
<source bridge='virbr0'/>
<model type='virtio'/>
</interface>
<serial type='pty'>
<target type='isa-serial' port='0'>
<model name='isa-serial'/>
</target>
</serial>
<console type='pty'>
<target type='serial' port='0'/>
</console>
</devices>
</domain>
EOF
virsh list --all virsh create $guest.xml --console ^]
maybe add your SSH pubkey in there and we’re good
ping opendns.com poweroff
as root
virsh list --all | grep $guest # empty cd /data/guests/ time nice tar cSf /data/templates/$guest.tar $guest/ ls -lh /data/templates/$guest.tar # 1,1G
eventually make it available for GNS3
grub-install: error: unknown filesystem.
==> you need to see the partition, use losetup for that
while trying to launch the guest as user
error: Failed to create domain from bookworm.xml error: internal error: /usr/lib/qemu/qemu-bridge-helper --use-vnet --br=virbr0 --fd=31: failed to communicate with bridge helper: Transport endpoint is not connected stderr=failed to parse default acl file `/etc/qemu/bridge.conf'
==> I don’t remember how I solved that one
https://stackoverflow.com/questions/21596384/cannot-disable-systemd-serial-getty-service
https://unix.stackexchange.com/questions/447070/serial-getty-error-messages