--- navigation: true title: LUKS Backup main: fluid: false --- :ellipsis{left=0px width=40rem top=10rem blur=140px} # Backup of LUKS Headers for Encrypted Disks/Volumes --- I recently realized that having just the password is not enough to unlock a LUKS volume after a failure or corruption. I learned how to dump the LUKS headers from disks/volumes and to use the serial numbers along with partition names to accurately identify which header corresponds to which disk/partition (I have 10 of them!). After struggling to do this manually, I asked Qwen3 (an LLM running on my RTX 5090) to create a script that automates the listing and identification of disks, dumps the headers, and stores them in an encrypted archive ready to be backed up on my backup server. This script: * Lists and identifies disks with their serial numbers * Lists partitions * Dumps headers into a secured folder under `/root` * Creates a temporary archive * Prompts for a password * Encrypts the archive with that password * Deletes the unencrypted archive ```bash #!/bin/bash # Directory where LUKS headers will be backed up DEST="/root/luks-headers-backup" mkdir -p "$DEST" echo "πŸ” Searching for LUKS containers on all partitions..." # Loop through all possible disk partitions (including NVMe and SATA) for part in /dev/sd? /dev/sd?? /dev/nvme?n?p?; do # Skip if the device doesn't exist if [ ! -b "$part" ]; then continue fi # Check if the partition is a LUKS encrypted volume if cryptsetup isLuks "$part"; then # Find the parent disk device (e.g. nvme0n1p4 β†’ nvme0n1) disk=$(lsblk -no pkname "$part" | head -n 1) full_disk="/dev/$disk" # Get the serial number of the parent disk SERIAL=$(udevadm info --query=all --name="$full_disk" | grep ID_SERIAL= | cut -d= -f2) if [ -z "$SERIAL" ]; then SERIAL="unknown" fi # Extract the partition name (e.g. nvme0n1p4) PART_NAME=$(basename "$part") # Build the output filename with partition name and disk serial OUTPUT="$DEST/luks-header-${PART_NAME}__${SERIAL}.img" echo "πŸ” Backing up LUKS header of $part (Serial: $SERIAL)..." # Backup the LUKS header to the output file cryptsetup luksHeaderBackup "$part" --header-backup-file "$OUTPUT" if [[ $? -eq 0 ]]; then echo "βœ… Backup successful β†’ $OUTPUT" else echo "❌ Backup failed for $part" fi fi done # Create a timestamped compressed tar archive of all header backups ARCHIVE_NAME="/root/luks-headers-$(date +%Y%m%d_%H%M%S).tar.gz" echo "πŸ“¦ Creating archive $ARCHIVE_NAME..." tar -czf "$ARCHIVE_NAME" -C "$DEST" . # Encrypt the archive symmetrically using GPG with AES256 cipher echo "πŸ” Encrypting the archive with GPG..." gpg --symmetric --cipher-algo AES256 "$ARCHIVE_NAME" if [[ $? -eq 0 ]]; then echo "βœ… Encrypted archive created: ${ARCHIVE_NAME}.gpg" # Remove the unencrypted archive for security rm -f "$ARCHIVE_NAME" else echo "❌ Encryption failed" fi ``` **Don’t forget to back up `/etc/fstab` and `/etc/crypttab` as well!**