There are times when it's good to have a serial console. If this is not true for you, then you're in the wrong place. This document is about modifying an Alpine Linux ISO to use a serial console on boot. The process was tested on Debian Linux (Bullseye) with the 3.14.1 standard release Alpine ISO. UEFI boot may not work - I did not test it.

Download/Extract source image

Note: there might be a newer version of Alpine to download.

user@host:~$ mkdir -p ~/tmp/mnt
user@host:~$ mkdir -p ~/tmp/alpine_serial

user@host:~$ cd tmp
user@host:~/tmp$ wget https://dl-cdn.alpinelinux.org/alpine/v3.14/releases/x86_64/alpine-standard-3.14.1-x86_64.iso

Mount the ISO and copy the contents to somewhere we can edit (the ISO is read-only). The /. in the cp command is to make sure we include the hidden file(s) in our copy.

user@host:~/tmp$ sudo mount alpine-standard-3.14.1-x86_64.iso mnt
mount: /home/user/tmp/mnt: WARNING: source write-protected, mounted read-only.

user@host:~/tmp$ sudo cp -a mnt/. alpine_serial

user@host:~/tmp$ ls -la alpine_serial
total 24
dr-xr-xr-x  5 root root 4096 Aug  6 00:28 ./
drwx------ 12 user user 4096 Aug 10 20:33 ../
-r--r--r--  1 root root   30 Aug  6 00:28 .alpine-release
dr-xr-xr-x  3 root root 4096 Aug  6 00:28 apks/
dr-xr-xr-x  5 root root 4096 Aug  6 00:28 boot/
dr-xr-xr-x  3 root root 4096 Aug  6 00:28 efi/

Tweak for serial console

The following uses sudo because the files are read-only and owned by root.

Make a backup of syslinux.cfg then edit to add SERIAL 0 115200 at the start of the file, and add console=ttyS0,115200 to the end of the APPEND line. I also delete the quiet option because I like to see what's going on during boot. The diff shows the changes, if you're familiar with diff...

user@host:~/tmp$ sudo cp alpine_serial/boot/syslinux/syslinux.cfg alpine_serial/boot/syslinux/syslinux.cfg.bak

user@host:~/tmp$ sudo vim alpine_serial/boot/syslinux/syslinux.cfg

user@host:~/tmp$ sudo diff -u alpine_serial/boot/syslinux/syslinux.cfg.bak alpine_serial/boot/syslinux/syslinux.cfg
--- alpine_serial/boot/syslinux/syslinux.cfg.bak  2021-08-10 20:36:29.849569528 +1200
+++ alpine_serial/boot/syslinux/syslinux.cfg      2021-08-10 20:37:28.042229283 +1200
@@ -2,9 +2,11 @@
+SERIAL 0 115200
 PROMPT 1
 DEFAULT lts

 LABEL lts
 MENU LABEL Linux lts
 KERNEL /boot/vmlinuz-lts
 INITRD /boot/initramfs-lts
 FDTDIR /boot/dtbs-lts
-APPEND modules=loop,squashfs,sd-mod,usb-storage quiet
+APPEND modules=loop,squashfs,sd-mod,usb-storage console=ttyS0,115200

Build new ISO

The build options are ones I copied from the internet - they seem to work. The only particularly interesting one (to me) is that -r sets ownership of all files to be root, all files/directories readable/traversable by all, and read-only.

