alpine-qemu-install =================== Unattended install of Alpine Linux onto a disk image, driven entirely over the serial console. No keypresses, no graphics, no interaction. What it does ------------ Given an Alpine virt ISO and a disk image you created beforehand, the script: - extracts the kernel, initramfs, and modloop from the ISO - builds an apkovl overlay containing the kernel modules and a first-boot script - boots that kernel under QEMU with the disk attached as /dev/vda - runs setup-alpine non-interactively against a pre-baked answer file - patches the installed root password, SSH key, and serial getty - powers the guest off when finished The disk image is the target. It appears as /dev/vda inside the guest and is erased. After install the layout is vda1=boot, vda2=swap, vda3=root. Requirements ------------ On the host: - 7z (7zip or p7zip-full; extracting the modloop squashfs needs it) - GNU tar - openssl - qemu-system-x86_64 KVM is used automatically when /dev/kvm is writable, otherwise the install runs under plain emulation (slower, but works). You also need an x86_64 Alpine virt ISO and a disk image created up front with qemu-img. qcow2 is recommended: it allocates sparsely and supports snapshots. Any other format qemu-img writes also works, since QEMU detects it. Usage ----- Create a disk, then run the install: qemu-img create -f qcow2 disk.qcow2 2G ROOT_PASSWORD=changeme \ ./alpine-qemu-install alpine-virt-3.20.0-x86_64.iso disk.qcow2 The install log streams to your terminal. When it reaches the end the guest powers off and the script exits. Boot the resulting image however you like, for example: qemu-system-x86_64 -m 1024 -nographic -drive file=disk.qcow2,if=virtio Configuration ------------- Configuration is by environment variable. Only ROOT_PASSWORD is required. - ROOT_PASSWORD root password for the installed system (required) - ROOT_PUBKEY an authorized_keys line; enables root SSH login - SSHD openssh, dropbear, or none (default openssh) - VM_HOSTNAME installed hostname (default alpine) - APK_MIRROR apk mirror base URL (default https://dl-cdn.alpinelinux.org/alpine) - RAM_MB guest memory in MB during install (default 1024) - SMP guest CPU count during install (default 2) The mirror must be reachable during install: setup-alpine fetches packages before the disk is even partitioned. How it works ------------ QEMU's vvfat is unreliable for the ~150 MB modloop file, and a failed modloop mount leaves the initramfs without /lib/modules, so setup-alpine cannot modprobe, partition, or run post-install scripts. To avoid that path, the script unpacks the modloop squashfs on the host and ships the modules inside the apkovl overlay instead. Only one small file goes on the vvfat drive: the apkovl tarball itself. The first-boot work runs from /etc/local.d/autoinstall.start via the default runlevel's local service. It removes itself before setup-disk copies the overlay onto the target, so the trigger never fires on the installed system. Debugging --------- The install is fully visible. QEMU runs with -nographic, so everything the first-boot script does is logged to ttyS0, which is your terminal. The script runs under set -x, so each command is printed before it runs. QEMU runs with -no-reboot, so on success or failure the guest stays down and the full log remains on screen. To leave QEMU manually, press Ctrl-a then x. The guest reports distinct success and failure values through qemu's isa-debug-exit device. The script exits 0 only when the guest reported completion, 3 when the first-boot script failed, and 1 when qemu stopped without any result, including a manual Ctrl-a x. Common failures: - "need 7z" or "need qemu-system-x86_64": install the missing host tool. - "modloop layout changed": install 7zip or p7zip-full, or the ISO changed and the modloop extraction needs updating. - "/dev/vda did not appear": the disk image was not attached or is missing. - apk fetch errors: the mirror is unreachable or APK_MIRROR is wrong. Security -------- ROOT_PASSWORD is hashed on the host; openssl reads it on stdin, so the plaintext never reaches the guest, the apkovl, the set -x install log, or a process listing. The hash is kept out of the log too, but any crypt hash can be attacked offline, so pick a real password for images you keep. Root SSH login is key-only: ROOT_PUBKEY installs the key, and Alpine's default PermitRootLogin prohibit-password refuses password logins over SSH.