This is a guide to install alpine, based on my own likings. It is a relatively easy install.
This installation guide is very inspired and based on Hugo's Installation Guide.
(Some) security considerations
This install is a bit more secure because it uses an encrypted filesystem (LUKS on top of LVM).
The whole is configured to work with UEFI + Secureboot, and the disk is automatically decrypted with the TPM (this secure chip inside your laptop) using Clevis, meaning, if you boot on your laptop, your file contents will be decrypted automatically. You will need to manually enter your password if the storage drive is used outside of your computer (evil maid attack).
To secure your laptop better, enable Secure Boot, and set a very strong BIOS/UEFI password.
A more secure approach would be to use something like systemd-homed that encrypts each user's home directory separately, but that requires systemd... There's a similar tool for alpine called pam_mount, but it's not compatible with home directories created with systemd-homed, and I haven't figured a very nice way to make it work on Alpine.
Setup the installer
Download the alpine installation disk from the Alpine downloads page, flash it to a USB and boot it up.
Configuring Alpine
Alpine has a very nice setup-alpine
script that setups the Alpine
installation. This is different from Arch where you'd need to do everything
manually. We are going to do it to configure most stup, until we arrive to the
Partitioning stage.
- Keyboard:
us
. - Keyboard variation:
us
. - Hostname:
name.vixalien.com
. - Network adapter:
wlan0
. - Wifi network.
- Wifi password.
- IP address:
dhcp
. - Networks:
done
. - Manual network configuration:
n
. - root password:
stronkpassword
- Time zone:
Africa/Kigali
. - User account:
alien
. This user will automatically be a member of the wheel group, and has (by default) privileges to usedoas
(alpine's alternative tosudo
). - The defaults are fine for all remaining steps.
Make Sure to stop the
setup-alpine
tool when they ask for disk, as we will do it manually.
Partitioning
Firstly, we will need to create a boot partition (aka EFI System Partition) to hold all our kernel information and bootloader (optional). Please make the partition 500 MB large, otherwise some distros like Fedora might refuse it, and the space might run out faster than you think if you decide to make backups of your UKIs or go distro-hopping, so 1GB is actually a better recommendation.
Then we are going to create an LVM partition that will be encrypted and have
volumes for all our other partitions (alpine, home, swap...). I ('d) like to
setup one partition called linux
so it can be reused by my other linux
installations.
Installing the necessary packages.
For partitioning, we will use gdisk. Let's also install other packages for ext4 and btrfs filesystems.
apk add lsblk gptfdisk btrfs-progs e2fsprogs
Activate the btrfs kernel module:
modprobe btrfs
cryptsetup is needed for LUKS encryption
apk add cryptsetup
LVM
apk add lvm2
Overwriting the disk
It might be a good idea to overwrite the disk using a tool like haveged
to
clear any leftover data.
TODO: there is probably an alternative for SSDs
Setup Disks
gdisk /dev/nvme0n1
Use n
to create 2 partitions:
- EFI partition. size:
512M
. GUID:ef00
(EFI System Partition) - LVM partition. size:
(leave blank)
. GUID:8309
(Linux LUKS)
For more info about gdisk Hex codes see https://wiki.archlinux.org/title/GPT_fdisk#Partition_type
Populate /dev
with new partitions:
partprobe /dev/nvme0n1
Identify your partitions
Find your partition names using lsblk
:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 511.9G 0 disk
├─nvme0n1p1 259:1 0 500M 0 part
└─nvme0n1p2 259:2 0 511.5M 0 part
In this case, /dev/nvme0n1p1
is the EFI partition, while /dev/nvme0n1p2
will
be our LUKS partition.
Configuring LUKS
This step will ask for a password to encrypt your whole disk with. Remember it, and make it strong.
cryptsetup luksFormat /dev/nvme0n1p2
You might want to familiarise yourself with different LUKS options. I find the defaults okay.
Open the LUKS partition:
cryptsetup luksOpen /dev/nvme0n1p2 lvmcrypt
LVM Physical and Logical Volumes
Create a physical volume
pvcreate /dev/vg0/lvmcrypt
Create a virtual volume named vg0
(or something else, this one is memorable)
vgcreate vg0 /dev/vg0/lvmcrypt
Create partitions
Swap partition. I have a 32GB RAM laptop:
lvcreate -L 32G vg0 -n swap # I have a 32GB RAM laptop
lvcreate -L 50G vg0 -n alpine # root
lvcreate l 100%FREE vg0 -n home
Create file systems
mkfs.exfat /dev/nvme0n1p1
mkfs.btrfs /dev/vg0/alpine
mkfs.ext4 /dev/vg0/home
Activate swap
mkswap /dev/vg0/swap
swapon /dev/vg0/swap
Create Btrfs Subvolumes
Temporarily mount the alpine
partition to create subvolumes.
mount /dev/vg0/alpine /mnt
Create the subvolumes adapted from Snapper's Suggested filesystem layout
btrfs subvolume create /mnt/@ # /
btrfs subvolume create /mnt/@var_log # /var/log
Find the id of the @
subvolume. Note it as <root-subvol-id>
. It will
probably be 256 or something.
btrfs subvolume list /mnt
Change the default subvolume to @
(Replace <root-subvol-id>
with the ID you
got from the previous step).
btrfs subvolume set-default <root-subvol-id> /
Unmount the partition
umount /mnt
Mount partitions
Create mountpoints and mount our partitions and subvolumes
mount /dev/vg0/alpine -o subvol=@ /mnt/
# Create mountpoints
mkdir -p /mnt/boot /mnt/boot /mnt/var/log
# Mount the remaining subvolumes
mount /dev/vg0/alpine -o subvol=@var_log /mnt/var/log
# Mount the efi system partition
mount /dev/nvme0n1p1 /mnt/boot
# Mount the home partition
mount /dev/vg0/home /mnt/home
Installation
Install a base alpine system
BOOTLOADER=none setup-disk -k edge /mnt
The BOOTLOADER=none
tells the script to not install any bootloader (grub is
the default), and -k edge
tells the script to install the edge
kernel
instead of the lts
one.
Chroot into the filesystem
chroot /mnt
mount -t proc proc /proc
mount -t devtmpfs dev /dev
Switch to the edge branch, and enable the community and testing repositories.
This is done by editing /etc/apk/repositories
and replacing its contents with:
http://dl-cdn.alpinelinux.org/alpine/edge/main
http://dl-cdn.alpinelinux.org/alpine/edge/community
http://dl-cdn.alpinelinux.org/alpine/edge/testing
You can choose a mirror closer to you if you want
Setup a local apk cache
I like having my downloaded apks available locally. In case I want to reinstall
them or something... You can skip this step if you want a really minimal design.
When prompted, say /var/cache/apk
for the cache directory.
setup-apkcache
(No) bootloader
The next step would be to install a bootloader like GRUB or systemd-boot/gummiboot. However, we don't need one of them since we instead create a Unified Kernel Image (UKI) which can be directly booted by the UEFI firmware, hence removing the need for a traditional bootloader. A UKI contains the following, and some more:
- The kernel itself
- The kernel’s command line parameters
- A small stub that execute the kernels with that command line
- The initramfs (or initrd): a small read-only filesystem with the necessary userspace utilities to boot into the main system.
The stub itself is provided by the gummiboot-efistub
package. It is considered
deprecated, but no solid alternative is available. The bundle itself is built by
efi-mkuki
, and secureboot-hook
will rebuild the bundle after each kernel
upgrade.
apk add secureboot-hook gummiboot-efistub
Install blkid
which will be used in a moment. This tool prints the UUID (and a
few other details) for a specified partition. This is the recommended way to
address a partition ambiguously:
apk add blkid
secureboot-hook
Edit /etc/kernel-hooks.d/secureboot.conf
with the following contents.
cmdline=/etc/kernel/cmdline
signing_disabled=yes
output_dir="/boot/EFI/Linux"
output_name="alpine-linux-{flavor}.efi"
/<efi>/EFI/Linux
is a more or less standard directory, and will be discovered
by systemd-boot
if you have that installed.
Signing is disabled only temporarily until I install the proper keys.
/etc/kernel/cmdline
Also create a file /etc/kernel/cmdline
that will contain arguments passed to
the kernel from the bootloader (UKI, in this case).
root=UUID=5021db58-cc3a-4829-a630-2d468f8d1761
rootflags=subvol=@
rootfstype=btrfs
cryptroot=UUID=0db973a0-1b95-4a23-a63f-cb6248fe2bf7
cryptdm=lvmcrypt
cryptkey
modules=sd-mod,btrfs,nvme
quiet
ro
The root
UUID can be determined with:
blkid /dev/vg0/alpine >> /etc/kernel/cmdline
The cryptroot
UUID:
blkid /dev/nvme0n1p2 >> /etc/kernel/cmdline
initrams
Edit /etc/mkinitfs/mkinitfs.conf
to add features
which are needed for our
encrypted root setup to work. While editing this line, it is also safe to delete
virtio
, which is used only in virtual machines. Also add kms
to enable
kernel mode setting. These features will be included in the generated
initramfs.
features="ata base ide scsi usb btrfs ext4 lvm kms keymap nvme cryptsetup cryptkey resume"
disable_trigger=yes
Boot Entry
We use efibootmgr
to create a boot entry that (hopefully) shows in the EFI
firmware, although you can always use the UFI's boot from file function or
execute the UKI from a UEFI shell. I heard reports that the boot entries may
disappear after firmware upgrades or other vendor EFI quirks, so bear that in
mind.
First exit the chroot using exit
. We can't use efibootmgr
inside the chroot
because it won't be able to read and set the EFI variables, mounted at
/sys/firmware/efi/efivars
. Then install efibootmgr:
apk add efibootmgr
Then create a boot entry named "Alpine Linux".
efibootmgr --disk /dev/nvme0n1 --part 1 --create --label 'Alpine Linux' --load /EFI/Linux/alpine-linux-edge.efi --verbose
Note: This procedure only needs to be done once; after that the Unified Kernel Image will be generated automatically every time the kernel is upgraded.
Finally, trigger the newly created kernel hook so that all the right files are
copied into /boot
apk fix kernel-hooks
Hibernation
To setup hibernation, we'll first need to find the UUID of our swap partition:
lsblk -f
Edit the /etc/kernel/cmdline
to let the system know where you will be resuming
from.
resume=UUID=<UUID of /dev/vg0/swap>
Enable the swap service during boot:
rc-update add swap default
Install zzz
and test hibernation
apk add zzz
zzz -Z # or ZZZ
You can now reboot and test your system
Desktop
Fonts
Installing the Noto fonts make almost every characters rendered (CJK and emoji):
apk add font-noto font-noto-cjk font-noto-extra font-noto-emoji
Install my preferred fonts
apk add font-jetbrains-mono font-liberation-serif
And configure fontconfig to use them at /etc/fonts/local.conf
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<alias>
<family>sans-serif</family>
<prefer>
<family>Cantarell</family>
</prefer>
</alias>
<alias>
<family>monospace</family>
<prefer>
<family>JetBrains Mono</family>
</prefer>
</alias>
<alias>
<family>serif</family>
<prefer>
<family>Liberation Serif</family>
</prefer>
</alias>
</fontconfig>
Snapper
Snapper is a tool to automatically or manually take snapshots of btrfs systems. I use it because I sometimes mess up my root system. Let's install it and setup up our first snapshot.
Remember: Snapshots are NOT backups.
apk add snapper
snapper -c root create-config /
This will create a new subvolume at /.snapshots
. Each snapshot will be stored
at /.snapshot/<snapshot-number>/snapshot
. It will also add a new line to
/etc/conf.d/snapper
.
Create a first snapshot:
snapper -c config create --description 'Base Installation'
You can also use LVM snaphots, but that is an alternative I have not explored yet. It like a more interesting option tbf.
Enable & Start function
I like to have this function handy. It is synonymous to
systemctl enable --now
. You can put it in /etc/profile or somewhere
rc-init() {
if [ $# -lt 1 ] || [ $# -gt 2 ]
then
>&2 echo "Invalid number of arguments provided (1-2 acceptable)"
return 1
fi
RUNLEVEL="${2:-default}";
rc-service $1 start
rc-update add $1 $RUNLEVEL;
}
rc-deinit() {
if [ $# -lt 1 ] || [ $# -gt 2 ]
then
>&2 echo "Invalid number of arguments provided (1-2 acceptable)"
return 1
fi
RUNLEVEL="${2:-default}";
rc-service $1 stop
rc-update del $1 $RUNLEVEL;
}
GNOME
Setup the GNOME Desktop Environment (what I use, No I don't use sway or hyprland yet:))
setup-desktop gnome
Allow updates to be carried out in GNOME Software.
rc-init apk-polkit-server
Allow switching the power profiles in the quick settings
apk add power-profiles-daemon
If you have a convertible, allow turning your laptop to flip it
apk add iio-sensor-proxy
Hardware acceleration
apk add intel-media-driver
NetworkManager
Setup NetworkManager to manage your... network. Also setup WiFi and a TUI
(nmtui
). I also prefer using iwd
instead of wpa_supplicant
as the actual
WiFi backend, since it's what I'm familiar with.
apk add networkmanager networkmanager-wifi networkmanager-wifi networkmanager-dnsmasq
I like to use this configuration:
[main]
dhcp=internal
plugins=keyfile
dns=dnsmasq
[device]
wifi.scan-rand-mac-address=yes
wifi.backend=wpa_supplicant
[connectivity]
uri=http://nmcheck.gnome.org/check_network_status.txt
There are probably other configuration options to set.
Enable and activate the service:
rc-init networkmanager
Since we are now using NetworkManager to manage our connections, we can disable the default networking service.
rc-update del networking
You might also see that chronyd
takes a while to sync on boot. We can tell it
to do that in the background on boot instead by editing /etc/conf.d/chronyd
and setting
FAST_STARTUP=yes
Bluetooth
apk add bluez bluez-openrc
Enable the Bluetooth experimental features to view the battery charge of your
bluetooth earphones at /etc/bluetooth/main.conf
Experimental=True
Reboot or load the kernel module
modprobe btusb
Start & enable the bluetooth service
rc-service bluetooth start
rc-update add bluetooth default
Sound
By default, checking dmesg
seems to indicate missing firmware:
> dmesg | grep firmw
[ 1.096997] i915 0000:00:02.0: [drm] Finished loading DMC firmware i915/tgl_dmc_ver2_12.bin (v2.12)
[ 36.239398] iwlwifi 0000:00:14.3: loaded firmware version 77.2df8986f.0 QuZ-a0-hr-b0-77.ucode op_mode iwlmvm
[ 36.363387] Bluetooth: hci0: Minimum firmware build 1 week 10 2014
[ 36.365863] Bluetooth: hci0: Found device firmware: intel/ibt-19-0-4.sfi
[ 36.447917] sof-audio-pci-intel-tgl 0000:00:1f.3: Direct firmware load for intel/sof/sof-tgl.ri failed with error -2
[ 36.447919] sof-audio-pci-intel-tgl 0000:00:1f.3: error: sof firmware file is missing, you might need to
[ 36.447921] sof-audio-pci-intel-tgl 0000:00:1f.3: error: failed to load DSP firmware -2
[ 36.714932] psmouse serio1: trackpoint: Elan TrackPoint firmware: 0xa1, buttons: 3/3
[ 38.519487] Bluetooth: hci0: Waiting for firmware download to complete
apk add sof-firmware
Reboot or try to load the kernel module using the relevant soundcard name found
using find /lib/modules/* -type f -name '*.zst' -name '*sof*' -name '*tgl'
:
modprobe sof-audio-pci-intel-tgl
Install PipeWire packages and friends.
apk pipewire wireplumber pipewire-pulse pipewire-alsa pipewire-spa-bluez gst-plugin-pipewire
Future considerations
- Use Unl0kr
- Use Clevis
References: