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.