Alpine data disk setup on a Raspberry PI 4B
This post was updated on the 3rd of August 2025.
Stuff used
- Raspberry Pi 4B 4GB rev 1.2
- A Seagate 4 TB USB 3.0 external HDD
- Alpine 3.22.1 (rpi)
Although the installation media was prepared on a Fedora 41 machine, the same process can be achieved by unzipping using 7-zip the tarball on Windows and installing GPT/ext4 related packages (apk add gptfdisk e2fsprogs) within Alpine after running setup-alpine (see docs)
Consider creating a
setup.mdfile with this guide (or part of it) on the bootable media so you can alwaysvi/catit on the fly from within Alpine. (source code)
Image download
- Head over to the official website
- Find the Raspberry PI dedicated section
- Click on the first
aarch64label (should download a tar.gz) - Download the relative sha256 checksum and the GPG sign as well
- Open a terminal and type the following:
curl https://alpinelinux.org/keys/ncopa.asc | gpg --import ;
gpg --verify alpine-*.tar.gz.asc
# Ignore warning of not being able to verify that the key belongs to the owner
sha256sum -c alpine-*.tar.gz.sha256
If verification went smoothly we’re ready to create a bootable media
Disk paritioning
Let’s start with an empty, clean disk.
| Size | Filesystem | Mount point | Comment |
|---|---|---|---|
| 100 MB | FAT32 | /media/boot | Boot (read-only) |
| 300 MB | ext4 | /media/setup | LBU storage + APK cache |
| - MB | ext4 | /var | Data |
lbucancommitto a path, however the apkovl backup must be at the root of the partition so it can be discovered at boot.
Here is a list of options you have on how to identify the device (/dev/sdX) of the disk:
- use
lsblk - hot-plug the disk after boot and use
dmesgto see what the kernel tells you - use
df - use the Disks application in GNOME: hot-plug the device and it should pop up in the list of devices
If you see multiple partitions, like
/dev/sdX2or/dev/sdX6you should double check if it’s the correct device
I am going to use gdisk to partition my disk, since I want to use the expert menu to write the partition layout. After typing sudo gdisk /dev/sdX:
o # Create new parition table
Y # Confirm
n # Create new partition
<enter> # First partiion
<enter> # Default is fine
+200M # Size
ef00 # EFI system partition
n # Create new partition
<enter> # Second partiion
<enter> # Default is fine
+300M # Size
8300 # Linux filesystem
n # Create new partition
<enter> # Third partiion
<enter> # Default is fine
<enter> # Utilize the whole disk
8310 # Linux /var
x # Expert menu
a # Attributes
1 # First partition
2 # Switch Legacy BIOS bootable attribute
<enter> # Exit from attributes menu
w # Write and exit from gdisk
Y # Confirm
You should now have three partitions. Check it by issuing: sudo blkid /dev/sdX? (? is a wildcard to pick any digit)
Now format the partitions with:
# Format the boot partition as FAT32, so we can modify cmdline.txt and usercfg.txt from Windows
sudo mkfs.vfat -F 32 /dev/sdX1
# Disable journaling and 64 bit ext4 options "to reduce write operations and allow the disk to
# spin down after the .apkovl and the packages have been read from the partition during the boot"
# See: https://wiki.alpinelinux.org/wiki/Diskless_Mode
sudo mkfs.ext4 -O ^has_journal,^64bit /dev/sdX2
# Variable data needs journaling to make sure that even on power loss we can recover
sudo mkfs.ext4 /dev/sdX3
Please be patient with ext4, since it may take a bit of time on big and slow disks (ie, 4TB+ external HDDs)
Bootable device
Please note that the following commands use
sudosince official docs use--same-owner, which extracts stuff as if owned by root (uid 0, gid 0) Being a FAT32 partition it’s fine to omit this option and work as non root (using fuse to mount the device).
Mount as superuser the first partition with sudo mount /dev/sdX1 /mnt.
Push the mount directory with pushd /mnt and issue (after replacing ~/Downloads with the path where you downloaded the tarball):
sudo tar -p -s --atime-preserve --same-owner --one-top-level=$PWD -zxvf ~/Downloads/alpine-*.tar.gz
Create answer file
Trust me, you want this, you waste a lot less time when experimenting. Also, please note that if you want to touch as little as possible the boot partition, consider storing it in the second partition, or on a server!
Create a setup-answers file, with you favorite editor within the /mnt boot partition mountpoint with the following content:
# Use US layout with US-INTL variant
KEYMAPOPTS="us us-intl"
# Set hostname to coffee
HOSTNAMEOPTS="-n coffee"
# Contents of /etc/network/interfaces
INTERFACESOPTS="auto lo
iface lo inet loopback
auto eth0
iface eth0 inet
address 192.168.1.106/24
gateway 192.168.1.1
hostname coffee
"
# Set timezone to Europe/Rome
TIMEZONEOPTS="-z Europe/Rome"
# Set http/ftp proxy to none
PROXYOPTS="none"
# Use "official" Alpine CDN, use "-f" to look for fastest mirror
APKREPOSOPTS="-1"
# Prompt non-root user creation
# Install OpenSSH - this is needed for sftp and scp support
SSHDOPTS="-c openssh"
# Use chrony
NTPOPTS="-c chrony"
# Perform diskless setup
DISKOPTS="none"
LBUOPTS="none"
APKCACHEOPTS="none"
I couldn’t get setup-disk to work in data mode on the /dev/sda3 partition, so I had to manually edit fstab (which is good, since I get to customize my install). A working fstab:
# This is the standard Alpine boot image.
# You may wipe all other partitions, reboot and be able to start from scratch
/dev/sda1 /media/boot vfat defaults,ro 0 0
# This is where LBU stores its backups (LBU_PATH variable)
# This is also where APK caches are stored (reinstalled at boot automatically)
/dev/sda2 /media/setup ext4 defaults,rw 0 0
# This is user data, intended for containers, network shares etc.
/dev/sda3 /var ext4 defaults,rw 0 0
We use
0in the last field, skipping fsck: it’s not installed by default at system boot. We can enable it later on with the apk cache. You may consider adding thenoatimeoption on ext4 partitions to prevent access times writing (reducing writes to disk).
After saving and closing the file you may now pop the directory with popd and unmount the disk with sudo umount /mnt.
First boot
- Plug the usb disk in the Raspberry Pi USB 3.0 port
- Connect the HDMI/micro HDMI cable
- Plug in usb keyboard
- Plug in the power plug
- Insert
rootwhen prompted to login
If your keyboard is backlit turn it off or use a different one - the USB hub can’t output enough watts to a spinning HDD and the rainbow keyboard
Data disk setup
First of all we need to locate the answers file by using ls. It should be either in /media/usb/ or /media/sda1/.
Now issue a setup-alpine -f /media/sda1/setup-answers and provide the root password and optionally create a non-root user. Provide a space separated list of DNS servers (i.e. 1.1.1.1 1.0.0.1) in order to resolve the mirrors domain names.
You now have a configured environment with internet synced time and a working package manager.
Manual disk setup
Override the fstab with:
cp /media/sda1/fstab /etc/fstab
# Make sure everyone can read it, but only root can write to it
chown root:root /etc/fstab
chmod 644 /etc/fstab
Make sure all media mountpoints exist:
mkdir /media/boot
mkdir /media/setup
Mount both media devices:
mount /dev/sda1
mount /dev/sda2
Variable Data setup
Add rsync package: apk add rsync
Remove apk cache before copying variable data: rm -rf /var/cache/apk
Mount and copy the /var directory over to the partition:
mount /dev/sda3 /mnt
rsync -Pa /var/ /mnt/
umount /mnt
Now mount the variable data partition too: mount /dev/sda3
apk setup
Open /etc/apk/repositories with vi:
- Change the local repo to
/media/boot/apks(should be/media/sda1/apksor/media/usb/apks) - Uncomment the community repo (should be the last line).
Create the apk cache directory: mkdir /media/setup/cache
Now issue: setup-apkcache /media/setup/cache.
fsck setup for ext4 partitions
After setting the apk cache you may apk add e2fsprogs, which provides fsck.ext4.
You may now set the fsck (last) field of the /var entry (/dev/sda3 in my case) within /etc/fstab to 2 (instead of 0, which would skip it)
Feel free to fsck the
/media/setupfstab entry too.
LBU seutpripped DVDs/CDs
Now set LBU_BACKUPDIR=/media/setup/ within /etc/lbu/lbu.conf.
You can finally lbu commit and reboot.
Final checks
- If you see your hostname at login then LBU was configured correctly
- If you see fsck stating that
/dev/sda3is clean at boot the apk cache was configured correctly - Alternatively, if you see openssh being loaded at boot the apk cache was configured correctly
Update the apk indecies and fetch new package versions:
apk update
apk upgrade
Do not forget to lbu commit.
Check non-root admin
Run doas whoami, insert your normal user password and make sure it outputs root
Check non-root ssh login
You can already copy over your key with ssh-copy-id <user>@<ip> from your workstation.
Insert the password and login with ssh <user>@<ip>. Do not forget to lbu commit after adding the key.
Allow root login
This is required if you skipped the user creation step
Open the /etc/ssh/sshd_config configuration and uncomment PermitRootLogin yes.
Do not forget to lbu commit and also rc-service sshd restart.
Now do ssh-copy-id root@<ip> from your workstation. Log back in the Raspberry via ssh root@<ip>.
Remember to include the authorized keys to lbu commitable list via:
lbu include /root/.ssh/authorized_keys
Again, do not forget to lbu commit once you’ve added it.
Final tweaks on boot parition
poweroff your raspberry and plug the disk back in your desktop.
Make sure to mount the boot partition (/dev/sdX1 previously) as read-write (default, unless you’re working live from your alpine system)
Update bootable read-only system
This works for restoring the boot image to its original form too. This is useful after messing up big time while tweaking files in it.
Simply wipe the partition as FAT32 and extract the tar contents to the partition root.
Remove setup helper files
You may either “update” the bootable partition to the same installed version, or simply rm fstab setup-answers.
Tweak cmdline.txt
By default the root tmpfs uses half of your ram. In my case 2 GB is too much, I need just 200 MB: add rootflags=size=200M to cmdline.txt.
Since we don’t need usb/sd modules discovery we can remove them from the modules list. This also gets rid of ln: usbdisk: File exists when mounting to /media/usb manually.
Here’s the final cmdline.txt:
modules=loop,squashfs quiet console=tty1 rootflags=size=200M
Get rid of unused files
A backup of the partition is recommended, although you may always wipe the partition and untar the original tarball.
Here’s a list of files you don’t need to boot a Raspberry Pi 4B:
- bootcode.bin
- start.elf
- fixup.dat
You may then also remove all *.dtb files, excluding bcm2711-rpi-4-b.dtb.
If you see the rainbow screen it means that you accidentaly removed the required dtb file for your board.
Please check the official docs to know what can be removed.
Update firmware
You may check which version you’re using by installing vcgencmd with apk add raspberrypi-utils-vcgencmd and running vcgencmd version
Head over to the official repo latest release and download the tarball.
Replace the following files:
bootcode.bin(not needed on Raspberry Pi 4/5 variants)start.elf(orstart4.elfon a Raspberry Pi 4 variant)fixup.dat(orfixup4.daton a Raspberry Pi 4 variant)
Switch to cut-down firmware
From official docs:
a cut-down version of the firmware that removes support for hardware blocks such as codecs and 3D as well as debug logging support; it also imposes initial frame buffer limitations. The cut-down firmware is automatically used when gpu_mem=16 is specified in config.txt.
Similarly to previous step copy the start_cd.elf+fixup_cd.dat (or start4cd.elf+fixup4cd.dat on a Raspberry Pi 4 variant) to boot directory.
Now set gpu_mem=16 in config.txt (not usercfg.txt, as it’s included from config.txt and it can’t set this parameter)
You may remove non-cut-down binary (elf) and linker (dat) files.
(Optional) Flash USB-favourable eeprom
With the aid of the Raspberry Pi imager utility you can flash the most up-to-date eeprom version, possibly with “USB-over-uSD preference”.
- Poweroff the device
- Insert just the sd card
- Power device on and wait for green flashing/green screen