Skip to content

How I encrypt my disks on Linux, with an explanation of PBKDF2 and Argon2

I invested some time to understand the command cryptsetup luksFormat that is used for disk encryption on Linux and to figure out a secure choice of parameters that work best for my needs and the hardware I have. Within I present to you my learnings about encryption (Key Derivation Functions, KDF for short), my hardware-specific recommendations (I use a AMD Ryzen 5 5600G CPU and 32 GiB of DDR4 RAM), and how you need to adjust the parameters I chose for myself if you have less RAM than I use.

Introduction#

To be fleshed out. Stay tuned.

The actual command#

Let's say you want to encrypt the second partition on the one/only SSD (named sda) in your computer. That would be your root partition if you have only two partitions on your SSD: The first partition on a consumer's computer that isn't ancient is the ESP (EFI System Partition) that would have the kernel name descriptor /dev/sda1 in this scenario. The second partition that would contain your root filesystem to be mounted to / would have the kernel name descriptor /dev/sda2 in this scenario. I recommend using UUIDs or partition labels rather than kernel name descriptors (names like /dev/sda1) for a more robust/persistent naming methodology, but to not distract from the point of this post, I'll stick with the familiar /dev/sda1, /dev/sda2, … naming method.

The parametrized cryptsetup luksFormat command I myself use on my hardware (a AMD Ryzen 5 5600G CPU and 32 GiB of DDR4 RAM) is the following, and I'll explain each parameter and why your hardware matters:

cryptsetup luksFormat \
    --verify-passphrase \
    --type luks2 \
    --sector-size 4096 \
    --cipher aes-xts-plain64 \
    --key-size 512 \
    --pbkdf argon2id \
    --hash sha512 \
    --iter-time 3000 \
    --pbkdf-parallel 4 \
    --pbkdf-memory 4194304 \
    /dev/sda2

The backslash (\) at the end of each line (except the final line) is just a line-break to made a long command more readable.

Explanations of the parameters#

Some of these parameters are the default choice for their option flag. I'll mention when I deviated from the default and why, and when I stated the default explicitly simply for clarity and avoiding unexpected results.

Let's start with the general options and the options for the actual encryption algorithm (which will be AES):

  • You can choose between LUKS1 and LUKS2 encryption. LUKS2 should be the default. I mentioned it explicitly just for clarity. Some of the other options become available only if you use LUKS2.
  • --verify-passphrase also is the default. Specifying it means that when you provide your password, you want to be prompted to enter it a second time to notice when you made a typing mistake to not be unable to decrypt your disk later on. Before you are asked to provide your password, you are warned that the command cryptsetup luksFormat will throw away the encryption/decryption keys (more precisely, the LUKS header) of a previous disk encryption, which is for all our intents and purposes equivalent to wiping the partition. You will be asked to type in YES to confirm that you are aware of this. If you know what you're doing, you can skip that prompt (similar to --quiet) with --batch-mode, but that would also skip the opportunity to confirm your password. The combination of --batch-mode and --verify-passphrase lets you avoid having to type YES but still lets you enter your password twice. That's a use case for explicitly stating --verify-passphrase even though it's the default. Out of habit I always provide --verify-passphrase, since I never enter these commands by hand but copy and paste them from my notes anyway.
  • The sector size: Maybe you know that SSDs have a sector size of either 512e (512 emulated) or 4Kn (4K native). The parameter --sector-size doesn't need to be provided because the cryptsetup luksFormat command is smart enough to use the sector size the SSD itself advertises itself to use. Some SSDs, especially NVMe SSDs, are unreliable in that regard though and advertise themselves to be 512e (for backwards compatibility) even when they're 4Kn. Ensuring that the correct value is used for the sector size increases the read/write performance of your disk. Be warned though: Specifying 4096 when that's not the correct sector size of your disk can lead to data loss. If in doubt, omit this option. It's available only for LUKS2, not for LUKS1.
  • The encryption cipher (with its mode of operation): aes-xts-plain64 is the default. I state it explicitly simply for clarity. We use the Advanced Encryption Standard (AES) for the encryption cipher and XTS for its mode of operation. AES is secure and widely supported in hardware, unlike alternative ciphers. Choosing AES for the cipher therefore doesn't lower the input/output speeds relative to unencrypted disks as much as alternative encryption ciphers.
  • The key size: The thing about the mode XTS is that it uses two keys, not just one. It splits the bits you specify between two keys, thereby halving the specified number of bits. If you want AES-256 encryption, you have to specify 512 bits for the key size. The default key size is 256 bits though, which would result in the less secure AES-128 encryption due to using the mode XTS.

