LUNAROPS · OPERATIONAL UPLINK 100% UPTIME 1,247d POSTS 893 JEFF.MOON@LUNAROPS.DEV UTC --:--:--

LUKS and Full-Disk Encryption: Root Partitions, TPM Unlock, and Key Management

luksencryptionsecuritylinuxtpmcryptsetup

Linux Unified Key Setup (LUKS) is the standard full-disk encryption on Linux. It ships in every mainstream distro installer, backs the volume encryption for ext4/XFS/Btrfs on top of dm-crypt, and quietly protects laptops, servers, and removable drives around the world. Like most things that ship by default, the installer gets you 80% of the way — the remaining 20% (key rotation, TPM-backed unlock, headers, keyslots, recovery plans) is where production use lives, and where most people run into trouble years later when a drive fails and they realize they never actually thought about recovery.

This post covers LUKS from the working-engineer perspective: the format, the tools, common deployment patterns (root, home, removable drives, TPM unlocking), and the mistakes that turn “I encrypted it” into “I can’t get my data back”.

What LUKS actually is

Underneath everything on Linux, block-level encryption is provided by the kernel’s dm-crypt device mapper target. dm-crypt takes a raw block device and a key, and exposes a new mapped device (/dev/mapper/encrypted_root) that transparently encrypts and decrypts reads and writes using that key.

dm-crypt by itself is stateless — you provide a key, it does the math. It has no idea what password you used, whether you want multiple passwords, how to survive a key change, or how to distinguish “bad password” from “this isn’t an encrypted device”. LUKS is the metadata layer that adds all of those:

  • A header at the start of the device describing cipher, hash, key size, and slot layout.
  • Up to 8 keyslots (LUKS1) or 32 keyslots (LUKS2), each holding a copy of the master key encrypted with a different passphrase-derived key.
  • Anti-forensic splitting of keyslot data so even shredding a keyslot is robust.
  • Per-keyslot KDF parameters so you can cheaply decrypt on a server while making brute-force expensive.

The practical consequence: the master key is stored once, encrypted N times with N different passphrases (keyslot entries). Changing a passphrase re-encrypts a keyslot with a new passphrase-derived key but doesn’t touch the master key, so data does not need to be re-encrypted. Removing all keyslots — or destroying the header — makes the data cryptographically unrecoverable.

LUKS2 is the current format and the default on modern distros. LUKS1 still exists for legacy reasons and for bootloaders that don’t support LUKS2 (older GRUB). If you have a choice, use LUKS2.

The tools

  • cryptsetup — the canonical userspace tool. Every LUKS operation goes through this.
  • dmsetup — lower level, for inspecting the live device mapper state.
  • systemd-cryptsetup — systemd’s unlocker, consumed by /etc/crypttab.
  • systemd-cryptenroll — LUKS2-aware tool for enrolling TPMs, FIDO2 tokens, and recovery keys.
  • clevis — alternative policy-based enrollment (TPM, Tang network unlock).

If you only remember one command, remember cryptsetup luksDump /dev/sdX — it shows the entire header, every keyslot, the cipher, and the KDF parameters, and it’s the starting point for any debugging.

Encrypting a fresh disk

The canonical flow for setting up a new encrypted volume:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Interactive: prompts for a passphrase
cryptsetup luksFormat /dev/sdb

# Verify
cryptsetup luksDump /dev/sdb

# Open it — exposes /dev/mapper/mydata
cryptsetup open /dev/sdb mydata

# Now put a filesystem on the mapped device
mkfs.xfs /dev/mapper/mydata
mount /dev/mapper/mydata /mnt/mydata

To unmount and close:

1
2
umount /mnt/mydata
cryptsetup close mydata

The luksFormat defaults on modern cryptsetup are good: LUKS2 format, aes-xts-plain64 cipher, 512-bit key, argon2id KDF with parameters tuned to take ~2 seconds on the encrypting machine. Don’t override unless you know why.

Explicitly setting parameters

If you need to pin parameters (often for compliance or reproducible builds):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cryptsetup luksFormat \
    --type luks2 \
    --cipher aes-xts-plain64 \
    --key-size 512 \
    --hash sha512 \
    --pbkdf argon2id \
    --pbkdf-memory 1048576 \
    --pbkdf-parallel 4 \
    --iter-time 2000 \
    /dev/sdb

The KDF choice matters. argon2id resists GPU and ASIC attacks; pbkdf2 does not. LUKS2 defaults to argon2id. LUKS1 is stuck with pbkdf2. This alone is a reason to prefer LUKS2.

