Alpine Linux on Zynq

Posted on 8 May 2019 By Noah Hütter


After building the Linux Kernel in a recent post we will run Alpine Linux as a leight weight distribution on the Zynq SoC

Table of Contents

Disclaimer: This code is largely copied from, licensed under MIT license.


This tutorial is built on top of the Linux on Zynq tutorial. It requires the Kernel built in the previous post. So if you want to get it working, start over at Linux on Zynq and come back here after you are done.

Clone my zynq-sandbox repository from github if you have not done so already.

git clone

Install qemu the CPU emulator.

sudo apt install qemu-user-static

Change to a suitable build folder.

mkdir -p sw/linux/build/alpine
cd sw/linux/build/alpine


Download sources

We define the base url to pull the sources


Next, download the sources:

# Alpine u-boot tar
curl -o $alpine_tar -L $alpine_url/releases/armv7/$alpine_tar

# Tools
curl -o $tools_tar -L $alpine_url/main/armv7/$tools_tar

# Firmware
curl -o $firmware_tar -L $alpine_url/main/armv7/$firmware_tar


Now we unpack the downloaded sources:

# Alpine uboot
mkdir alpine-uboot
tar -zxf $alpine_tar --directory=alpine-uboot

# Alpine tools
mkdir alpine-tools
tar -zxf $tools_tar --directory=alpine-tools --warning=no-unknown-keyword

Create initramfs

Unzip the Alpine vanilla initramfs

Create a directory and change into it

mkdir alpine-initramfs
cd alpine-initramfs

Unzip the alpine initramfs vanilla image. The gzip commands decompresses (-d) and outputs on stdout (-c). Gzip won’t decompress if it does not know the file suffix, so the file is copied to the current directory and then unpacked.

cp ../alpine-uboot/boot/initramfs-vanilla initramfs-vanilla.gz
gzip -dc initramfs-vanilla.gz | cpio -id
rm initramfs-vanilla.gz

Remove unwanted stuff

Remove kernel module configurations

rm -rf etc/modprobe.d

Remove kernel firmware (binary drivers)

rm -rf lib/firmware

Remove kernel modules

rm -rf lib/modules

Remove cache

rm -rf var

Repack initramfs

Now that the Alpine initramfs is cleaned up, we repack the initramfs.

find . | sort | cpio --quiet -o -H newc | gzip -9 > ../initrd.gz


cd ..

Generate image for U-Boot

Now that we have packed the initramfs into a compressed archive, a bootimage is generated of U-Boot.

mkimage -A arm -T ramdisk -C gzip -d initrd.gz uInitrd

Linux modules

Copy modules from our Linux Kernel

We want to use the modules from the previously built Linux kernel. First we create a target directory to copy these files.

mkdir -p $modules_dir/kernel

Now look for all .ko files in the kernel and copy them to the new location. This command looks for all .ko files, sets user and group to 0 and copies them to the target location.

find $linux_dir -name \*.ko -printf '%P\0' | tar --directory=$linux_dir --owner=0 --group=0 --null --files-from=- -zcf - | tar -zxf - --directory=$modules_dir/kernel

Copy the modules order and builtin files to the destination.

cp $linux_dir/modules.order $linux_dir/modules.builtin $modules_dir/

From the copied kernel modules we generate modules.dep and map files.

depmod -a -b alpine-modloop $linux_ver

Kernel modules form Alpine

Now we copy selected firmware binaries from the alpine firmware archive into our alpine-modloop directory.

tar -zxf $firmware_tar --directory=alpine-modloop/lib/modules --warning=no-unknown-keyword --strip-components=1 --wildcards lib/firmware/ar* lib/firmware/rt*

Additional firmware download and untar:

add_fw="linux-firmware-ath9k_htc-20190322-r0.apk linux-firmware-brcm-20190322-r0.apk linux-firmware-rtlwifi-20190322-r0.apk"
for tar in $add_fw
  curl -L $url -o $tar
  tar -zxf $tar --directory=alpine-modloop/lib/modules --warning=no-unknown-keyword --strip-components=1

Pack kernel modules and firmware

Now we pack the kernel modules and firmware into a squashfs file using xz compression.

mksquashfs alpine-modloop/lib modloop -b 1048576 -comp xz -Xdict-size 100%

Create root

Now its time to create the root partition and some empty directories.


Create directory.

mkdir -p $root_dir/usr/bin
mkdir -p $root_dir/etc
mkdir -p $root_dir/etc/apk

Create an apk cache where the SD-card will be mounted on the target.