Now let's look at the parameters for the key derivation function (KDF) whose purpose is to protect you against brute-force cracking of your password by someone who obtained physical access to your disks, even when the password you provided has low entropy. Even a three-word password separated by a fixed separator, such as horse-battery-staple, has relatively low entropy. Therefore KDFs are crucial.

  • The option flag is unfortunately named --pbkdf, not --kdf. The KDF named PBKDF (Password-Based Key Derivation Function) from the year 2000 is just one possible KDF and there was later an improved version of it: PBKDF2. A much better and more modern KDF (from the year 2015) is Argon2 of which there are two variants: argon2i and argon2id. This option --pbkdf allows you to choose between the three KDFs pbkdf2, argon2i, and argon2id. Definitely choose argon2id, it's the best of these three options (unless you have a highly specific use case that would justify argon2i).
  • The --hash option: Internally, Argon2 uses a hash function. By default, only 256 bits are used for the hash function. That should be secure enough, but if you want the most secure encryption, you should prefer 512 bits. The boot manager GRUB can be a bit slow and apparently doesn't handle 512-bit hashes well, so choosing the higher value could mean longer waiting times when decrypting your encrypted disks. You have to decide for yourself if you want to compromise on security for a smoother user experience or if a small delay is a price worth to pay for better security.
  • The option --iter-time is the number of milliseconds you can tolerate to wait for your encrypted disk to get decrypted. It's used only once, to generate the "master key" (or "volume key" as cryptsetup calls it). To generate the "master key" with Argon2, you need three parameters: the memory cost, the amount of parallelism, and the number of iterations. (PBKDF2 has only one parameter: the number of iterations. The memory cost and parallelism don't apply to PBKDF2. Many guides are wrong in that regard and show you completely inappropriate values which then result in subpar encryption. But we don't use PBKDF2, we use Argon2.) The interplay between these three parameters is an optimization problem. You want to maximize all three numbers such that you still are below the number of (milli)seconds you're willing to wait for decryption. To solve that optimization problem, you state only two of the three parameters (the memory cost and parallelism) and then let cryptsetup figure out the number of iterations such that you stay under your tolerated waiting time. This is highly dependent on your hardware: a fast CPU can compute many more iterations during the same tolerated time than a slow CPU. Once you found the optimal number of iterations for your specific hardware and can generate the "master key" from your three parameters, the tolerated waiting time (--iter-time) is discarded. What the KDF does is compute the (high-entropy) "master key" from your (low-entropy) password. It uses the three parameters for that, which are now set in stone. If you were to move your encrypted disk into another computer with a much slower CPU, decryption would take much longer on that slower computer. The slow CPU will use the same configuration that your fast CPU could handle within the tolerated waiting time, and since it's much slower it won't be able to stay under the tolerated time anymore. If you plan to move encrypted disks between different machines a lot, you should therefore encrypt them on your slowest computer. The recommended absolute minimum (and sensible default) for --iter-time is 2000 milliseconds. That is, two seconds. Waiting for two seconds for your disk to decrypt is just the price to pay if you don't want people who gained physical access to your disk to be able to break into your computer by guessing your (probably low-entropy) password with brute force. For myself and my hardware I determined that three seconds (3000 milliseconds) allows for more security while still being tolerable. If you're impatient and cannot wait at all, choose 2000 milliseconds. If you want a bit more security, choose 3000 milliseconds. If you're paranoid and are willing to wait 5 seconds to decrypt your disk, choose 5000 milliseconds. I will come back to this setting later when I talk about how to use the built-in benchmark to adjust my command (with regard to the memory cost) for your own hardware. Keep in mind that GRUB can be slow. Just because you choose 2000 milliseconds here doesn't necessarily mean that you'll have to wait at most two seconds for your disk to be decrypted. Especially if you use 512 bits for the hash function, waiting times can be much longer than what you specify here.
  • The memory cost: The memory cost is the most important value for Argon2. Electricity is cheap in most countries, so it's much more feasible for an attacker to double his electricity bill than to double the amount of RAM in his computer. Increasing the memory cost (as Argon2 does) is thus a much better protection than increasing the number of iterations. PBKDF2 knows no other parameters other than iterations, it cannot do anything other than increase the number of iterations (and the electricity bill for the attacker) to provide better security. Where PBKDF2 tries to fit hundreds of thousands of hash computations into your tolerable waiting time, depending on how fast your CPU is, Argon2 can achieve much better security with merely four iterations due to relying on RAM costs instead. This is what so many guides out there get wrong. How high a value you can choose for --pbkdf-memory depends on how much RAM you have. The IRTF (Internet Research Task Force) recommends you choose 6 GiB for this option. That's not possible though if you have only 8 GiB (or perhaps even 16 GiB) of RAM in your computer. The security of your data is highly dependent on the amount of RAM you have. If you don't have enough RAM for a secure disk encryption, you cannot have a secure disk encryption with Argon2. If you have only 8 GiB of RAM, try 2 GiB for this option rather than 6 GiB which should still be plenty secure for a personal machine. The purpose of the KDF is to protect your data when someone stole or seized your disks and now is willing to spend thousands of dollars to crack your disk encryption. If you're a nobody and didn't commit a crime, that is likely not your threat model. On your business computer with valuable company data, you hopefully have more than 8 GiB of RAM. The maximum value that cryptsetup supports is 4 GiB anyway, so in 2026 it's not even possible to follow the recommendation of the IRTF. The IRTF would rather you bought more RAM though than you compromised on your security. Remember, we're trying to solve an optimization problem and if there's no solution that fits both your specified memory cost and tolerable waiting time, then cryptsetup will automatically lower the memory cost without you noticing it in order to find a solution, thus weakening your encryption. I'll come back to that later when I talk about benchmarking. I use a AMD Ryzen 5 5600G CPU and 32 GiB of DDR4 RAM. Through benchmarking I found out that I can use the maximum of 4 GiB for best possible security if I choose 3000 milliseconds for a tolerable waiting time. Had I used the default 2000 milliseconds, cryptsetup would have used a memory cost lower than 4 GiB. For the fun of it, I figured out how long I would have to wait for decryption if I wanted the recommended 6 GiB: It was 4500 milliseconds. That is, 4.5 seconds instead of 3 seconds. Since cryptsetup luksFormat currently (and for the foreseeable future) doesn't support memory costs higher than 4 GiB, it's not possible to actually execute the cryptsetup command with these values. You have to provide this value in KiB. To get from 1 KiB to 1 GiB you have to multiply by \(1024^2\). 4 GiB are therefore \(4 \cdot 1024^2\) or 4194304 KiB. 2 GiB are \(2 \cdot 1024^2\) or 2097152 KiB.
  • The parallelism: Here you specify how many CPU threads you want to use. Use the highest value possible for this option. You cannot use a value higher than the number of threads of your CPU. The highest value currently supported by cryptsetup is 4. Most modern CPUs should have four threads. So, as of 2026, always use 4 for the parameter --pbkdf-parallel.