The --iter-time 2000 tells cryptsetup to choose KDF parameters that take roughly 2000 ms on this machine. This calibrates to your machine’s speed, so a fast server gets strong KDF parameters and a Raspberry Pi gets survivable ones. If you encrypt on a fast machine and expect to decrypt on a slow one, lower this number or unlock will feel eternal.

Keyslot management

Adding a second keyslot

Every LUKS volume starts with one keyslot. Add more with:

1
2
cryptsetup luksAddKey /dev/sdb
# Prompts for existing passphrase, then twice for the new one

Useful reasons to add keyslots:

  • A recovery key held in a safe, separate from the daily-use passphrase.
  • An automation key used for scripted unlock on provisioning.
  • A shared key used by multiple administrators (though per-admin keys are better for auditability).

Adding a key file

Instead of a passphrase, LUKS can accept a file’s contents as the unlock material:

1
2
3
4
5
6
# Generate 4096 random bytes as the key file
dd if=/dev/urandom of=/etc/keys/mydata.key bs=4096 count=1
chmod 0400 /etc/keys/mydata.key

# Enroll it
cryptsetup luksAddKey /dev/sdb /etc/keys/mydata.key

This is the foundation for unattended unlocks (secondary disks unlocked by a file stored on the encrypted root disk), but note: a key file stored on another encrypted volume is only as secure as that other volume’s unlock. If the root is unlocked via TPM with no user interaction, so is every secondary volume keyed to a file on root.

Removing a keyslot

1
2
3
4
5
cryptsetup luksRemoveKey /dev/sdb
# Prompts for the passphrase of the slot to remove
# OR
cryptsetup luksKillSlot /dev/sdb 3
# Removes slot 3 (requires another valid passphrase)

Always remove old keyslots when rotating. A LUKS volume with an ex-employee’s passphrase still enrolled is a half-rotated volume.

Inspecting keyslots

1
cryptsetup luksDump /dev/sdb

Output shows every keyslot, its KDF, memory/iteration/parallel parameters, and any token associations (for TPM/FIDO2 enrollments). Learn to read this — you cannot operate LUKS without it.

The LUKS header: the most important thing to back up

The header contains the encrypted master key. If the header is lost or corrupted, your data is unrecoverable, even if you remember every passphrase.

This is the single most important operational fact about LUKS.

Back up the header immediately after luksFormat:

1
cryptsetup luksHeaderBackup /dev/sdb --header-backup-file /secure/backup/sdb-luks-header.bin

Store this backup somewhere offline, because it is sufficient (with a passphrase) to decrypt the volume — so don’t leave it on a shared network drive. A safe, a password manager attachment, or burned to a physical medium are appropriate.

Restore with:

1
cryptsetup luksHeaderRestore /dev/sdb --header-backup-file /secure/backup/sdb-luks-header.bin

Detached headers

For high-assurance setups, store the header on a separate device entirely:

1
2
cryptsetup luksFormat --header /path/to/header.img /dev/sdb
cryptsetup open --header /path/to/header.img /dev/sdb mydata

The header device can be a USB key that stays in a safe except when mounting. Without the header, the disk looks completely random — no indication it’s even encrypted, let alone with LUKS. Plausible deniability, in a sense. Operationally fragile; use only when the threat model justifies it.

Encrypting the root filesystem

At install time, every major distro offers “encrypt disk” in the installer. For manual setups:

  1. Format the disk with LUKS2.
  2. Open the LUKS volume.
  3. Create a filesystem on the mapped device.
  4. Install the OS.
  5. Configure /etc/crypttab with an entry for the root volume.
  6. Rebuild the initramfs to include LUKS tooling and the correct device mapping.
  7. Configure the bootloader (GRUB needs GRUB_ENABLE_CRYPTODISK=y).

The initramfs part is where people get stuck. The kernel at boot time has no filesystem; it needs a minimal userspace (initramfs) that contains cryptsetup, the right kernel modules, and instructions to unlock the LUKS device before mounting root.

Every distro handles this slightly differently:

  • Debian/Ubuntu: update-initramfs -u after editing /etc/crypttab.
  • Fedora/RHEL: dracut -f — dracut reads /etc/crypttab automatically.
  • Arch: edit /etc/mkinitcpio.conf HOOKS to include encrypt (or sd-encrypt for systemd-based init), then mkinitcpio -P.

The /etc/crypttab entry looks like:

root   UUID=<uuid-of-/dev/sda2>   none   luks,discard

none here means “prompt for passphrase at boot”. discard enables TRIM passthrough — see the section on discard below.

For the root volume only, the kernel command line also matters. With GRUB and systemd:

GRUB_CMDLINE_LINUX="rd.luks.uuid=luks-<uuid> root=/dev/mapper/root"