user@host:~/tmp$ sudo genisoimage -r -J -b boot/syslinux/isolinux.bin -c boot/syslinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -o ./alpine_serial.iso ./alpine_serial
I: -input-charset not specified, using utf-8 (detected in locale settings)
Using NCURS000.APK;1 for  ./alpine_serial/apks/x86_64/ncurses-terminfo-base-6.2_p20210612-r0.apk (ncurses-libs-6.2_p20210612-r0.apk)
Using ALPIN000.APK;1 for  ./alpine_serial/apks/x86_64/alpine-baselayout-3.2.0-r16.apk (alpine-base-3.14.1-r0.apk)
Using OPENS000.APK;1 for  ./alpine_serial/apks/x86_64/openssh-client-common-8.6_p1-r2.apk (openssh-sftp-server-8.6_p1-r2.apk)
Using OPENS001.APK;1 for  ./alpine_serial/apks/x86_64/openssh-sftp-server-8.6_p1-r2.apk (openssh-keygen-8.6_p1-r2.apk)
Using IFUPD000.APK;1 for  ./alpine_serial/apks/x86_64/ifupdown-ng-wifi-0.11.3-r0.apk (ifupdown-ng-ppp-0.11.3-r0.apk)
Using OPENS002.APK;1 for  ./alpine_serial/apks/x86_64/openssh-keygen-8.6_p1-r2.apk (openssh-server-common-8.6_p1-r2.apk)
Using WPA_S000.APK;1 for  ./alpine_serial/apks/x86_64/wpa_supplicant-openrc-2.9-r14.apk (wpa_supplicant-2.9-r14.apk)
Using BUSYB000.APK;1 for  ./alpine_serial/apks/x86_64/busybox-initscripts-3.2-r2.apk (busybox-suid-1.33.1-r3.apk)
Using OPENS003.APK;1 for  ./alpine_serial/apks/x86_64/openssh-server-common-8.6_p1-r2.apk (openssh-8.6_p1-r2.apk)
Using OPENS004.APK;1 for  ./alpine_serial/apks/x86_64/openssh-8.6_p1-r2.apk (openssh-client-default-8.6_p1-r2.apk)
Using IFUPD001.APK;1 for  ./alpine_serial/apks/x86_64/ifupdown-ng-ppp-0.11.3-r0.apk (ifupdown-ng-0.11.3-r0.apk)
Using HAVEG000.APK;1 for  ./alpine_serial/apks/x86_64/haveged-1.9.14-r1.apk (haveged-openrc-1.9.14-r1.apk)
Using PPP_P000.APK;1 for  ./alpine_serial/apks/x86_64/ppp-passprompt-2.4.9-r0.apk (ppp-passwordfd-2.4.9-r0.apk)
Using BUSYB001.APK;1 for  ./alpine_serial/apks/x86_64/busybox-suid-1.33.1-r3.apk (busybox-1.33.1-r3.apk)
Using OPENS005.APK;1 for  ./alpine_serial/apks/x86_64/openssh-client-default-8.6_p1-r2.apk (openssh-server-8.6_p1-r2.apk)
Using E2FSP000.APK;1 for  ./alpine_serial/apks/x86_64/e2fsprogs-libs-1.46.2-r0.apk (e2fsprogs-1.46.2-r0.apk)
Size of boot image is 4 sectors -> No emulation
  7.14% done, estimate finish Tue Aug 10 20:39:51 2021
 14.27% done, estimate finish Tue Aug 10 20:39:51 2021
 21.39% done, estimate finish Tue Aug 10 20:39:51 2021
 28.52% done, estimate finish Tue Aug 10 20:39:51 2021
 35.64% done, estimate finish Tue Aug 10 20:39:51 2021
 42.78% done, estimate finish Tue Aug 10 20:39:51 2021
 49.89% done, estimate finish Tue Aug 10 20:39:51 2021
 57.03% done, estimate finish Tue Aug 10 20:39:51 2021
 64.14% done, estimate finish Tue Aug 10 20:39:51 2021
 71.28% done, estimate finish Tue Aug 10 20:39:51 2021
 78.40% done, estimate finish Tue Aug 10 20:39:51 2021
 85.53% done, estimate finish Tue Aug 10 20:39:51 2021
 92.65% done, estimate finish Tue Aug 10 20:39:51 2021
 99.77% done, estimate finish Tue Aug 10 20:39:51 2021
Total translation table size: 2048
Total rockridge attributes bytes: 10942
Total directory bytes: 26624
Path table size(bytes): 116
Max brk space used 23000
70162 extents written (137 MB)
user@host:~/tmp$

Tweak permissions and observe the glory of our new ISO.

user@host:~/tmp$ sudo chmod a+r alpine_serial.iso
user@host:~/tmp$ ls -lh *.iso
-rw-r--r--  1 root   root   138M Aug 10 20:39  alpine_serial.iso

Test

If you have qemu installed, you can boot the image and see if it works.

user@host:~/tmp$ qemu-system-x86_64 -boot d -cdrom alpine_serial.iso -m 512 -nographic -serial mon:stdio

References