Using the two parameters --pbkdf-memory (4194304 KiB if you have enough RAM, otherwise 2097152 KiB) and --pbkdf-parallel (always 4), cryptsetup will then internally perform a benchmark to find the highest possible number of iterations such that you remain under your tolerated waiting time. The minimum number of iterations for Argon2 is 4 though. (PBKDF2 will use hundreds of thousands of iterations because it tries to slow down brute-force guessing by increasing the electricity bill of an attacker rather than the money he needs to spend on RAM.) If your CPU is too slow for the values you specified, then cryptsetup will lower the crucial memory cost, thus weakening your disk's encryption. You can accept that or, if you can't, you can increase the amount of (milli)seconds you're willing to wait. To figure out the appropriate number of (milli)seconds you should provide using the option --iter-time, perform some benchmarks.

Benchmarking#

I'll again be explicit. In the following command, pay attention to (and vary) the two options --pbkdf-memory and --iter-time. I use this command as my starting point:

cryptsetup benchmark \
    --cipher aes-xts-plain64 \
    --key-size 512 \
    --pbkdf argon2id \
    --hash sha512 \
    --iter-time 3000 \
    --pbkdf-parallel 4 \
    --pbkdf-memory 4194304 \
    /dev/sda2

If the output of the given cryptsetup benchmark command contains the same value you specified for --pbkdf-memory, then you're good to go. For example, if you specified --pbkdf-memory 4194304 and the output contains 4194304, you're golden. Your CPU is fast enough to support these parameters, cryptsetup could solve the optimization problem under the specified constraints. It didn't have to weaken the constraints.

If the output of the previous command shows a value lower than what you provided for --pbkdf-memory though, then that means your CPU isn't fast enough to fit the mininum of 4 iterations (hash computations) into 3000 milliseconds. In that case, increase --iter-time until the previous command outputs the same memory cost that you specified with --pbkdf-memory, meaning cryptsetup didn't weaken the encryption. As I already mentioned, when I tried (just for the fun of it) how long I would have to wait, given the CPU I have, if I wanted to use the (for the foreseeable) unsupported memory cost of 6291456 KiB, I had to increment the --iter-time until I arrived at 4500 milliseconds.

Summary#

  1. Start by thinking about what would still be a tolerable user experience for you. Security is always a trade-off between convenience and protection. Choose something between 2000 and 5000 milliseconds for --iter-time.
  2. Choose the highest possible value for --pbkdf-parallel. The value is limited by the number of CPU threads you have or the maximum that cryptsetup currently supports (4 as of August 2026), whichever is lower.
  3. Find a high but still practical value for --pbkdf-memory. This is the most important parameter. The IRTF recommends 6 GiB, but cryptsetup supports only 4 GiB (as of August 2026). Use 2 GiB or buy more RAM if you don't have enough RAM for a secure encryption. Provide the value in KiB.
  4. Experiment with the command cryptsetup benchmark to figure out if your chosen values are feasible. If not, increase your tolerance threshold or accept a weaker encryption.

Final words#

Understanding Key Derivation Functions is valuable not just for disk encryption but also for password managers. I hope I could provide a clear understanding not just of how PBKDF2 and Argon2 work and what their purpose even is but also how you choose optimal parameters and fine-tune them to your specific requirements and hardware. Feel free to contact me. In any case, be well.