After grub-mkconfig -o /boot/grub/grub.cfg, the system will prompt for the LUKS passphrase at boot.

TPM-backed unlock

Typing a passphrase at every boot gets old. TPM (Trusted Platform Module) unlock binds the LUKS unlock to the hardware state of the machine — the TPM releases a key only if the boot chain (BIOS, bootloader, kernel, initramfs) hashes match the expected values. Modify any of those, and the TPM refuses to release the key.

For laptops, this gives you: power on → boot straight to login, without typing a passphrase, but with no way for an attacker to unlock the disk by booting their own OS.

The risk: if the system’s boot chain changes (firmware update, kernel update), the PCRs (Platform Configuration Registers) change, and the TPM won’t release the key. Always keep a passphrase keyslot as recovery.

systemd-cryptenroll (modern approach)

1
2
# Enroll the TPM, binding to PCRs 7 (Secure Boot state) and 14 (extensible)
systemd-cryptenroll /dev/sda2 --tpm2-device=auto --tpm2-pcrs=7+14

Then update /etc/crypttab:

root   UUID=<uuid>   none   luks,discard,tpm2-device=auto

PCR selection matters:

  • PCR 0: BIOS/firmware. Changes on firmware update.
  • PCR 1: BIOS configuration.
  • PCR 4: Boot loader code. Changes on bootloader update.
  • PCR 7: Secure Boot state. Changes if Secure Boot is disabled or keys change.
  • PCR 8–9: Bootloader config.
  • PCR 11: Unified kernel images (systemd-boot with UKI).
  • PCR 14: Extensible — shim-enrolled MOK state.

Binding to PCR 7 alone is the most stable choice: it only changes if someone tampers with Secure Boot. Binding to PCR 4 is stricter but breaks on every kernel update. Choose deliberately.

Re-sealing after updates

When a kernel or bootloader update changes PCRs, re-seal:

1
systemd-cryptenroll /dev/sda2 --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=7+14

Some distros handle this with hooks. On Arch, systemd provides systemd-pcrlock for predictable PCR policies that survive updates without re-sealing. It’s new and complex; evaluate for your use case.

Clevis (the older approach)

Clevis handles TPM2 unlock as one of several “pins”:

1
clevis luks bind -d /dev/sda2 tpm2 '{"pcr_ids":"7"}'

Clevis also provides Tang — network-bound disk encryption. A Tang server on your network offers unlock material; the encrypted disk unlocks only when it can reach the Tang server. Useful for servers in a locked room that should refuse to boot if stolen.

Recovery keys

Whatever automated unlock you set up (TPM, Tang, FIDO2), always keep a passphrase. Hardware fails. Firmware updates brick enrolled PCRs. TPMs get cleared.

1
2
# Generate and enroll a long random recovery key
systemd-cryptenroll /dev/sda2 --recovery-key

This prints a 48-character string. Write it down. Put it in a safe. Don’t skip this.

Key rotation

Changing a passphrase

1
2
cryptsetup luksChangeKey /dev/sdb
# Prompts for current, then new

This does not re-encrypt data. It only re-encrypts the keyslot’s copy of the master key.

Rotating the master key

Sometimes you genuinely want to rotate the master key — compliance, suspicion of compromise, etc. LUKS2 supports this via reencrypt:

1
cryptsetup reencrypt /dev/sdb

This is slow: every block on the disk is read, decrypted with the old master key, re-encrypted with the new one, and written back. A 1 TB disk takes hours. It is crash-safe — if interrupted, resume with cryptsetup reencrypt /dev/sdb again.

Changing cipher

1
cryptsetup reencrypt --cipher aes-xts-plain64 --key-size 512 /dev/sdb

Same performance caveat. Useful if an old volume was created with weaker defaults.

The discard (TRIM) question

discard on an encrypted SSD has a tradeoff:

Pro: The SSD’s flash wear leveling works properly, TRIM discards unused blocks, write amplification stays low, performance over time is preserved.

Con: An attacker observing the disk can see which blocks are in use vs discarded. They don’t see what’s in used blocks, but the allocation pattern itself leaks information (filesystem type, approximate free space, that the disk is encrypted at all).

For most users, turn discard on. The threat model that defeats discard (an attacker who can read the physical disk periodically over time) is unusual, and the performance cost of no-TRIM on SSDs is real.

Enable per-volume in /etc/crypttab:

root   UUID=<uuid>   none   luks,discard

And at the filesystem layer: prefer fstrim.timer over the discard mount option — weekly batched TRIM beats per-unlink synchronous TRIM for most workloads.

Removable drives

For USB drives you want portable:

