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:

  1. Control — you understand exactly what boots, and from where.
  2. Simplicity — fewer layers and abstractions.
  3. 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,MODEL

Option 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.conf

Bring interface up and request DHCP

ip link set wlan0 up
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf
dhcpcd wlan0

Verify connectivity

ping -c 3 artixlinux.org

Step 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_wiped

Now 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_wiped

Step 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/sda1

Option B — removable /boot

USB device:

  • sdb1: EFI System (256–512 MB)
mkfs.fat -F32 /dev/sdb1

Internal 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=fullblock

4.2 Open plain dm-crypt mapping

Option A

cryptsetup -c aes-xts-plain64 -d /root/root.key -s 512 open --type plain /dev/sda2 root

Option B

cryptsetup -c aes-xts-plain64 -d /root/root.key -s 512 open --type plain /dev/sda1 root

4.3 Format and mount

mkfs.ext4 /dev/mapper/root
mount /dev/mapper/root /mnt
mkdir -p /mnt/boot

Mount /boot:

# Option A
mount /dev/sda1 /mnt/boot

# Option B
mount /dev/sdb1 /mnt/boot

Step 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.enc

Step 6 — Install Artix base system (runit)

basestrap /mnt \
  base base-devel \
  linux linux-firmware \
  runit elogind-runit \
  cryptsetup \
  grub efibootmgr \
  git vim

Copy 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 /mnt

Step 7 — Users and credentials

Set root password

passwd

Create a normal user and add to the wheel group

useradd -m -G wheel -s /bin/bash shaun
passwd shaun

Enable sudo

pacman -S sudo
visudo

Uncomment:

%wheel ALL=(ALL:ALL) ALL

Step 8 — Install xmew initramfs hook

cd /opt
git clone https://github.com/shaunpitchers/xmew.git
cp /opt/lib/initcpio/install/xmew /lib/initcpio/install/
cp /opt/lib/initcpio/hooks/xmew    /lib/initcpio/hooks/
vim /etc/mkinitcpio.conf

Ensure 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 -P

Step 9 — Install and configure GRUB (EFI)

grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB
vim /etc/default/grub

Add 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.cfg

Step 10 — Reboot

exit
umount /mnt/boot
umount /mnt
cryptsetup close root
reboot

What comes next

Part 3 will cover the system and network hardening, and backups for a secure system.