Note: This article is heavily based on The Uncommitted Changes Blog by Pankaj Raghav. All credit goes to the original author.


Setting up auto-mounting for a LUKS-encrypted external drive on NixOS requires coordinating LUKS decryption, udev device detection, and systemd mounting. Here’s how to configure it so the drive automatically decrypts and mounts when connected.

First, encrypt the drive if you haven’t already:

1
2
3
4
5
6
cryptsetup luksFormat /dev/sda1
## Enter password
cryptsetup open /dev/sda1 vault
dd bs=512 count=4 if=/dev/random of=/root/keyfile.key iflag=fullblock
cryptsetup luksAddKey /dev/sda1 /root/keyfile.key
sudo mkfs.btrfs -L vault /dev/mapper/vault

The keyfile allows automatic decryption without typing a password each time. Your device layout will look like:

1
2
3
4
$ lsblk -f
nvme1n1                                                                                 
└─nvme1n1p1 crypto_LUKS 2           1342cc60-7514-4d70-8d1b-303b009cea34                
  └─vault   btrfs             vault e04b44ad-1beb-4902-9b91-e5e6ed43e51c  369.6G    20% /mnt/vault

Configure /etc/crypttab to automatically decrypt the drive. The noauto option prevents systemd from trying to unlock at boot:

1
2
3
environment.etc.crypttab.text = ''
  vault UUID=1342cc60-7514-4d70-8d1b-303b009cea34 /root/mykeyfile.key noauto
'';

Note that the UUID here is the partition’s UUID (the LUKS container), not the filesystem UUID. This generates a systemd service systemd-cryptsetup@vault.service that you can test manually:

1
sudo systemctl start systemd-cryptsetup@vault.service

Use a udev rule to trigger decryption when the drive is connected. First, identify your device’s unique identifier:

1
udevadm info /dev/sda1

Create a udev rule that starts the cryptsetup service when your specific device is detected:

1
2
3
4
services.udev.extraRules = ''
  SUBSYSTEM=="block" ENV{ID_WWN}=="nvme.144d-933432304e50305234983030383659-53616d73756e6720506f727461626c6520535344205835-00000001",\
  ENV{SYSTEMD_WANTS}="systemd-cryptsetup@vault.service"
'';

Replace ID_WWN with your device’s unique identifier from udevadm info. Then configure the mount point for the decrypted filesystem:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fileSystems."/mnt/vault" = {
  device = "/dev/disk/by-uuid/e04b44ad-1beb-4902-9b91-e5e6ed43e51c";
  fsType = "btrfs";
  options = [
    "defaults"
    "noatime"
    "x-systemd.automount"
    "x-systemd.device-timeout=5"
    "noauto"
  ];
};

This UUID is the filesystem UUID (the decrypted /dev/mapper/vault), not the partition UUID. The noauto option prevents mounting at boot, while x-systemd.automount handles automatic mounting when the device is detected.

Here’s the complete external-disk.nix configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  environment.etc.crypttab.text = ''
    vault UUID=1342cc60-7514-4d70-8d1b-303b009cea34 /root/mykeyfile.key noauto
  '';

  # The above crypttab creates a systemd cryptsetup vault service, which the below udev rule depends on
  services.udev.extraRules = ''
    SUBSYSTEM=="block" ENV{ID_WWN}=="nvme.144d-933432304e50305234983030383659-53616d73756e6720506f727461626c6520535344205835-00000001", ENV{SYSTEMD_WANTS}="systemd-cryptsetup@vault.service"
  '';

  fileSystems."/mnt/vault" = {
    device = "/dev/disk/by-uuid/e04b44ad-1beb-4902-9b91-e5e6ed43e51c";
    fsType = "btrfs";
    options = [
      "defaults"
      "noatime"
      "x-systemd.automount"
      "x-systemd.device-timeout=5"
      "noauto"
    ];
  };
}

Now when you connect your external drive, it will automatically decrypt and mount—perfect for automated backup workflows with btrfs snapshots.


Credit: