---
title: Password hashing
description: Learn about the password hashing algorithms used by SuperTokens and how to customize and calibrate them.
sidebar:
  order: 50
---

## Overview

**SuperTokens** supports two password hashing algorithms: `BCrypt` and `Argon2`.
Per current best practices, `Argon2` is the recommended algorithm.
However, **SuperTokens** uses `BCrypt` by default since `Argon2` requires custom settings that are specific to the hardware in which the core is running on.

### Hashing time

The key metric to aim for when hashing passwords is the amount of time each hash would take.
By default, **SuperTokens** has configured these algorithms to take 300 milliseconds per hash on a machine with 1 GB of `RAM` and 2 virtual `CPU` cores.

## Change the hashing algorithm

You can switch algorithms whenever you want.
The change affects only the new users that sign up.
Previous passwords undergo decryption using the original algorithm.
For example, if you hash a password with `BCrypt`, it verifies using `BCrypt` even if the core configuration changes to `Argon2`.

Instructions:

<DependentContent passive group="core-hosting">
<ContentOption title="Managed service" value="managed">
1. Go to the [SuperTokens SaaS dashboard](https://supertokens.com/dashboard) and select the relevant **Managed** deployment.
      2. Open **Configuration** and find the **Password Hashing Algorithm** setting.
      3. Change the algorithm. Configuration changes are saved automatically.
</ContentOption>
</DependentContent>

<CodeGroup group="core-hosting">
<Tab title="Managed service" value="managed">

</Tab>
<Tab title="Self-hosted with Docker" value="self-hosted-docker">
```bash
docker run \
    -p 3567:3567 \
    -e PASSWORD_HASHING_ALG=BCRYPT \
    -e BCRYPT_LOG_ROUNDS=11 \
    -d supertokens/supertokens-<db_name>
```
</Tab>
<Tab title="Self-hosted without Docker" value="self-hosted-binary">
```yaml
# You need to add the following to the config.yaml file.
# The file path can be found by running the "supertokens --help" command

password_hashing_alg: BCRYPT
bcrypt_log_rounds: 11
```
</Tab>
</CodeGroup>

## Hashing calibration

This information is relevant only for self hosted core instances.
The managed service instances have already calibrated the algorithms based on the hardware.

:::note[When you change the hashing settings make sure to run the calibration CLI command to find the right balance for your hardware.]
:::

<DependentContent passive group="password-hashing-algorithm">
<ContentOption title="Argon2" value="argon2">
#### Algorithm settings

| Name | Default | Description |
| --- | --- | --- |
| `password_hashing_alg` | - | This setting chooses which password hashing algorithm to use. For using Argon2, set this to `ARGON2`. |
| `argon2_iterations` | `1` | This controls how much `CPU` processing power the hashing process uses. The higher the value, the more processing power, and hence the more time each hash takes. |
| `argon2_memory_kb` | `87795` (85 MB) | The amount of memory (`RAM`) that each hash takes. The higher this is, the harder it becomes to crack hashes offline, and the longer the algorithm takes. |
| `argon2_parallelism` | `2` | This is the number of threads the algorithm uses during hashing. The higher this is, the harder it would be to crack passwords offline using multiple cores. Should be equal to the number of virtual cores (or twice the number of physical cores) available in the system. |
| `argon2_hashing_pool_size` | `1` | This is the maximum number of concurrent hashes that the core performs. A value of `1` means that the core does only one hash at one point in time, other requests for hashing queue up and wait for their turn. |

##### Example

If each hash takes 300 milliseconds, a value of `1` here would entail ~ a max of 3 hashes per second (1000 ms / 300 ms). A value of `2` here would entail a max of 6 hashes per second (1000 ms / 300 ms)*2.

Password hashing occurs during sign in, sign up, and password reset flows. Therefore, you can set this value according to the target time per hash and how many sign ups/in you expect per second.

#### Change the settings
</ContentOption>
<ContentOption title="Bcrypt" value="bcrypt">
#### Algorithm settings

| Name | Default | Description |
|------|--------------|-------------|
| `password_hashing_alg` | - | This setting chooses which password hashing algorithm to use. For using bcrypt, set this to `BCRYPT`. |
| `bcrypt_log_rounds` | `11` | The number of rounds to use for hashing is `2^bcrypt_log_rounds`. The higher this value, the more time hashing takes. |

#### Change settings
</ContentOption>
</DependentContent>

<CodeGroup group="password-hashing-algorithm">
<Tab title="Argon2" value="argon2">
<DependentContent group="docker" label="Deployment method">
<ContentOption title="With Docker" value="with-docker">
```bash
docker run \
    -p 3567:3567 \
    -e PASSWORD_HASHING_ALG=ARGON2 \
    -e ARGON2_ITERATIONS=1 \
    -e ARGON2_MEMORY_KB=87795 \
    -e ARGON2_PARALLELISM=2 \
    -e ARGON2_HASHING_POOL_SIZE=1 \
    -d supertokens/supertokens-<db_name>
```
</ContentOption>
<ContentOption title="Without Docker" value="without-docker">
```yaml
# You need to add the following to the config.yaml file.
# The file path can be found by running the "supertokens --help" command

password_hashing_alg: ARGON2
argon2_iterations: 1
argon2_memory_kb: 87795
argon2_parallelism: 2
argon2_hashing_pool_size: 1
```
</ContentOption>
</DependentContent>
</Tab>
<Tab title="Bcrypt" value="bcrypt">
<DependentContent group="docker" label="Deployment method">
<ContentOption title="With Docker" value="with-docker">
```bash
docker run \
    -p 3567:3567 \
    -e PASSWORD_HASHING_ALG=BCRYPT \
    -e BCRYPT_LOG_ROUNDS=11 \
    -d supertokens/supertokens-<db_name>
```
</ContentOption>
<ContentOption title="Without Docker" value="without-docker">
```yaml
# You need to add the following to the config.yaml file.
# The file path can be found by running the "supertokens --help" command

password_hashing_alg: BCRYPT
bcrypt_log_rounds: 11
```
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>

<DependentContent passive group="password-hashing-algorithm">
<ContentOption title="Argon2" value="argon2">
#### Calibrate to your hardware

To find the optimal setting for your hardware, you can run the `hashingCalibrate` command via the CLI. This command takes a few parameters:

- `--with_alg`:
    - The value of this should be `argon2`
    - Compulsory parameter
- `--with_time_per_hash_ms`:
    - This requires the target time per hash (in milliseconds).
    - The default value is `300`.
- `--with_argon2_hashing_pool_size`:
    - This affects how much memory the hashing process uses per hash.
    - The default value is `1`
- `--with_argon2_max_memory_mb`:
    - This is the maximum amount of memory (`RAM`) that the core should use for password hashing. The amount of memory per password hash is `with_argon2_max_memory_mb / with_argon2_hashing_pool_size`.
    - The default value is `1024`.
- `--with_argon2_parallelism`:
    - This is the number of threads that argon2 should use. The higher this is, the harder it becomes to crack passwords offline.
    - The default value is `2*number of cores`

Running the algorithm takes minutes.
</ContentOption>
<ContentOption title="Bcrypt" value="bcrypt">
#### Calibrate to your hardware

To find the optimal setting for your hardware, you can run the `hashingCalibrate` command via the CLI. This command takes a few parameters:

- `--with_alg`:
    - The value of this should be `bcrypt`.
    - Compulsory parameter
- `--with_time_per_hash_ms`:
    - This requires the target time per hash (in milliseconds).
    - The default value is `300`
</ContentOption>
</DependentContent>

<CodeGroup passive group="password-hashing-algorithm">
<Tab title="Argon2" value="argon2">
<DependentContent group="docker" label="Deployment method">
<ContentOption title="With Docker" value="with-docker">
```bash
docker run supertokens/supertokens-<db_name> supertokens hashingCalibrate --with_alg=argon2
```
</ContentOption>
<ContentOption title="Without Docker" value="without-docker">
```bash
supertokens hashingCalibrate --with_alg=argon2
```
</ContentOption>
</DependentContent>
</Tab>
<Tab title="Bcrypt" value="bcrypt">
<DependentContent group="docker" label="Deployment method">
<ContentOption title="With Docker" value="with-docker">
```bash
docker run supertokens/supertokens-<db_name> supertokens hashingCalibrate --with_alg=bcrypt
```
</ContentOption>
<ContentOption title="Without Docker" value="without-docker">
```bash
supertokens hashingCalibrate --with_alg=bcrypt
```
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>

The above produces an output like:

<CodeGroup passive group="password-hashing-algorithm">
<Tab title="Argon2" value="argon2">
```text
====Input Settings====

-> Target time per hash (--with_time_per_hash_ms): 300 MS
-> Number of max concurrent hashes (--with_argon2_hashing_pool_size): 1
-> Max amount of memory to consume across 1 concurrent hashes (--with_argon2_max_memory_mb): 1024 MB
-> Argon2 parallelism (--with_argon2_parallelism): 4


====Running algorithm====

Current argon2 settings
-> memory: 1024 MB
-> iterations: 1
Calculating average hashing time....
..................................................Took 574 MS per hash


Adjusting memory to reach target time.

Current argon2 settings
-> memory: 972 MB
-> iterations: 1
Calculating average hashing time....
..................................................Took 529 MS per hash


Adjusting memory to reach target time.

Current argon2 settings
-> memory: 924 MB
-> iterations: 1
Calculating average hashing time....
..................................................Took 494 MS per hash


<....Truncated....>


Adjusting memory to reach target time.

Current argon2 settings
-> memory: 367 MB
-> iterations: 2
Calculating average hashing time....
..................................................Took 319 MS per hash


Adjusting memory to reach target time.

Current argon2 settings
-> memory: 348 MB
-> iterations: 2
Calculating average hashing time....
..................................................Took 303 MS per hash


====Final values====
Average time per hash is: 303 MS

argon2_memory_kb: 357104 (348 MB)
argon2_iterations: 2
argon2_parallelism: 4
argon2_hashing_pool_size: 1

====================
You should use these as docker environment variables or put them in the config.yaml file in the SuperTokens installation directory.
```
</Tab>
<Tab title="Bcrypt" value="bcrypt">
```text
====Input Settings====
-> Target time per hash (--with_time_per_hash_ms): 300 MS


====Running algorithm====

Current log rounds: 11
..........Took 158 MS per hash

Incrementing log rounds and trying again...

Current log rounds: 12
..........Took 310 MS per hash

====Final values====
Average time per hash is: 310 MS

bcrypt_log_rounds: 12

====================
You should use this as a docker environment variable or put this in the config.yaml file in the SuperTokens installation directory.
```
</Tab>
</CodeGroup>

<DependentContent passive group="password-hashing-algorithm">
<ContentOption title="Argon2" value="argon2">
The contents of the `====Final values====` gives you the values of the parameters to provide to the core.

The algorithm starts with the highest amount of memory per hash (= `with_argon2_max_memory_mb/with_argon2_hashing_pool_size`) and `1` iteration.
It calculates the current average hashing time by simulating hashes concurrently (based on the value of `with_argon2_hashing_pool_size`). If the hashing time is greater than the target time, it reduces the memory by 5%. If it's less than the target time, it increases the number of iterations. The algorithm stops if the current time is within 10 milliseconds (higher or lower) of the target time.

:::info[debug]
If you see an output like:
</ContentOption>
<ContentOption title="Bcrypt" value="bcrypt">
The contents of the `====Final values====` gives you the values of the parameters to provide to the core.

The algorithm starts with the minimum recommended value (`11`), and increments it until the average time per hash is greater than the target time. The final value is then equal to the value that yields the closest time per hash as the target one.
</ContentOption>
</DependentContent>

<CodeGroup passive group="password-hashing-algorithm">
<Tab title="Argon2" value="argon2">
```bash
/usr/bin/supertokens: line 9: 15 Killed "${ST_INSTALL_LOC}"jre/bin/java -classpath "${ST_INSTALL_LOC}cli/*" io.supertokens.cli.Main false "${ST_INSTALL_LOC}" $@
```
</Tab>
</CodeGroup>

<DependentContent passive group="password-hashing-algorithm">
<ContentOption title="Argon2" value="argon2">
it means that the system doesn't have enough memory. Try to run the algorithm again with a lower memory value by passing `--with_argon2_max_memory_mb`
:::
</ContentOption>
</DependentContent>


## See also

<CardGroup cols={3}>
  <Card title="Customize the sign in form" href="/authentication/email-password/customize-the-sign-in-form" />
  <Card title="Customize the sign up form" href="/authentication/email-password/customize-the-sign-up-form" />
  <Card title="Hooks and overrides" href="/authentication/email-password/hooks-and-overrides" />
  <Card title="Password reset" href="/authentication/email-password/password-reset" />
</CardGroup>
