Full disk encryption (FDE) is one of those things that every Linux admin knows they should be doing — and yet, a surprising number of production servers and workstations are still running with unencrypted storage. In 2026, with exploitation timelines measured in hours and physical theft still a very real threat, there's honestly no good reason to leave data at rest unprotected.
The good news? LUKS2 combined with TPM2 auto-unlock, FIDO2 hardware tokens, and UEFI Secure Boot gives you enterprise-grade encryption without the headache of typing passphrases on every single reboot.
This guide walks through the entire hardened disk encryption stack: choosing the right LUKS2 cipher and KDF parameters, enrolling TPM2 and FIDO2 unlock methods with systemd-cryptenroll, deploying Clevis/Tang network-bound disk encryption (NBDE) for headless servers, and tying everything to a verified Secure Boot chain. All commands have been tested on Fedora 41, Ubuntu 24.04 LTS, and RHEL 9.
Why LUKS2 Over LUKS1
LUKS1 served us well for over a decade. But LUKS2 brings improvements that make it the only sensible choice for new deployments in 2026:
- Argon2id key derivation — LUKS2 defaults to the memory-hard Argon2id KDF, which is dramatically more resistant to GPU and ASIC brute-force attacks than LUKS1's PBKDF2. Attackers simply can't parallelize Argon2id cheaply across GPU farms.
- 32 key slots — LUKS1 had only 8. With 32 slots, it's practical to enroll a passphrase, a recovery key, a TPM2 binding, and multiple FIDO2 tokens all at once.
- JSON metadata — Token metadata lives in a structured JSON area within the LUKS2 header, which lets
systemd-cryptenrolland Clevis embed unlock parameters directly. - Hardware token integration — Native support for TPM2, FIDO2, PKCS#11, and OPAL self-encrypting drives.
- Online re-encryption — You can re-encrypt an active, mounted volume without downtime. That's a game-changer for production systems.
If you've got existing LUKS1 volumes, convert them before moving forward:
sudo cryptsetup convert /dev/sdX --type luks2
Creating a Hardened LUKS2 Volume
The default cryptsetup luksFormat parameters are decent out of the box, but for maximum security you'll want to set the cipher, key size, and KDF parameters explicitly.
Choosing the Right Cipher and Key Size
AES-XTS with a 512-bit key (256-bit effective encryption strength) is the standard choice. Before formatting anything, run cryptsetup benchmark to confirm your hardware supports AES-NI acceleration:
# Check hardware acceleration and throughput
sudo cryptsetup benchmark
# Expected output on modern AES-NI hardware:
# aes-xts 256b ~4000 MiB/s encrypt ~4000 MiB/s decrypt
# aes-xts 512b ~3800 MiB/s encrypt ~3800 MiB/s decrypt
On systems with AES-NI (which is virtually every x86_64 CPU since 2010), AES-XTS delivers 3,000–4,000+ MiB/s. That's fast enough that encryption adds negligible overhead even on NVMe drives.
Tuning Argon2id Parameters
The Argon2id KDF parameters control how much CPU time and memory are needed to derive the encryption key from a passphrase. Higher values make brute-force attacks more expensive — but they also increase your unlock time, so there's a tradeoff to consider:
- --iter-time — Target unlock time in milliseconds (default: 2000ms). For servers that rarely reboot, bumping this to 5000ms is a reasonable hardening step.
- --pbkdf-memory — Memory cost in KiB. The default auto-benchmarks between 64 MiB and 1 GiB. Servers with plenty of RAM can safely go with 1048576 (1 GiB).
- --pbkdf-parallel — Number of parallel threads. Match this to your CPU core count, typically 4.
# Create a hardened LUKS2 volume
sudo cryptsetup luksFormat \
--type luks2 \
--cipher aes-xts-plain64 \
--key-size 512 \
--hash sha256 \
--pbkdf argon2id \
--pbkdf-memory 1048576 \
--pbkdf-parallel 4 \
--iter-time 5000 \
/dev/sdX
After formatting, open and mount the volume:
sudo cryptsetup open /dev/sdX cryptdata
sudo mkfs.ext4 /dev/mapper/cryptdata
sudo mount /dev/mapper/cryptdata /mnt/encrypted
TPM2 Auto-Unlock with systemd-cryptenroll
Trusted Platform Module 2.0 (TPM2) is a hardware security chip found on most modern motherboards. It stores encryption keys securely and only releases them when the system's boot chain matches a known-good state. So you get automatic disk unlock at boot — no passphrase needed — while the disk stays locked if someone tampers with the boot process or moves the drive to a different machine.
Pretty neat, right?
Prerequisites
# Verify TPM2 is present and accessible
sudo systemd-cryptenroll --tpm2-device=list
# Install required packages
# Fedora/RHEL:
sudo dnf install tpm2-tss tpm2-tools
# Ubuntu/Debian:
sudo apt install tpm2-tools libtss2-dev
# Arch:
sudo pacman -S tpm2-tss tpm2-tools
Understanding PCR Registers
Platform Configuration Registers (PCRs) are TPM hash registers that store measurements of each component in the boot chain. When you bind a LUKS key to specific PCRs, the TPM only releases the key if all measured components match what was recorded at enrollment time.
Here are the most important PCRs for disk encryption:
| PCR | What It Measures | Changes When |
|---|---|---|
| 0 | UEFI firmware | Firmware update |
| 2 | Option ROMs and plug-in firmware | Hardware change |
| 4 | Bootloader (GRUB, systemd-boot) | Bootloader update |
| 7 | Secure Boot state and certificate chain | Secure Boot policy change |
| 9 | Kernel command line | Boot parameters change |
| 11 | Unified Kernel Image (UKI) components | Kernel/initramfs update |
| 14 | Shim/MOK signing certificates | Distro key rotation |
Recommended PCR Selection
The PCR combination you choose is basically a tradeoff between security and how often you'll need to re-enroll after updates:
- PCR 7 only — Binds to Secure Boot state. Survives kernel and firmware updates. Requires Secure Boot to be enabled. This is the sweet spot for most users, and it's what I'd recommend as a starting point.
- PCR 7+14 — Adds shim certificate verification. Still survives kernel updates as long as your distro's signing key doesn't rotate. A solid choice for enterprise deployments.
- PCR 0+2+4+7 — Maximum verification of the entire boot chain. The catch? You'll need to re-enroll after any firmware, bootloader, or Secure Boot policy change.
Avoid PCR 11 — binding to the raw PCR 11 value breaks on every kernel or initramfs update because it measures the UKI components directly. Don't do it unless you're using signed PCR policies (covered later).
Enrolling TPM2
# Step 1: Enroll a recovery key first (CRITICAL — do this before TPM2)
sudo systemd-cryptenroll --recovery-key /dev/sdX
# Save the printed recovery key in a secure offline location
# Step 2: Enroll TPM2 with PCR 7 binding
sudo systemd-cryptenroll \
--tpm2-device=auto \
--tpm2-pcrs=7 \
/dev/sdX
# Step 3 (optional): Add a TPM2 PIN for defense-in-depth
sudo systemd-cryptenroll \
--tpm2-device=auto \
--tpm2-pcrs=7 \
--tpm2-with-pin=yes \
/dev/sdX
Configuring the Boot Process
For the root partition, you need to update your initramfs and /etc/crypttab so the unlock happens before the root filesystem mounts:
# /etc/crypttab — add tpm2-device=auto
cryptdata UUID=your-uuid-here - tpm2-device=auto
# For systemd-based initramfs (Fedora/RHEL):
sudo dracut -f
# For mkinitcpio (Arch/Manjaro):
# In /etc/mkinitcpio.conf, use systemd hooks:
# HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole sd-encrypt block filesystems fsck)
# MODULES=(tpm_tis)
sudo mkinitcpio -P
# For initramfs-tools (Ubuntu/Debian):
sudo update-initramfs -u -k all
Re-enrollment After Boot Chain Changes
If a firmware update, bootloader change, or Secure Boot modification causes the TPM to refuse the key, you'll get prompted for your passphrase or recovery key. After you boot successfully, just re-enroll:
# Wipe old TPM2 slot and enroll new one
sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/sdX
sudo systemd-cryptenroll \
--tpm2-device=auto \
--tpm2-pcrs=7 \
/dev/sdX
FIDO2 Hardware Token Unlock
FIDO2 security keys (think YubiKey 5, SoloKey, or Nitrokey) provide a physically separate authentication factor for disk unlock. Unlike TPM2 — which is soldered to the motherboard — a FIDO2 key travels with you and requires a physical tap to release the decryption key. It's an excellent option for laptops where you want two-factor protection.
Enrollment
# Install required library
sudo apt install libfido2-1 # Debian/Ubuntu
sudo dnf install libfido2 # Fedora/RHEL
# Enroll FIDO2 token with client PIN and user presence
sudo systemd-cryptenroll \
--fido2-device=auto \
--fido2-with-client-pin=yes \
--fido2-with-user-presence=yes \
/dev/sdX
# For tokens supporting EdDSA (more modern than default ECDSA):
sudo systemd-cryptenroll \
--fido2-device=auto \
--fido2-credential-algorithm=eddsa \
--fido2-with-client-pin=yes \
/dev/sdX
Enrolling a Backup FIDO2 Token
Always — and I can't stress this enough — enroll at least two FIDO2 tokens. If one gets lost or damaged, you can still unlock with the backup:
# Plug in BOTH tokens, then enroll the second one
# using the first token to authenticate
sudo systemd-cryptenroll \
--fido2-device=/dev/hidraw1 \
--fido2-credential-algorithm=eddsa \
--unlock-fido2-device=/dev/hidraw0 \
/dev/sdX
Configuring crypttab for FIDO2
# /etc/crypttab
cryptdata UUID=your-uuid-here - fido2-device=auto
Network-Bound Disk Encryption (NBDE) with Clevis and Tang
So what about headless servers sitting in a data center? Nobody's going to walk over and tap a FIDO2 key or type a passphrase every time they reboot. That's where Network-Bound Disk Encryption comes in.
The idea is elegant: the server unlocks automatically as long as it can reach a trusted Tang key server on the local network. If the server gets physically stolen and booted somewhere else, the Tang server is unreachable and the disk stays locked. Simple and effective.
Setting Up the Tang Server
# On a dedicated key server (RHEL/Fedora):
sudo dnf install tang
sudo systemctl enable --now tangd.socket
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --reload
# On Ubuntu/Debian:
sudo apt install tang
sudo systemctl enable --now tangd.socket
# Verify Tang is advertising keys:
curl -s http://tang-server.internal/adv | jq
Binding a Client to Tang
# Install Clevis on the client
# Fedora/RHEL:
sudo dnf install clevis clevis-luks clevis-dracut
# Ubuntu/Debian:
sudo apt install clevis clevis-luks clevis-initramfs clevis-tpm2
# Bind the LUKS volume to the Tang server
sudo clevis luks bind -d /dev/sdX tang '{"url":"http://tang-server.internal"}'
# Clevis will display the Tang server's thumbprint — verify it matches
# what you see on the Tang server itself:
tang-show-keys
# Rebuild initramfs to include Clevis
sudo dracut -f # Fedora/RHEL
sudo update-initramfs -u # Ubuntu/Debian
Combining TPM2 and Tang with Shamir Secret Sharing
Here's where things get interesting. Clevis supports Shamir Secret Sharing (SSS) policies that let you combine multiple unlock methods. For example, you can require both TPM2 AND Tang network access:
sudo clevis luks bind -d /dev/sdX sss \
'{"t":2,"pins":{"tpm2":{"pcr_ids":"7"},"tang":{"url":"http://tang-server.internal"}}}'
The "t":2 parameter means both pins must succeed. Change it to "t":1 if you want either one to be sufficient.
Tang Key Rotation
# Generate new keys on the Tang server
sudo tangd-keygen /var/db/tang
# Old keys are retained for existing clients
# Rebind each client to pick up the new key:
sudo clevis luks unbind -d /dev/sdX -s 2
sudo clevis luks bind -d /dev/sdX tang '{"url":"http://tang-server.internal"}'
Hardening the Secure Boot Chain
TPM2 PCR 7 binding is only meaningful if Secure Boot is actually configured properly. Without it, an attacker can just boot a modified kernel that bypasses your encryption entirely — making all that TPM2 work pointless.
Verifying Secure Boot Status
# Check current Secure Boot state
bootctl status
# Expected output should include:
# Secure Boot: enabled (user)
# TPM2 Support: yes
# Alternative check:
mokutil --sb-state
Essential Secure Boot Hardening Steps
- Enable Secure Boot in UEFI settings — set it to User Mode, not Setup Mode.
- Set a UEFI firmware password — this prevents someone from just walking up and disabling Secure Boot.
- Disable legacy/CSM boot — TPM2 requires UEFI mode, and legacy boot defeats the whole purpose.
- Disable external boot devices — prevent booting from USB or network unless you explicitly need it.
- Enroll custom Secure Boot keys (optional) — for maximum control, replace Microsoft's default keys with your own using
sbctlormokutil.
Using Unified Kernel Images (UKI) with Signed PCR Policies
For the highest level of security, you can combine Secure Boot with Unified Kernel Images. A UKI bundles the kernel, initramfs, command line, and splash screen into a single signed PE binary. This enables signed PCR policies — instead of binding to raw PCR values that change on every kernel update, you bind to a policy that verifies the UKI was signed by a trusted key:
# Fedora 41+ with systemd-boot and UKI:
# The kernel-install framework handles UKI generation automatically.
# Verify your UKI is being created:
ls /boot/efi/EFI/Linux/*.efi
# Enroll with signed PCR policy (systemd 255+):
sudo systemd-cryptenroll \
--tpm2-device=auto \
--tpm2-public-key=/path/to/pcr-signing-key.pem \
--tpm2-signature=/path/to/pcr-signature.json \
/dev/sdX
Managing Key Slots and Auditing
Once you've enrolled multiple unlock methods, it's important to keep track of what's in each LUKS2 key slot. This is one of those things that's easy to let slide — until you can't figure out which slot to wipe.
# List all enrolled key slots and their types
sudo systemd-cryptenroll /dev/sdX
# Example output:
# SLOT TYPE
# 0 password
# 1 recovery
# 2 tpm2
# 3 fido2
# Remove a specific slot (e.g., old TPM2 enrollment)
sudo systemd-cryptenroll --wipe-slot=2 /dev/sdX
# View detailed LUKS2 header information
sudo cryptsetup luksDump /dev/sdX
A few best practices for key slot management:
- Always keep at least one passphrase or recovery key as a fallback. Seriously, don't skip this.
- Document which slot number corresponds to which unlock method.
- After re-enrolling TPM2, wipe the old slot to prevent stale entries from piling up.
- Back up the LUKS2 header periodically:
sudo cryptsetup luksHeaderBackup /dev/sdX --header-backup-file /secure/backup/luks-header.img
Performance Considerations
LUKS2 with AES-XTS on modern hardware has minimal performance impact. That said, there are a few tuning opportunities worth knowing about:
- Verify AES-NI is active — run
grep -o aes /proc/cpuinfo | head -1. Without hardware acceleration, encryption throughput drops from ~4,000 MiB/s to a sluggish ~200 MiB/s. That's a massive difference. - Use the
no_read_workqueueandno_write_workqueueflags — on kernels 5.9+, these dm-crypt options bypass the kernel workqueue for encryption I/O, noticeably reducing latency on NVMe drives:
# /etc/crypttab with performance flags
cryptdata UUID=your-uuid - tpm2-device=auto,no-read-workqueue,no-write-workqueue
- Set an appropriate I/O scheduler —
noneormq-deadlinetends to perform best for encrypted NVMe drives. - Run benchmarks on your hardware — use
cryptsetup benchmarkfor cipher throughput andfiofor real-world I/O performance under encryption.
Troubleshooting Common Issues
TPM2 Fails to Unlock After Update
This is by far the most common issue you'll run into. A kernel, firmware, or bootloader update changes the PCR values, and the TPM refuses to release the key. Don't panic — just boot using your recovery key or passphrase, then re-enroll:
sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/sdX
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/sdX
systemd-cryptenroll Reports "No TPM2 Device Found"
# Verify the TPM kernel module is loaded
lsmod | grep tpm
# If empty, load it manually:
sudo modprobe tpm_tis
# Check if the device node exists:
ls -la /dev/tpmrm0
# Ensure the tpm2-abrmd service is running:
sudo systemctl status tpm2-abrmd
LUKS1 Volume — "Device Is Not a Valid LUKS2 Device"
# Check current LUKS version
sudo cryptsetup luksDump /dev/sdX | head -5
# Convert LUKS1 to LUKS2 (volume must be closed/unmounted)
sudo cryptsetup convert /dev/sdX --type luks2
Clevis Fails to Reach Tang Server at Boot
The network interface needs to be configured in the initramfs for NBDE to work at boot time. This trips people up more often than you'd expect. On dracut-based systems:
# Add network support to initramfs
echo 'add_dracutmodules+=" network "' | sudo tee /etc/dracut.conf.d/network.conf
sudo dracut -f
Frequently Asked Questions
Is TPM2 auto-unlock as secure as typing a passphrase?
Honestly, no. A passphrase exists only in your head and can't be extracted through hardware attacks. TPM2 auto-unlock is more of a "good enough" approach that protects against opportunistic theft — a stolen laptop or server with TPM2-bound encryption is effectively unreadable. However, a sophisticated attacker with prolonged physical access could potentially attempt bus-sniffing attacks on the TPM's SPI interface. For high-security environments, combine TPM2 with a PIN (--tpm2-with-pin=yes) or a FIDO2 key for two-factor disk encryption.
What happens if my TPM2 chip fails or I replace the motherboard?
The TPM2-bound key slot becomes unusable since the new TPM has completely different internal keys. This is exactly why enrolling a recovery key before the TPM2 enrollment is absolutely critical. Boot with the recovery key, then enroll the new TPM2 chip into a fresh key slot.
Can I use LUKS2 with TPM2 on a headless server with no console access?
Yes — and you've got two good options. Use TPM2 auto-unlock if the server rarely changes its boot chain, or go with Network-Bound Disk Encryption (NBDE) via Clevis/Tang. NBDE was purpose-built for exactly this scenario: the disk unlocks automatically when the Tang key server is reachable on the local network, and stays locked if the server leaves the trusted network.
Do I need to re-enroll TPM2 after every kernel update?
It depends on which PCRs you selected. If you bind to PCR 7 only (Secure Boot state), kernel updates won't affect the binding — and that's the approach I'd recommend for most setups. If you went with PCR 0+2+4+7, any firmware or bootloader change triggers re-enrollment. And binding to PCR 11 (raw UKI measurement) breaks on every kernel update, which is why it should be avoided unless you're using signed PCR policies.
How do I encrypt an existing unencrypted Linux installation?
LUKS2 actually supports in-place encryption using cryptsetup reencrypt. Back up your data first (please don't skip this), then run sudo cryptsetup reencrypt --encrypt --type luks2 --reduce-device-size 32M /dev/sdX. The --reduce-device-size flag reserves space for the LUKS2 header. Fair warning: this can take hours on large volumes, but it can be safely interrupted and resumed.