Andrew Mercer
on this page

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

  1. Concepts and architecture
  2. Preparing a disk
  3. Creating a LUKS container
  4. Keyfiles vs passphrases
  5. Opening, using, and closing
  6. Mounting automatically at boot
  7. Managing keys and passphrases
  8. Headers: inspect, back up, restore
  9. Inspecting and benchmarking
  10. Encrypting an existing disk in place
  11. Resizing encrypted volumes
  12. Stacking with LVM and software RAID
  13. Encrypted external and backup drives
  14. File-backed containers
  15. Modern unlock methods: TPM2, FIDO2, network
  16. SSDs, TRIM, and performance
  17. Troubleshooting
  18. Security best practices
  19. 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.

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.

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), or nvme format instead. 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 -n or printf); 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 in crypttab).
  • 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 in crypttab.
  • The filesystem UUID (the child under crypt, e.g. xfs) goes in fstab.

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 none instead 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 luks option 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 discard option in crypttab (securedata UUID=... none luks,discard), or on open with cryptsetup open --allow-discards. For LUKS2 you can persist it: cryptsetup --allow-discards --persistent refresh securedata.
  • Prefer scheduled fstrim (systemctl enable --now fstrim.timer) over the discard mount 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 4096 to 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

  1. Back up the header and store it off-machine and encrypted (section 8). Practice a restore once.
  2. Keep an independent recovery path: a strong passphrase in a password manager, in addition to any keyfile, TPM2, or FIDO2 method.
  3. 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.
  4. Protect keyfiles: root-owned, mode 0400, on an encrypted filesystem, never in a world-readable location or a git repository.
  5. Encrypt from the start. Adding encryption later does not scrub old plaintext left in unallocated blocks or on flash spare areas.
  6. Encrypt swap and /tmp (or use encrypted LVM for them), since they can leak secrets from memory.
  7. 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).
  8. Test restores and boots. Verify unlock via --test-passphrase, reboot-test crypttab, and periodically test that you can open the volume using only your backups.
  9. Use nofail on secondary volumes so a missing disk cannot block boot.
  10. Retire disks properly: luksErase (or overwrite the header) plus a firmware secure erase for SSDs; destroy header backups too.
  11. Keep cryptsetup and 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