Skip to content

LUKS_encryption

AmazinAxel edited this page Jul 12, 2026 · 1 revision

This documentation was generated by Claude


0. Identify the disk

lsblk -o NAME,SIZE,TYPE,MODEL

Assume the internal NVMe is /dev/nvme0n1 below. If yours differs, substitute it everywhere. (SATA would be /dev/sda; adjust p1/p21/2.)

Optional — check the drive's native sector size (informational):

cat /sys/block/nvme0n1/queue/physical_block_size   # often 512 on consumer NVMe
nvme id-ns -H /dev/nvme0n1 | grep -A20 "LBA Format" # shows if a 4Kn format exists

Even if the drive is 512-native, we still use 4096 LUKS sectors — it's independent and cuts crypto ops ~8x per 4K I/O.


1. Partition (GPT: 1 GB ESP + rest for LUKS)

sgdisk --zap-all /dev/nvme0n1
sgdisk -n1:0:+1G  -t1:ef00 -c1:ESP          /dev/nvme0n1   # EFI System Partition
sgdisk -n2:0:0    -t2:8309 -c2:cryptpersist  /dev/nvme0n1   # Linux LUKS, rest of disk
partprobe /dev/nvme0n1
lsblk /dev/nvme0n1

Format the ESP (stays unencrypted — required for boot):

mkfs.fat -F32 -n BOOT /dev/nvme0n1p1

2. Create the LUKS2 container — fast + secure choices

cryptsetup luksFormat \
  --type luks2 \
  --cipher aes-xts-plain64 \
  --key-size 512 \
  --pbkdf argon2id \
  --sector-size 4096 \
  /dev/nvme0n1p2

Why each flag:

  • aes-xts-plain64 — the standard disk cipher; hardware-accelerated by AES-NI on Ryzen, so it's both the fastest and the most trusted option. (Adiantum is only better on CPUs without AES-NI — not this one.)
  • --key-size 512 — XTS splits the key in two, so this is AES-256.
  • --pbkdf argon2id — memory-hard passphrase hashing; resists GPU/ASIC brute-force far better than PBKDF2. (This is the LUKS2 default, set explicitly for clarity.)
  • --sector-size 4096the performance lever: one AES-XTS op per 4K instead of eight per-512B ops. Set now so you never have to reencrypt.

Enter a strong passphrase when prompted (this is the root of your security — a weak passphrase makes all the above moot).

Open it and make the filesystem:

cryptsetup open /dev/nvme0n1p2 cryptpersist
mkfs.ext4 -L persist /dev/mapper/cryptpersist

Verify the sector size took:

cryptsetup luksDump /dev/nvme0n1p2 | grep -i sector    # -> 4096 [bytes]

3. Mount + generate config

mount /dev/mapper/cryptpersist /mnt
mkdir -p /mnt/boot
mount /dev/nvme0n1p1 /mnt/boot

nixos-generate-config --root /mnt

Grab the LUKS partition's UUID (you'll reference it in config):

blkid /dev/nvme0n1p2      # note UUID="...."  (the crypto_LUKS one)

4. Wire the performance + unlock options into hardware-configuration.nix

Edit /mnt/etc/nixos/hardware-configuration.nix and make the LUKS device block look like this (use the UUID from step 3):

boot.initrd.luks.devices."cryptpersist" = {
  device = "/dev/disk/by-uuid/PASTE-LUKS-UUID-HERE";
  allowDiscards = true;    # TRIM passthrough → sustained SSD write speed + wear-leveling
  bypassWorkqueues = true; # skip dm-crypt read/write workqueues → big NVMe latency win
};

# keyboard must work at the LUKS passphrase prompt in initrd
boot.initrd.availableKernelModules = [
  "nvme" "xhci_pci" "usbhid" "hid_generic" "i8042" "atkbd"
];

Mount /persist (or /, if not using impermanence) with noatime:

fileSystems."/" = {          # or "/persist" if you mirror your impermanence setup
  device = "/dev/mapper/cryptpersist";
  fsType = "ext4";
  options = [ "noatime" ];   # skip atime writes on every read
};

Impermanence (optional, to match your other hosts): if you want the tmpfs-root + /nix bind + /persist layout, layer your usual modules/impermanence.nix on top — the encryption steps above are identical; only the mount topology changes. Keep neededForBoot = true on the persisted filesystem.


5. Perf + battery tuning (Ryzen) — add to your host config

# CPU: modern EPP driver. REQUIRES enabling "CPPC" in the BIOS/UEFI first,
# otherwise it silently falls back to acpi-cpufreq.
boot.kernelParams = [ "amd_pstate=active" ];

# Weekly TRIM (works together with allowDiscards above)
services.fstrim.enable = true;

# TLP: under amd-pstate-epp the ONLY valid governors are performance/powersave.
# Do NOT use "schedutil" here — it doesn't exist under this driver.
services.tlp = {
  enable = true;
  settings = {
    CPU_SCALING_GOVERNOR_ON_AC  = "performance";
    CPU_SCALING_GOVERNOR_ON_BAT = "powersave";      # dynamic under amd-pstate-epp, NOT a min-freq lock
    CPU_ENERGY_PERF_POLICY_ON_AC  = "performance";
    CPU_ENERGY_PERF_POLICY_ON_BAT = "balance_power"; # good snappiness/battery balance
    CPU_BOOST_ON_AC  = 1;
    CPU_BOOST_ON_BAT = 0;
  };
};

The NVMe I/O scheduler defaults to none on NixOS (correct — don't change it).


6. Install

nixos-install --root /mnt        # set root password when prompted
reboot

7. Post-boot verification

# encryption sector size
cat /sys/block/dm-0/queue/logical_block_size          # -> 4096
# TRIM passthrough active
cat /sys/block/dm-0/queue/discard_granularity         # -> 4096 (not 0)
# workqueue bypass present
sudo dmsetup table cryptpersist | tr ' ' '\n' | grep workqueue   # no_read/no_write_workqueue
# CPU driver (after enabling BIOS CPPC + reboot)
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver          # -> amd-pstate-epp
cat /sys/devices/system/cpu/amd_pstate/status                    # -> active
# I/O scheduler
cat /sys/block/nvme0n1/queue/scheduler                # -> [none]

Security/perf summary

Choice Reason
LUKS2 + AES-XTS-256 Standard, AES-NI-accelerated → fast and strong
argon2id KDF Memory-hard; resists brute-force
4096-byte sectors ~8x fewer crypto ops per 4K I/O
bypassWorkqueues Removes dm-crypt's spinning-disk latency path
allowDiscards + fstrim SSD stays fast; mild info leak (used-block map) — standard laptop tradeoff
noatime, scheduler=none No wasted writes; NVMe-appropriate queueing
amd_pstate=active (needs BIOS CPPC) Real dynamic scaling + working EPP battery tuning

Not covered on purpose: OPAL/self-encrypting-drive hardware encryption. It offloads crypto to the drive controller, but you trust opaque firmware (history of SEDs that didn't truly encrypt), and with AES-NI + 4096 sectors the CPU savings are marginal. Tuned software LUKS is the better balance here.

This flake is a submission to Hack Club's Riceathon

Clone this wiki locally