mkdir -p $root_dir/media/mmcblk0p1/cache
ln -s /media/mmcblk0p1/cache $root_dir/etc/apk/cache

Copy contents from apline root dir into alpine-root.

cp -r alpine/root/etc $root_dir/

Copy alpine binary and qemu arm CPU emulator to install alpine. Further, for the chroot environment to find the alpine servers, our hosts resolv config is copied.

cp -r alpine-tools/sbin $root_dir/
cp /usr/bin/qemu-arm-static $root_dir/usr/bin/
cp /etc/resolv.conf $root_dir/etc/

We now install alpine by running apk.static in a chroot.

sudo chroot $root_dir /sbin/apk.static \
  --repository $alpine_url/main \
  --update-cache --allow-untrusted --initdb \
  add alpine-base

Create a repositories file for upstream repository path.

echo $alpine_url/main > $root_dir/etc/apk/repositories
echo $alpine_url/community >> $root_dir/etc/apk/repositories


Now we chroot into the alpine-base installation and complete further installations.

sudo chroot $root_dir /bin/sh

Install some packages

apk update
apk add haveged openssh iw iptables curl wget less nano bc dcron

init system

Alpine-linux uses OpenRC for its init system. More infos can be found here. Add services to the boot runlevel. From the alpine documentation:

Generally the only services you should add to the boot runlevel are those which deal with the mounting of filesystems, set the initial state of attached peripherals and logging

ln -s /etc/init.d/bootmisc etc/runlevels/boot/bootmisc
ln -s /etc/init.d/hostname etc/runlevels/boot/hostname
ln -s /etc/init.d/hwdrivers etc/runlevels/boot/hwdrivers
ln -s /etc/init.d/modloop etc/runlevels/boot/modloop
ln -s /etc/init.d/swclock etc/runlevels/boot/swclock
ln -s /etc/init.d/sysctl etc/runlevels/boot/sysctl
ln -s /etc/init.d/syslog etc/runlevels/boot/syslog
ln -s /etc/init.d/urandom etc/runlevels/boot/urandom

For the shutdown runlevel:

Changes to the shutdown runlevel and then halts the host.

ln -s /etc/init.d/killprocs etc/runlevels/shutdown/killprocs
ln -s /etc/init.d/mount-ro etc/runlevels/shutdown/mount-ro
ln -s /etc/init.d/savecache etc/runlevels/shutdown/savecache

For the sysinit runlevel:

Brings up any system specific stuff such as /dev, /proc and optionally /sys for Linux based systems

ln -s /etc/init.d/devfs etc/runlevels/sysinit/devfs
ln -s /etc/init.d/dmesg etc/runlevels/sysinit/dmesg
ln -s /etc/init.d/mdev etc/runlevels/sysinit/mdev

Add some services to the default runlevel.

rc-update add local default
rc-update add dcron default
rc-update add haveged default
rc-update add sshd default


Setup ssh deamon.

# permit root login
sed -i 's/^#PermitRootLogin.*/PermitRootLogin yes/' etc/ssh/sshd_config

Change root password.

echo root:$passwd | chpasswd

Set hostname.

setup-hostname $hostname
hostname $hostname

Add some aliases to the root .profile.

cat <<- EOF_CAT > root/.profile
alias rw='mount -o rw,remount /media/mmcblk0p1'
alias ro='mount -o ro,remount /media/mmcblk0p1'

Configure alpine local backup to backup to SD card partition 1. Also include some directories. lbu only includes /etc per default. More documentation on lbu here.

sed -i 's/^# LBU_MEDIA=.*/LBU_MEDIA=mmcblk0p1/' etc/lbu/lbu.conf
lbu add root
lbu delete etc/resolv.conf
lbu delete root/.ash_history

Create backup

lbu commit -d

Finish up

We now exit the chroot and restore our hostname.

sudo hostname -F /etc/hostname

Create ZIP

We now created all necessary files folders and filesystems. For convenience we copy all needed ressources in a new folder.

mkdir -p $zip_dir

cp ../boot.bin $zip_dir/
cp ../uImage $zip_dir/
cp ../devicetree.dtb $zip_dir/
cp ../uEnv.txt $zip_dir/

cp -r $root_dir/media/mmcblk0p1/cache $zip_dir/
cp $root_dir/media/mmcblk0p1/red-pitaya.apkovl.tar.gz $zip_dir/
cp modloop $zip_dir/
cp uInitrd $zip_dir/

Zip all files that need to be copied to the SD-Card.

zip -r red-pitaya-alpine-3.9-armv7-`date +%Y%m%d`.zip $zip_dir/