ZFS now has native support for encryption. This has many advantages over ZFS on LUKS including multi-disk, encrypted zfs send, portable across *BSD/Linux and others.
For a Root on ZFS guide with native encryption, see here.
This documentation describes how to set up Alpine Linux using ZFS with a pool that is located in an encrypted partition. To encrypt the partition the Device Mapper crypt (dm-crypt) module and Linux Unified Key Setup (LUKS) is used.
Note that you must install the
/boot/ directory on an unecrypted partition to boot correctly.
We'll be using the syslinux bootloader and traditional BIOS booting.
- An instance Alpine on a medium other than the one you'll boot from, see official installation guide.
Hard Disk Device Name
The following documentation uses the
/dev/sda device as the installation destination. If your environment uses a different device name for your hard disk, use the corresponding device name in the examples.
Setting up Alpine Linux Using ZFS on Top of a LUKS Partition
To install Alpine Linux in a ZFS pool on top of a LUKS encrypted partition, you cannot use the official installation procedure. The installation requires several manual steps you must run in the Alpine Linux Live CD environment.
Preparing the Installation Environment
Before you begin to install Alpine Linux on the medium you intend to boot from, prepare the installation you already have:
- Update the
# apk update
- Install the following packages required to set up ZFS and LUKS:
# apk add cryptsetup e2fsprogs syslinux zfs zfs-$(uname -r | rev | cut -d'-' -f1 | rev) # modprobe zfs
Creating the Partition Layout
Linux requires an unencrypted
/boot/ partition to boot. You can assign the remaining space for the encrypted ZFS pool.
- Start the
fdiskutility to set up partitions:
# fdisk /dev/sda
- Create the
100mto create a new 100 MB primary partition.
- Create the
- Set the
- Set the
- Create the LUKS partition:
2to start creating the next partition. Press
Enterto select the default start cylinder. Enter the partition size. For example,
512mfor 512 MB or
5gfor 5 GB. Alternatively, press
Enterto set the maximum available size.
- To verify the settings, press
p. The output should look similar to this:
- To verify the settings, press
Device Boot Start End Sectors Size Id Type /dev/sda1 * 2048 206847 204800 100M 83 Linux /dev/sda2 206848 41943039 41736192 19.9G 83 Linux
wto save the changes.
- Optionally, fill the LUKS partition with random values:
# dd if=/dev/urandom of=/dev/sda2 bs=1M
Encrypting the ZFS Partition
- To encrypt the partition which will later contain the LVM PV:
# cryptsetup luksFormat /dev/sda2
- If you prefer setting an individual hashing algorithm and hashing schema:
- To run a benchmark:
# cryptsetup benchmark
- To encrypt the partition using individual settings, enter, for example:
# cryptsetup -v -c aes-xts-plain64 -s 256 --hash sha256 --iter-time 2000 --use-urandom luksFormat /dev/sda2
cryptsetup --help for options.
Creating the filesystems
- Open the LUKS partition:
# cryptsetup open --type luks /dev/sda2 crypt
Creating the ZFS pool
# zpool create -o ashift=12 -O normalization=formD -O atime=off -m none -R /mnt -O compression=lz4 tank /dev/mapper/crypt
Meaning of the
zpool create options:
|Creating the zpool
|Set the default Unicode (UTF-8) normalization to 'formD'
|Disabling updates to file access time. This reduces writes to disk, but might cause issues with mailers, like
|No mountpoint, as we'll handle this later.
|Set the altroot to
/mnt. It's like a temporary mountpoint for the pool.
|Use lz4 compression for the pool. Is generally recommended.
|The pool name.
tank will be used in throughout this guide.
|The path to the block device ZFS will use.
After completing this, confirm that the pool has been created:
# zpool status
Should return something like:
pool: tank state: ONLINE scan: none requested config: NAME STATE READ WRITE CKSUM tank ONLINE 0 0 0 crypt ONLINE 0 0 0 errors: No known data errors
Creating the required datasets
# zfs create -o mountpoint=none -o canmount=off tank/ROOT # zfs create -o mountpoint=/ tank/ROOT/alpine
Creating optional datasets (feel free to add your own)
# zfs create -o mountpoint=/home tank/HOME # zfs create -o mountpoint=/var/log tank/LOG
# mkfs.ext4 /dev/sda1
/mnt/boot/directory and mount the
/dev/sda1partition in this directory:
# mkdir /mnt/boot/ # mount -t ext4 /dev/sda1 /mnt/boot/
Installing Alpine Linux
In this step you will install Alpine Linux in the
/mnt/ directory, which contains the mounted file system structure.
- Install Alpine Linux:
# setup-disk -m sys /mnt/
- The installer downloads the latest packages to install the base installation. Additionally, the installer automatically creates the entries for the mount points in the
fstabfile (but we'll have to edit it manually later), which are currently mounted in the
- To enable the operating system to decrypt the LUKS partition at boot time, create the
/mnt/etc/crypttabfile. Enter the following line into the file to decrypt the
/dev/sda2partition using the
luksmodule and map it to the
crypt /dev/sda2 none luks
- Delete the zfs entries in
/mnt/etc/fstabas ZFS mounts them automagically. Your fstab should look similar to this:
UUID=6b4f2c9c-0a0f-4a8c-a73b-d2b47920ad6f /boot ext4 rw,relatime,stripe=4,data=ordered 0 2
- Edit the
/mnt/etc/mkinitfs/mkinitfs.conffile and append the
zfsmodule to the
features="ata base ide scsi usb virtio ext4 lvm cryptsetup zfs"
- Rebuild the initial RAM disk:
# mkinitfs -c /mnt/etc/mkinitfs/mkinitfs.conf -b /mnt/ $(ls /mnt/lib/modules/)
- The command uses the settings from the
mkinitfs.conffile set in the
-cparameter to generate the RAM disk. The command is executed in the
/mnt/directory and the RAM disk is generated using the modules for the installed kernel. Without setting the kernel version using the
$(ls /mnt/lib/modules/) option,
mkinitfstries to generate the RAM disk using the kernel version installed in the temporary environment, which can differ from the latest one installed by the
- Edit the
/mnt/etc/update-extlinux.conffile, set the root ZFS dataset and append the following kernel options to the
root=tank/ROOT/alpine default_kernel_opts="... cryptroot=/dev/sda2 cryptdm=crypt rootfstype=zfs"
cryptrootparameter sets the name of the device that contains the root file system. The
cryptdmparameter sets the name of the mapping previously set in the
rootfstypeoption sets the root filesystem type to zfs.
- Because the
update-extlinuxutility operates only on the
/boot/directory, temporarily change the root to the
/mnt/directory and update the boot loader configuration:
# chroot /mnt/ # update-extlinux # exit
- Ignore the errors the
- Write the MBR to the
# dd bs=440 count=1 conv=notrunc if=/mnt/usr/share/syslinux/mbr.bin of=/dev/sda
Unmounting the filesystems
# umount /mnt/boot/
- Unmount all zfs filesystems:
# zfs unmount -a
- Export all zfs pools:
# zpool export -a
- Close the
# cryptsetup luksClose crypt
- Reboot the system:
In case your system fails to boot, you can verify the settings and fix incorrect configurations:
- Load the ZFS kernel module:
# modprobe zfs
# zpool import -R /mnt tank # mount -t ext4 /dev/sda1 /mnt/boot
- Verify that you run the steps described in the Installing Alpine Linux section correctly. Update the configuration if necessary.