LUKS (Linux Unified Key Setup): The Complete Guide¶
LUKS is the standard for block-device encryption on Linux. It sits on top of dm-crypt (the kernel's device-mapper encryption target) and adds a versioned on-disk header, multiple key slots, and a passphrase-hardening key derivation function. You interact with it through the cryptsetup command.
References:
Conventions used in this guide
- Target device:
/dev/sdX1(double-check the device name; formatting the wrong disk is unrecoverable).- Mapping name:
securedata(appears as/dev/mapper/securedata).- Mount point:
/mnt/securedata.- Keyfile:
/root/securedata.key.- Commands need root (
sudo).- WARNING marks destructive commands.
Table of Contents¶
- Concepts and architecture
- Preparing a disk
- Creating a LUKS container
- Keyfiles vs passphrases
- Opening, using, and closing
- Mounting automatically at boot
- Managing keys and passphrases
- Headers: inspect, back up, restore
- Inspecting and benchmarking
- Encrypting an existing disk in place
- Resizing encrypted volumes
- Stacking with LVM and software RAID
- Encrypted external and backup drives
- File-backed containers
- Modern unlock methods: TPM2, FIDO2, network
- SSDs, TRIM, and performance
- Troubleshooting
- Security best practices
- Cheat sheet
1. Concepts and architecture¶
Filesystem (ext4, xfs, ...) mounted at /mnt/securedata
|
/dev/mapper/securedata decrypted view (device-mapper "crypt" target)
|
LUKS container on /dev/sdX1 header + key slots + encrypted payload
|
Partition / disk / LVM LV / RAID / file
| Term | Meaning |
|---|---|
| dm-crypt | The kernel module doing the actual sector-level encryption. |
| LUKS header | Metadata at the start of the device: cipher, key size, KDF parameters, UUID, and up to 8 key slots (32 in LUKS2). |
| Master (volume) key | The random key that actually encrypts the data. It is stored, encrypted, in each key slot. |
| Key slot | An entry that wraps the master key with a passphrase or keyfile. Any one slot can unlock the volume, which is why you can add, change, or remove passphrases without re-encrypting data. |
| Mapping name | The name you give when opening (cryptsetup open ... securedata); the decrypted device appears as /dev/mapper/securedata. |
| KDF | Key derivation function that makes brute-forcing passphrases slow (PBKDF2 in LUKS1; Argon2id by default in LUKS2). |
LUKS1 vs LUKS2¶
| LUKS1 | LUKS2 | |
|---|---|---|
Default in current cryptsetup |
No | Yes |
| KDF | PBKDF2 | Argon2i/Argon2id (memory-hard) |
| Key slots | 8 | 32 |
| Header | Fixed, binary | JSON metadata, redundant copy |
| Labels, tokens (TPM2/FIDO2), online re-encryption | No | Yes |
GRUB compatibility for /boot |
Best | Improving (older GRUB versions have Argon2 limits) |
Use LUKS2 unless something specific (such as an old bootloader) requires LUKS1. Check which you have with cryptsetup luksDump.
Sensible cipher defaults¶
Current defaults are aes-xts-plain64 with a 512-bit key (XTS splits it into two 256-bit AES keys, so this is "AES-256") and a SHA-256 hash. Older notes often show -c aes -s 256 -h sha256: that works, but it selects a legacy/weaker mode and half the key size. Prefer the modern defaults and only override when you have a reason.
2. Preparing a disk¶
2.1 Install the tools¶
sudo apt install cryptsetup # Debian/Ubuntu
sudo dnf install cryptsetup # RHEL/Fedora
Loading the aes and sha256 modules by hand (modprobe aes) is not needed on any modern kernel; they load on demand.
2.2 Partition (optional but recommended)¶
lsblk # identify the correct device
sudo parted -s /dev/sdX mklabel gpt mkpart primary 1MiB 100%
sudo partprobe /dev/sdX # make the kernel re-read the table
If fdisk warns that the partition table could not be re-read because the device is busy, run partprobe (or kpartx) or reboot before continuing.
You can also encrypt an entire unpartitioned disk (/dev/sdX). Partitions make the disk's purpose obvious to other tools and administrators.
2.3 Securely erase old data (recommended for reused disks)¶
Encryption only protects data written after setup. If the disk previously held sensitive data, overwrite it first, and if it is a new disk, filling it with random data also hides how much of the volume is actually used.
Fast method: overwrite through a throwaway plain dm-crypt mapping. The encrypted zeros look like random data on disk and this is much faster than reading from /dev/urandom:
sudo cryptsetup open --type plain -d /dev/urandom /dev/sdX1 to_be_wiped # WARNING: destroys data
sudo dd if=/dev/zero of=/dev/mapper/to_be_wiped bs=1M status=progress
sudo cryptsetup close to_be_wiped
Simple method:
sudo dd if=/dev/urandom of=/dev/sdX1 bs=1M status=progress # WARNING: destroys data
Alternative: badblocks -c 10240 -wsvt random /dev/sdX1 is faster than dd from /dev/urandom on some systems, and doubles as a surface test.
SSDs and NVMe: overwriting is unreliable because of wear leveling and over-provisioning. Use
blkdiscard /dev/nvme0n1, ATA Secure Erase (hdparm --security-erase), ornvme formatinstead. See section 16.
3. Creating a LUKS container¶
3.1 With a passphrase (the simplest and most common)¶
sudo cryptsetup luksFormat --type luks2 --verify-passphrase /dev/sdX1
You will be asked to type YES in uppercase, then to enter and confirm a passphrase.
Explicit options (all of these are the current defaults, shown for clarity):
sudo cryptsetup luksFormat --type luks2 \
--cipher aes-xts-plain64 --key-size 512 --hash sha256 \
--pbkdf argon2id --label securedata \
--verify-passphrase /dev/sdX1
Useful extras:
| Option | Purpose |
|---|---|
--label NAME |
LUKS2 label, usable as /dev/disk/by-label-style reference in crypttab (LABEL=-like lookups through the container's own label are only available in LUKS2). |
--sector-size 4096 |
Use 4 KiB crypto sectors on 4Kn/AF disks for better performance (LUKS2 only). |
--iter-time 2000 |
Milliseconds spent on the KDF when unlocking (default 2000). Higher is safer but slower to open. |
-d / --key-file FILE |
Use a keyfile instead of a passphrase (section 4). |
3.2 With a keyfile¶
sudo cryptsetup luksFormat --type luks2 --key-file /root/securedata.key /dev/sdX1
See section 4 for how to create the keyfile. (-d is the short form of --key-file.)
3.3 Verify¶
sudo cryptsetup isLuks /dev/sdX1 && echo "is LUKS"
sudo cryptsetup luksDump /dev/sdX1
lsblk --fs /dev/sdX1
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sdX1 crypto_LUKS 2 securedata 1f7c2b9e-4d0a-4e73-9a1b-6c5d8e2f0a44
Note this UUID: it identifies the encrypted container and is what crypttab needs (section 6). The filesystem inside will get a different UUID.
4. Keyfiles vs passphrases¶
A key slot can be unlocked by a passphrase or a keyfile, and you can have both on the same volume at once (for example: a keyfile for unattended boot mounts, plus a passphrase you keep offline for recovery). An older note claimed these were mutually exclusive; that is not true for LUKS.
| Passphrase | Keyfile | |
|---|---|---|
| Convenience | Must be typed at every unlock | Automatic, good for unattended mounts |
| Risk | Weak passphrases can be guessed | Anyone who can read the file can unlock the disk |
| Best for | Laptops, portable drives | Servers, secondary data disks whose keyfile lives on an already-encrypted root filesystem |
4.1 Create a keyfile¶
Use random bytes, not a typed password, and create it with tight permissions from the start:
sudo bash -c 'umask 077; dd if=/dev/urandom of=/root/securedata.key bs=4096 count=1'
sudo chown root:root /root/securedata.key
sudo chmod 0400 /root/securedata.key
bs=32 count=1(256 bits) is the minimum sensible size; larger is fine (cryptsetup reads up to 8 MiB by default).- A typed passphrase saved to a file works too, but be careful about trailing newlines (
echo -norprintf); cryptsetup treats a keyfile's bytes literally, including a final newline, whereas an interactive passphrase excludes it. - Store keyfiles in a root-only location such as
/root/or/etc/cryptsetup-keys.d/. Use absolute paths (~is not expanded at boot and is not usable incrypttab). - An old note said hidden keyfile names (starting with
.) fail. Current cryptsetup has no such rule, but it is a harmless habit to avoid them because path and permission mistakes are easier to spot. - Back the keyfile up somewhere safe and offline. If the keyfile is lost and it is the only key, the data is gone.
4.2 Add a keyfile to a container that already has a passphrase¶
sudo cryptsetup luksAddKey /dev/sdX1 /root/securedata.key
Enter any existing passphrase:
To authenticate with an existing keyfile instead:
sudo cryptsetup luksAddKey --key-file /root/old.key /dev/sdX1 /root/new.key
4.3 Random-password alternative¶
If you need a typed-in-able secret, generate a strong one instead of inventing one:
pwgen -s -n 40 1 # 40 random characters
Use it as a passphrase (typed) rather than as a file, and put it in your password manager.
5. Opening, using, and closing¶
open and luksOpen are aliases; close and luksClose likewise.
5.1 Open (unlock)¶
sudo cryptsetup open /dev/sdX1 securedata # prompts for passphrase
sudo cryptsetup open --key-file /root/securedata.key /dev/sdX1 securedata
sudo cryptsetup open --type luks /dev/sdX1 securedata # force LUKS1/2 detection, useful for recovery
5.2 Create a filesystem (first time only)¶
sudo mkfs.xfs /dev/mapper/securedata
# or
sudo mkfs.ext4 -m 1 /dev/mapper/securedata # -m 1: reserve 1% for root (default is 5%, wasteful on data disks)
XFS is a good default for large backup/data disks (fast, scales well) but cannot be shrunk. Use ext4 if you may need to shrink later.
5.3 Mount¶
sudo mkdir -p /mnt/securedata
sudo mount /dev/mapper/securedata /mnt/securedata
df -Th /mnt/securedata
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/securedata xfs 1.9T 14G 1.9T 1% /mnt/securedata
lsblk --fs /dev/sdX
NAME FSTYPE LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sdX
└─sdX1 crypto_LUKS 1f7c2b9e-4d0a-4e73-9a1b-6c5d8e2f0a44
└─securedata xfs 15967ecd-bfa2-4f8e-b2a9-3dd3feeee3ba 1.8T 1% /mnt/securedata
5.4 Close (lock)¶
Order matters: unmount first, then close.
sudo umount /mnt/securedata
sudo cryptsetup close securedata
To confirm it is really closed, check that /dev/mapper/securedata is gone and lsblk shows no crypt child.
6. Mounting automatically at boot¶
Two files cooperate: at boot, /etc/crypttab unlocks the container and creates the mapping, then /etc/fstab mounts the filesystem from that mapping.
6.1 Find the two UUIDs¶
lsblk --fs /dev/sdX
ls -la /dev/disk/by-uuid | grep sdX
- The LUKS container UUID (FSTYPE
crypto_LUKS) goes incrypttab. - The filesystem UUID (the child under
crypt, e.g.xfs) goes infstab.
Do not mix them up: it is the most common cause of a drive that "will not mount at boot".
6.2 /etc/crypttab¶
Format: name device keyfile-or-none options
# /etc/crypttab
securedata UUID=1f7c2b9e-4d0a-4e73-9a1b-6c5d8e2f0a44 /root/securedata.key luks,nofail
- Use
noneinstead of a keyfile path to be prompted for a passphrase at boot. - Useful options:
nofail(do not fail the boot if the disk is missing),noauto(do not unlock at boot; unlock manually),discard(allow TRIM, see section 16),x-systemd.device-timeout=10s,readonly. - The
luksoption is optional with systemd but harmless and self-documenting.
6.3 /etc/fstab¶
UUID=15967ecd-bfa2-4f8e-b2a9-3dd3feeee3ba /mnt/securedata xfs defaults,nofail 0 2
or by mapper path (equally stable, because you chose the name):
/dev/mapper/securedata /mnt/securedata ext4 defaults,nofail 0 2
For an XFS filesystem the last field is conventionally 0 (XFS does not use fsck at boot); ext4 uses 2 for non-root filesystems.
6.4 Test without rebooting¶
sudo systemctl daemon-reload # regenerate units from crypttab/fstab
sudo systemctl start [email protected]
sudo mount -a
findmnt /mnt/securedata
Then do a real reboot at least once to confirm the boot-time path. If the volume backs the root filesystem or is needed early, also rebuild the initramfs so it knows how to unlock it:
sudo update-initramfs -u # Debian/Ubuntu
sudo dracut -f # RHEL/Fedora
6.5 Security note on boot-time keyfiles¶
Automatic unlock with a keyfile only protects against someone who steals the disk alone. If the keyfile sits on the same unencrypted machine, an attacker with the whole machine has everything. The strong pattern is: encrypt the root filesystem with a passphrase (or TPM2, section 15), and keep the keyfiles for secondary disks on that encrypted root.
7. Managing keys and passphrases¶
7.1 Inspect key slots¶
sudo cryptsetup luksDump /dev/sdX1
Look at the Keyslots: section (LUKS2) or Key Slot N: ENABLED lines (LUKS1) to see how many are in use.
7.2 Add, change, and remove keys¶
# Add a new passphrase (authenticate with any existing one)
sudo cryptsetup luksAddKey /dev/sdX1
# Change a passphrase in place
sudo cryptsetup luksChangeKey /dev/sdX1
# Remove a passphrase or keyfile by supplying it (whichever slot it matches gets removed)
sudo cryptsetup luksRemoveKey /dev/sdX1
sudo cryptsetup luksRemoveKey /dev/sdX1 /root/old.key
# Remove a specific slot by number
sudo cryptsetup luksKillSlot /dev/sdX1 0
luksDelKey is a deprecated name for luksKillSlot.
7.3 Rotating a passphrase safely¶
The pattern is add the new one first, verify it, then remove the old one. Never remove your last working key.
sudo cryptsetup luksAddKey /dev/sdX1 # authenticate with the old passphrase; enter the new one
sudo cryptsetup open --test-passphrase /dev/sdX1 # verify the NEW passphrase works
sudo cryptsetup luksKillSlot /dev/sdX1 0 # remove the old slot (authenticate with the new passphrase)
--test-passphrase checks that a passphrase or keyfile unlocks the device without actually opening it. Add --key-file to test a keyfile.
7.4 A compromised key does not equal re-encryption¶
Changing a passphrase re-wraps the master key; it does not change the master key itself. If someone had the header and an old passphrase (for example, an old header backup), they can still decrypt with that combination. If a key is truly compromised, re-encrypt the volume with a new master key (cryptsetup reencrypt, section 10).
7.5 Destroy access on purpose¶
sudo cryptsetup luksErase /dev/sdX1 # WARNING: wipes ALL key slots; data becomes permanently unreadable
This is the quick "crypto-shred" option. It is instant, but any header backup you kept would still unlock the data, so destroy those too.
8. Headers: inspect, back up, restore¶
The LUKS header is a single point of failure. If it is damaged or overwritten (a stray dd, a bad partition-table write, a failing sector), the data is unrecoverable even if you know the passphrase. Back it up immediately after creating a container.
8.1 Back up¶
sudo cryptsetup luksHeaderBackup /dev/sdX1 --header-backup-file /safe/place/sdX1-luks-header.img
- Store it off the machine and encrypted (for example, in an encrypted password-manager attachment or on another encrypted volume).
- Treat the backup as sensitive: combined with an old passphrase that was later removed, it can unlock the data. Refresh the backup after key changes and delete stale ones.
8.2 Restore¶
sudo cryptsetup luksHeaderRestore /dev/sdX1 --header-backup-file /safe/place/sdX1-luks-header.img
8.3 Open using a header backup without restoring it¶
Useful to check that a backup works, or to recover when the on-disk header is damaged:
sudo cryptsetup luksOpen /dev/sdX1 recovered --header /safe/place/sdX1-luks-header.img
8.4 Detached headers¶
You can also create a container whose header lives elsewhere (for example on a USB stick), so the disk itself looks like random data:
sudo cryptsetup luksFormat --header /media/usb/sdX1.header /dev/sdX1
sudo cryptsetup open --header /media/usb/sdX1.header /dev/sdX1 securedata
Lose the header file and the data is gone, so back it up as above.
9. Inspecting and benchmarking¶
9.1 Status of an open mapping¶
sudo cryptsetup status securedata
/dev/mapper/securedata is active and is in use.
type: LUKS2
cipher: aes-xts-plain64
keysize: 512 bits
key location: keyring
device: /dev/sdX1
sector size: 512
offset: 32768 sectors
size: 3906957312 sectors
mode: read/write
9.2 List all encrypted devices and how they are stacked¶
sudo dmsetup ls --tree
sudo dmsetup ls --target crypt
sudo dmsetup info -C
lsblk # look for TYPE "crypt"
enc_scsi-35000cca23c0fce44 (253:8)
└─ (8:160)
9.3 Benchmark cipher speed¶
cryptsetup benchmark
Shows encryption/decryption throughput per cipher on your CPU (and KDF timings). On CPUs with AES-NI, aes-xts usually runs at several GB/s, which is why encryption rarely bottlenecks storage. If aes is far slower than expected, check that AES-NI is available (grep -m1 -o aes /proc/cpuinfo) or consider xchacha20,aes-adiantum-plain64 on CPUs without hardware AES.
10. Encrypting an existing disk in place¶
LUKS2 can encrypt a disk that already holds data, without a full copy, using cryptsetup reencrypt. It needs 16-32 MiB of free space at the start of the device for the header, so shrink the filesystem slightly first (or use a detached header).
sudo umount /dev/sdX1
sudo e2fsck -f /dev/sdX1 # ext4 only
sudo resize2fs /dev/sdX1 <current-size-minus-32M> # make room for the header (ext4)
sudo cryptsetup reencrypt --encrypt --reduce-device-size 32M /dev/sdX1
sudo cryptsetup open /dev/sdX1 securedata
sudo mount /dev/mapper/securedata /mnt/securedata
Take a full backup first. A power loss or crash mid-way can be fatal; the tool records progress and can resume, but do not rely on that alone. The same tool can also change the cipher or master key of an existing LUKS2 container (cryptsetup reencrypt /dev/sdX1) and decrypt one (--decrypt).
To convert a LUKS1 header to LUKS2 (or the reverse) in place:
sudo cryptsetup luksHeaderBackup /dev/sdX1 --header-backup-file /safe/place/pre-convert.img # first!
sudo cryptsetup convert --type luks2 /dev/sdX1
11. Resizing encrypted volumes¶
The layers must be resized in the right order.
Growing (bottom up): underlying device, then the LUKS mapping, then the filesystem.
# 1. Grow the partition/LV/disk underneath (for example lvextend -L +20G /dev/vg/lv_crypt)
sudo cryptsetup resize securedata # 2. tell dm-crypt the device grew (works online)
sudo resize2fs /dev/mapper/securedata # 3a. ext4
sudo xfs_growfs /mnt/securedata # 3b. XFS (takes the mount point)
Shrinking (top down), ext4 only and offline: filesystem, then LUKS, then the underlying device, keeping each lower layer larger than the one above it. The step-by-step version is in the companion LVM guide. XFS cannot be shrunk.
12. Stacking with LVM and software RAID¶
12.1 Common layouts¶
| Layout | Stack | Notes |
|---|---|---|
| LVM on LUKS | disk → LUKS → PV → VG → LVs | One passphrase unlocks everything; LV names and layout are hidden. Standard for full-disk encryption installers. |
| LUKS on LVM | disk → PV → VG → LV → LUKS → filesystem | Per-volume keys; some LVs may stay unencrypted. |
| LVM on LUKS on mdadm RAID | 2 disks → md RAID1 → LUKS → PV → VG → LVs | Encrypt once, above the RAID, so you do the crypto work once instead of once per disk. |
12.2 Open and mount a LUKS + LVM + RAID system from a live environment¶
Example: a machine whose boot is md126 (RAID1, unencrypted /boot) and whose root is md127 (RAID1) containing LUKS containing the vg_root volume group.
Before opening:
sudo lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 1.8T 0 disk
├─sda1 8:1 0 512M 0 part
│ └─md126 9:126 0 511M 0 raid1 /boot
└─sda2 8:2 0 1.8T 0 part
└─md127 9:127 0 1.8T 0 raid1
sdb 8:16 0 1.8T 0 disk
├─sdb1 8:17 0 512M 0 part
│ └─md126 9:126 0 511M 0 raid1 /boot
└─sdb2 8:18 0 1.8T 0 part
└─md127 9:127 0 1.8T 0 raid1
Assemble the RAID if the live system did not (mdadm --assemble --scan), then unlock LUKS and activate LVM:
sudo cryptsetup open /dev/md127 luksrecovery --type luks # md126 is /boot, md127 holds the encrypted root
sudo vgscan # volume groups are normally found automatically
sudo vgchange -ay vg_root # activate them if they were not
After opening:
sudo lsblk
sda2 8:2 0 1.8T 0 part
└─md127 9:127 0 1.8T 0 raid1
└─luksrecovery 253:0 0 1.8T 0 crypt
├─vg_root-root 253:1 0 1.8T 0 lvm /
├─vg_root-swap 253:2 0 1G 0 lvm [SWAP]
└─vg_root-tmp 253:3 0 1G 0 lvm /tmp
(If you do not pass a name, the mapping is auto-named after the container, for example luks-<uuid>.)
Mount and work:
sudo mkdir -p /mnt/luksrecovery
sudo mount /dev/mapper/vg_root-root /mnt/luksrecovery
Note: in a mapper name, a hyphen inside an LV name is doubled (root-0 appears as vg_root-root--0).
12.3 Close it all again, in reverse order¶
sudo umount /mnt/luksrecovery
sudo vgchange -an vg_root # deactivate the VG so it releases the LUKS device
sudo cryptsetup close luksrecovery
sudo mdadm --stop /dev/md127 # only if you assembled it manually and are done
Skipping vgchange -an is the classic cause of "Device ... is still in use" (see troubleshooting).
13. Encrypted external and backup drives¶
13.1 One-time setup¶
# Partition, then:
sudo cryptsetup luksFormat --type luks2 --label backupdrive /dev/sdX1 # passphrase, or add --key-file
sudo cryptsetup luksHeaderBackup /dev/sdX1 --header-backup-file ~/backupdrive-header.img # store it safely elsewhere!
sudo cryptsetup open /dev/sdX1 backupdrive
sudo mkfs.xfs -L backup /dev/mapper/backupdrive
sudo cryptsetup close backupdrive
For a portable drive that will be plugged into other machines, use a passphrase, not a keyfile, and prefer ext4 or XFS with a label so desktops can auto-detect it (most desktop environments will prompt for the passphrase and mount it for you).
13.2 Recover files from an encrypted drive on another machine¶
sudo cryptsetup open /dev/sdX1 luksrecovery --type luks
sudo mkdir -p /mnt/luksrecovery
sudo mount /dev/mapper/luksrecovery /mnt/luksrecovery
# ...copy files off...
sudo umount /mnt/luksrecovery
sudo cryptsetup close luksrecovery
If the header is damaged, add --header /path/to/header-backup.img to the open command (section 8.3).
13.3 Rotating offsite backup disks: a helper script¶
Several disks rotated offsite can share one small script. Look up each disk's LUKS container UUID with lsblk --fs while it is attached, then list them in the script.
#!/usr/bin/env bash
# /usr/local/sbin/backupdisk: identify, mount, or unmount the rotating backup disk
set -euo pipefail
# Map friendly names to LUKS container UUIDs (from: lsblk --fs)
declare -A DISKS=(
[disk1]="REPLACE-WITH-LUKS-UUID-1"
[disk2]="REPLACE-WITH-LUKS-UUID-2"
)
KEYFILE="/root/backupdrive.key"
MAPPER="backupdrive"
MOUNTPOINT="/mnt/backupdrive"
[[ $EUID -eq 0 ]] || { echo "Run as root." >&2; exit 1; }
find_disk() {
local name uuid
for name in "${!DISKS[@]}"; do
uuid="${DISKS[$name]}"
if [[ -e "/dev/disk/by-uuid/$uuid" ]]; then
echo "$name"; return 0
fi
done
return 1
}
case "${1:-}" in
status)
if name=$(find_disk); then echo "Attached: $name"; else echo "No backup disk attached."; fi
;;
mount)
name=$(find_disk) || { echo "No backup disk attached." >&2; exit 1; }
dev="/dev/disk/by-uuid/${DISKS[$name]}"
cryptsetup open --key-file "$KEYFILE" "$dev" "$MAPPER"
mkdir -p "$MOUNTPOINT"
mount "/dev/mapper/$MAPPER" "$MOUNTPOINT"
echo "Mounted $name at $MOUNTPOINT"
;;
umount|unmount)
umount "$MOUNTPOINT"
cryptsetup close "$MAPPER"
echo "Unmounted and locked."
;;
*)
echo "Usage: $0 {status|mount|umount}" >&2
exit 2
;;
esac
Improvements over a naive version: it resolves the disk by UUID (device names such as /dev/sdb change between attachments), it quotes all variables, it fails fast on errors (set -euo pipefail), and it does not hardcode which disk is which through nested if tests.
14. File-backed containers¶
You can create an encrypted "vault" inside an ordinary file (handy for portable secrets or for testing all of the above without a spare disk):
truncate -s 10G vault.img # sparse file
sudo cryptsetup luksFormat vault.img
sudo cryptsetup open vault.img vault # cryptsetup sets up the loop device for you
sudo mkfs.ext4 /dev/mapper/vault
sudo mount /dev/mapper/vault /mnt/vault
# ...use it...
sudo umount /mnt/vault
sudo cryptsetup close vault
Grow it later: truncate -s +5G vault.img, then sudo losetup -c <loopdev> (if open), cryptsetup resize vault, and resize2fs.
15. Modern unlock methods: TPM2, FIDO2, network¶
LUKS2 supports tokens, letting hardware or network services unlock a volume in place of typing a passphrase. Always keep a passphrase or recovery key slot in addition, because firmware updates or hardware changes can invalidate TPM bindings.
15.1 systemd-cryptenroll (TPM2, FIDO2, recovery key)¶
# Add a printable recovery key (do this first, store it offline)
sudo systemd-cryptenroll --recovery-key /dev/sdX1
# Bind to the TPM2 chip, sealed to Secure Boot state (PCR 7); optionally add a PIN
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/sdX1
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 --tpm2-with-pin=yes /dev/sdX1
# Enroll a FIDO2 security key
sudo systemd-cryptenroll --fido2-device=auto /dev/sdX1
# Remove a method later
sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/sdX1
Then reference the token in crypttab:
securedata UUID=1f7c2b9e-4d0a-4e73-9a1b-6c5d8e2f0a44 none tpm2-device=auto,nofail
15.2 Network-bound unlock (Clevis and Tang)¶
For servers that must boot unattended in a trusted network but stay protected if a disk is stolen, Clevis binds a LUKS slot to a Tang server. The disk only unlocks when it can reach the Tang server:
sudo clevis luks bind -d /dev/sdX1 tang '{"url":"http://tang.example.internal"}'
sudo clevis luks list -d /dev/sdX1
16. SSDs, TRIM, and performance¶
- TRIM through dm-crypt is off by default, because passing discards reveals which blocks are unused (a minor information leak about filesystem usage). Enable it if you accept that trade-off for better SSD performance and longevity, either with the
discardoption incrypttab(securedata UUID=... none luks,discard), or on open withcryptsetup open --allow-discards. For LUKS2 you can persist it:cryptsetup --allow-discards --persistent refresh securedata. - Prefer scheduled
fstrim(systemctl enable --now fstrim.timer) over thediscardmount option, which trims on every delete. - On fast NVMe with many cores, the kernel's dm-crypt work queues can hurt latency. Newer kernels (5.9+) and cryptsetup (2.3.4+) provide
--perf-no_read_workqueue --perf-no_write_workqueue, which can be persisted with--persistent. Benchmark before and after; it is a workload-specific tuning, not a universal win. - Match
--sector-size 4096to 4K-native disks at format time to avoid read-modify-write overhead. - Overwrite-based secure erasure is unreliable on flash. Use a firmware secure erase or
blkdiscard, and rely on encryption from day one so that discarding the header (luksErase) is a real erase.
17. Troubleshooting¶
| Symptom | Likely cause | What to do |
|---|---|---|
Device securedata is still in use. when closing |
Filesystem still mounted, or an LVM VG / process / another mapping sits on top | umount; vgchange -an <vg>; dmsetup ls --tree; lsof/fuser -vm; check /sys/block/dm-*/holders/ |
No key available with this passphrase. |
Wrong passphrase, wrong keyboard layout (especially in an initramfs prompt), keyfile with trailing newline mismatch, or wrong device | Retry; test with --test-passphrase; check the layout; for keyfiles use the exact bytes used at creation |
Device /dev/sdX1 is not a valid LUKS device. |
Wrong device, the header was overwritten, or a detached header is needed | cryptsetup isLuks; check blkid; try --header <backup> (section 8) |
Device /dev/sdX1 does not exist or access denied. |
Typo, missing privileges, or the disk is not attached | Use sudo; verify with lsblk |
Device or resource busy on luksFormat |
The device is mounted, in an active RAID/LVM, or has open mappings | Unmount, deactivate, and stop anything holding it |
| Boots to emergency mode after editing crypttab/fstab | Wrong UUID (container vs filesystem), missing keyfile, or no nofail |
Boot rescue; fix the UUIDs; add nofail; systemctl daemon-reload; rebuild the initramfs if needed |
Failed to start [email protected] |
Wrong keyfile path or permissions, or the device does not exist | journalctl -u systemd-cryptsetup@name; verify the path is absolute and the file is 0400 root |
| Crypttab keyfile ignored, prompted for passphrase anyway | Path is a relative or ~ path; keyfile not readable in the initramfs |
Use an absolute path; for early-boot devices include the keyfile in the initramfs |
| Very slow unlock | High KDF cost (Argon2 memory/time), especially in the initramfs on small RAM | Lower --iter-time on the slot (luksChangeKey --iter-time 1000) if acceptable |
Failed to setup dm-crypt key mapping |
Kernel crypto modules or keyring issue | modprobe dm_crypt; check kernel logs with dmesg |
| Data unreadable after a header overwrite | Header damaged | Restore from the header backup (section 8). Without one, recovery is not possible |
| After partition changes the kernel still sees the old table | Device busy when the table was written | partprobe, kpartx, or reboot |
Handy diagnostics:
lsblk -f
sudo cryptsetup luksDump /dev/sdX1
sudo cryptsetup status securedata
sudo dmsetup info -C
sudo dmsetup ls --tree
journalctl -b -u 'systemd-cryptsetup@*'
dmesg | grep -Ei 'crypt|dm-'
"Device is still in use" in detail¶
When closing a mapping, you may see:
Device luksrecovery is still in use.
lsof | grep luksrecovery returns nothing, but dmsetup info -C shows the mapping still open. The mapping is held by something stacked on top of it, typically an active LVM volume group. Deactivate it, then close:
sudo vgchange -an vg_root
sudo cryptsetup close luksrecovery
18. Security best practices¶
- Back up the header and store it off-machine and encrypted (section 8). Practice a restore once.
- Keep an independent recovery path: a strong passphrase in a password manager, in addition to any keyfile, TPM2, or FIDO2 method.
- Use strong passphrases (a 5-6 word random passphrase or 20+ random characters) for anything an attacker can copy offline. Argon2 slows guessing but does not rescue a weak passphrase.
- Protect keyfiles: root-owned, mode
0400, on an encrypted filesystem, never in a world-readable location or a git repository. - Encrypt from the start. Adding encryption later does not scrub old plaintext left in unallocated blocks or on flash spare areas.
- Encrypt swap and
/tmp(or use encrypted LVM for them), since they can leak secrets from memory. - Lock or power off when unattended. Encryption at rest does not help against someone with access to a running, unlocked system or its RAM (cold-boot and DMA attacks).
- Test restores and boots. Verify unlock via
--test-passphrase, reboot-testcrypttab, and periodically test that you can open the volume using only your backups. - Use
nofailon secondary volumes so a missing disk cannot block boot. - Retire disks properly:
luksErase(or overwrite the header) plus a firmware secure erase for SSDs; destroy header backups too. - Keep
cryptsetupand the kernel updated, and prefer LUKS2 defaults over legacy options.
19. Cheat sheet¶
Create and format¶
sudo cryptsetup luksFormat --type luks2 /dev/sdX1 # passphrase
sudo cryptsetup luksFormat --key-file /root/securedata.key /dev/sdX1
sudo cryptsetup luksHeaderBackup /dev/sdX1 --header-backup-file hdr.img
sudo cryptsetup open /dev/sdX1 securedata
sudo mkfs.xfs /dev/mapper/securedata
Use¶
sudo cryptsetup open [--key-file KEY] /dev/sdX1 securedata
sudo mount /dev/mapper/securedata /mnt/securedata
sudo umount /mnt/securedata
sudo cryptsetup close securedata
Keys¶
sudo cryptsetup luksDump /dev/sdX1
sudo cryptsetup luksAddKey /dev/sdX1 [/root/new.key]
sudo cryptsetup luksChangeKey /dev/sdX1
sudo cryptsetup luksRemoveKey /dev/sdX1 [keyfile]
sudo cryptsetup luksKillSlot /dev/sdX1 <slot>
sudo cryptsetup open --test-passphrase /dev/sdX1
Boot automation¶
# /etc/crypttab
securedata UUID=<LUKS-container-uuid> /root/securedata.key luks,nofail
# /etc/fstab
UUID=<filesystem-uuid> /mnt/securedata xfs defaults,nofail 0 2
sudo systemctl daemon-reload && sudo systemctl start systemd-cryptsetup@securedata && sudo mount -a
Inspect¶
cryptsetup benchmark
sudo cryptsetup status securedata
sudo dmsetup ls --tree
lsblk --fs
Recover / clean up¶
sudo cryptsetup luksHeaderRestore /dev/sdX1 --header-backup-file hdr.img
sudo cryptsetup open /dev/sdX1 recovered --header hdr.img
sudo vgchange -an <vg> # before closing if LVM sits on top
sudo cryptsetup luksErase /dev/sdX1 # crypto-shred (destroys all key slots)
Further reading¶
man cryptsetup,man crypttab,man systemd-cryptenroll- Linux Unified Key Setup, Wikipedia
- cryptsetup wiki and its FAQ
- Arch Wiki: dm-crypt and dm-crypt/Device encryption