Building a Free and Minimal Unix System (Part 2)- Artix runit on plain dm-crypt
In Part 1 I argued that a free system starts below the operating system: firmware, boot, and storage. This post moves from foundations to implementation.
The goal here is a minimal Artix Linux system using runit, installed
onto a disk encrypted with plain dm-crypt. The default
approach uses a single internal disk. An optional variant places
/boot on removable media.
Disk unlocking is handled in the initramfs using a small encrypted
key file (root.enc) and a custom hook
(xmew).
These are the steps I go through when installing on a new machine. When I was implementing this method, I got a few commands wrong and locked myself out of the system or it wouldn’t boot and I had to start again. For some people this would be enough for them to give up, but I persisted as part of my unix learning and now I know MOST of these steps off by heart.
This is not a beginner guide. Several steps below are destructive and irreversible. If you do not understand a command, stop and learn before proceeding.
I have assumed the user is using UEFI boot loader in some commands (mainly the grub commands). If you are on libreboot or legacy boot then the command is slightly different.
I plan to make this into a simple script to automate this process and eliminate the chance of error.
Read this twice
You are about to erase disks, generate key material, and configure a system that offers very little forgiveness if you make a mistake.
Read each section fully before running commands. Confirm device names on your machine. There is no safety net here by design.
Trade-offs and threat model
This setup prioritises:
- Control — you understand exactly what boots, and from where.
- Simplicity — fewer layers and abstractions.
- Disk secrecy at rest — encrypted storage without identifying metadata.
Plain dm-crypt (important)
Plain dm-crypt is a trade-off, not a universal recommendation.
Pros
- no header
- encrypted regions resemble random data at rest
Cons
- no metadata
- no recovery UX
- mistakes are catastrophic
If you want convenience, use LUKS. If you accept responsibility in exchange for simplicity, read on.
Assumptions and device layout
Before doing anything destructive, identify the disks:
lsblk -o NAME,SIZE,TYPE,MOUNTPOINTS,MODELOption A — single internal disk
/dev/sda1→ EFI System Partition (FAT32), mounted at/boot/dev/sda2→ encrypted root (plain dm-crypt)
Option B — removable
/boot
/dev/sdb1→ EFI System Partition (USB), mounted at/boot/dev/sda1→ encrypted root (plain dm-crypt)
Do not copy/paste device names. Confirm them on your machine.
Step 1 — Networking (wpa_supplicant)
Artix install media typically does not include iwctl.
Use wpa_supplicant.
Create configuration
wpa_passphrase "SSID" "PASSWORD" > /etc/wpa_supplicant/wpa_supplicant.confBring interface up and request DHCP
ip link set wlan0 up
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf
dhcpcd wlan0Verify connectivity
ping -c 3 artixlinux.orgStep 2 — Scramble the internal disk
Before partitioning, scramble the disk so unused space does not reveal structure.
One approach is to map the disk with a random key and write zeros through it. The ciphertext becomes random.
This can take a long time. That is the cost of doing it properly.
Create an encrypted partition on the whole disk using dm-crpyt (plain encryption):
cryptsetup open --type plain --key-file /dev/urandom --sector-size 512 /dev/sda to_be_wipedNow use dd utility to write all zero’s to the entire disk, then close the encrypted partition (erasing it for good.):
dd if=/dev/zero of=/dev/mapper/to_be_wiped bs=1M status=progress
cryptsetup close to_be_wipedStep 3 — Partitioning
Now you have a completely erased and clean disk. Use
fdisk, cfdisk, or gdisk. The
exact tool does not matter; the partitioning does.I prefer
cfdisk.
Option A — single disk
Create a GPT with:
sda1: EFI System (256–512 MB)sda2: Linux filesystem (encrypted root container)
Format EFI:
mkfs.fat -F32 /dev/sda1Option B — removable
/boot
USB device:
sdb1: EFI System (256–512 MB)
mkfs.fat -F32 /dev/sdb1Internal disk:
sda1: encrypted root container
Step 4 — Create key material and open encrypted root
4.1 Create root.key
dd bs=512 count=1 if=/dev/random of=/root/root.key iflag=fullblock4.2 Open plain dm-crypt mapping
Option A
cryptsetup -c aes-xts-plain64 -d /root/root.key -s 512 open --type plain /dev/sda2 rootOption B
cryptsetup -c aes-xts-plain64 -d /root/root.key -s 512 open --type plain /dev/sda1 root4.3 Format and mount
mkfs.ext4 /dev/mapper/root
mount /dev/mapper/root /mnt
mkdir -p /mnt/bootMount /boot:
# Option A
mount /dev/sda1 /mnt/boot
# Option B
mount /dev/sdb1 /mnt/bootStep 5 — Encrypt the key
file (root.enc)
Encrypt root.key with OpenSSL (PBKDF2):
This will prompt you for the password to the encrypted key file. Make sure it is a strong password or passphrase and that you remember it.
openssl aes-256-cbc -pbkdf2 -e -in /root/root.key -out /root/root.encStep 6 — Install Artix base system (runit)
basestrap /mnt \
base base-devel \
linux linux-firmware \
runit elogind-runit \
cryptsetup \
grub efibootmgr \
git vimCopy only the encrypted key into the installed system:
cp /root/root.enc /mnt/root/Do not copy /root/root.key into the installed
system.
Generate fstab and then chroot into the system
fstabgen -U /mnt >> /mnt/etc/fstab
artix-chroot /mntStep 7 — Users and credentials
Set root password
passwdCreate a normal user and add to the wheel group
useradd -m -G wheel -s /bin/bash shaun
passwd shaunEnable sudo
pacman -S sudo
visudoUncomment:
%wheel ALL=(ALL:ALL) ALL
Step 8 — Install xmew initramfs hook
cd /opt
git clone https://github.com/shaunpitchers/xmew.gitcp /opt/lib/initcpio/install/xmew /lib/initcpio/install/
cp /opt/lib/initcpio/hooks/xmew /lib/initcpio/hooks/vim /etc/mkinitcpio.confEnsure that the xmew and encrypt hooks are placed in exactly this position, and the encrypted root.enc file is in FILES:
FILES=(/root/root.enc)
HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block xmew encrypt filesystems fsck)mkinitcpio -PStep 9 — Install and configure GRUB (EFI)
grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUBvim /etc/default/grubAdd this to the end of the string on the line GRUB_CMDLINE_LINUX_DEFAULT:
"cryptdevice=/dev/disk/by-id/XXXXXXX_XXXX:root cryptkey=rootfs:/root/root.key crypto=:aes-xts-plain64:512:0:"Mine looks like this:
GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet cryptdevice=/dev/disk/by-id/nvme-CT1000P310SSD8_25054DDD88EE-part2:root cryptkey=rootfs:/root/root.key crypto=:aes-xts-plain64:512:0:"grub-mkconfig -o /boot/grub/grub.cfgStep 10 — Reboot
exit
umount /mnt/boot
umount /mnt
cryptsetup close root
rebootWhat comes next
Part 3 will cover the system and network hardening, and backups for a secure system.