1
2
3
4
cryptsetup luksFormat /dev/sdc1
cryptsetup open /dev/sdc1 portable
mkfs.ext4 /dev/mapper/portable
cryptsetup close portable

GNOME and KDE desktops will detect the LUKS drive on insertion and prompt for a passphrase. Store the passphrase in a password manager; no TPM here because the drive needs to unlock on arbitrary hosts.

For drives meant for archival (data you’ll decrypt again years later), back up the LUKS header separately and make sure you’re using LUKS2 with a current cipher. Cipher choices that looked fine in 2013 may not be recommended in 2030. If the default aes-xts-plain64 shifts, reencrypt is your migration path.

Things that will bite you

Lost passphrase, no backup

No recovery. LUKS is designed exactly so that nobody — including you — can bypass the KDF. Offline brute-force is the only option, and argon2id makes that infeasible. Restore from your off-disk backup. You do have one, right?

Header corruption from dd accidents

Every few months someone tries to dd if=/dev/zero of=/dev/sda bs=1M count=1 and nukes the first megabyte of their encrypted disk. The LUKS header lives there. luksHeaderBackup before doing anything risky. And always put backups offline.

Boot order surprises

If your system has both a TPM-unlock and a passphrase keyslot and the TPM is working, systemd-cryptsetup uses the TPM silently. If the TPM fails, it falls back to prompting. If the TPM fails to respond but thinks it’s still valid, you’ll wait 30 seconds at boot for the TPM timeout. Verify by rebooting after enrollment — don’t ship a configuration you haven’t tested.

Encrypted swap

Swap can be encrypted with a random per-boot key (/dev/urandom as the “keyfile”):

swap   /dev/sda3   /dev/urandom   swap,cipher=aes-xts-plain64:sha256,size=512

Every boot generates a new key — suspended memory cannot survive a reboot in recoverable form. Don’t use this with hibernate, which writes RAM to swap and needs it back after power-off. For hibernate-compatible swap, use a real LUKS volume with a persistent key.

UEFI Secure Boot + TPM

Secure Boot state lives in PCR 7. If you ever disable Secure Boot (even temporarily to troubleshoot), the TPM will no longer release the key. Re-enroll with the new PCR state or stick a recovery passphrase in from the systemd prompt.

discard leaks on snapshotting backups

If you dd a raw encrypted disk to a backup image, discarded blocks may still be zeroed and compressible. An attacker who gets the image can identify non-allocated blocks, even without the passphrase. For image-level backups of encrypted disks, use LUKS-aware tools or accept the leak.

A sensible pattern for a daily-use laptop

  1. LUKS2 root partition, default cipher (aes-xts-plain64), argon2id KDF.
  2. TPM2 unlock bound to PCR 7 for user-invisible boot.
  3. Recovery passphrase generated with systemd-cryptenroll --recovery-key, stored in a password manager.
  4. LUKS header backup stored in a password manager attachment.
  5. discard enabled for SSD health.
  6. fstrim.timer enabled weekly.
  7. Kernel update testing: verify boot after every dnf update or apt upgrade that touches the kernel or bootloader. Re-seal TPM if needed.

A sensible pattern for a server

  1. LUKS2 for the data volume, root unencrypted (or Tang-unlocked via clevis).
  2. Unlock automation via Tang network-bound policy — requires the Tang server to be reachable at boot.
  3. Two enrolled keyslots: one for the Tang pin, one recovery passphrase known to on-call.
  4. LUKS header backed up to an off-server encrypted vault.
  5. Key rotation scheduled annually; luksChangeKey for passphrases, full reencrypt only on compromise suspicion.
  6. Document the recovery flow in your runbooks. Test it on a scratch machine before you need it at 3 a.m.

When LUKS is the wrong choice

  • File-level encryption needs. Use fscrypt (native on ext4/F2FS) or gocryptfs/CryFS on top of a normal filesystem.
  • Multi-user systems where users should have independent encryption. fscrypt with per-user keys is a better match.
  • Cloud volumes where the cloud provides encryption at rest. Layering LUKS on top of AWS EBS or Azure Managed Disk encryption is occasionally useful (defense in depth, customer-held keys) but more often just adds operational complexity with no real threat model improvement.
  • Databases doing their own encryption. PostgreSQL TDE, MySQL keyring-based encryption — if the DB does it, LUKS is redundant for that tablespace.

LUKS is not magic. It’s a well-designed key-management wrapper around the kernel’s already-good block encryption. The failure modes are procedural, not cryptographic: lost headers, unrotated keyslots, TPM bindings that break on kernel updates, backups that were never tested. Treat the header like a precious artifact, document your recovery paths, test them, and LUKS will quietly do its job for a decade.

Comments