A KMS key or a private key in .env
Audience: users who sign with a private key in .env, an environment variable or the Hardhat keystore, and want to know what a KMS key changes. Assumes you know what a private key and an RPC URL are; no cloud experience.
A private key in .env is a secret your process reads and signs with. A KMS key is a key your process never sees: the cloud signs on request, for whoever its access control allows, and the plugin checks each signature.
In Hardhat 3, a key reaches accounts through configVariable("…"), which reads an environment variable by default, or the Hardhat keystore when the config loads @nomicfoundation/hardhat-keystore. The keystore stores the value "encrypted in a file in your home directory" (Configuration variables, checked 2026-10-07). Hardhat itself does not load a .env file; a project that uses one loads it with a package such as dotenv. The rows below on a .env key hold for the keystore once Hardhat has decrypted the value into the process.
What each protects against
| Risk | Private key in .env or the keystore | KMS key with hardhat-kms |
|---|---|---|
| A dependency or script copies the key and sends it away (exfiltration) | The copy signs from anywhere, for as long as the key holds anything. The only fix is to move every fund and role to a new key. | There is no key to copy. Code in the process can still sign while it runs, and can read the cloud credentials in its environment (What the plugin does not protect against). Revoking those credentials stops it, and the key and its address stay. |
| The key or a credential ends up in a CI log | A printed private key is the key. Anyone who reads the log can sign. | A key id signs nothing on its own. A key id read from a configuration variable prints as the variable's name in the plugin's errors, debug output and task output (No secrets in errors, logs or task output). A leaked cloud credential signs until it expires or is revoked. A CI job that signs in with OpenID Connect (OIDC) gets short-lived credentials and stores no long-lived secret (Cloud credentials for KMS signing). |
| A laptop is lost or stolen | A .env file is plain text on the disk. A keystore file needs its password. | The laptop holds cloud credentials, not the key. Revoke them, or the user's access to the key, in the cloud console. |
| You need to know what was signed, and when | Nothing records a signature unless your own code does. | The cloud's audit log records each sign call; kms history lists them from CloudTrail, Cloud Audit Logs or the Key Vault audit log (kms history). |
| Who may sign | Whoever holds the file, the variable or the keystore password. | The cloud's IAM or RBAC, which you can grant per identity and take back without changing the key (AWS, Google Cloud, Azure). |
What neither protects against
A compromised build that signs a bad transaction. Any code in your Hardhat process, such as a script, a test, a plugin or a dependency, can send a request to the connection, and the account signs it, whether its key sits in .env or in a KMS. hardhat-kms refuses some requests for every caller, such as a bare 32-byte digest over RPC or typed data for another chain (Guarantees), but it cannot tell your transaction from one an attacker built. Run only code you trust in a process that can reach a key that holds funds.
Also out of reach of both: losing the key. A deleted KMS key cannot be recovered after its provider's waiting period (Prevent and recover from losing a key), and a .env key with no backup is gone with its file.
Cost and latency per signature
A .env key signs inside the Node.js process, at no charge. A KMS key costs a fee per call, and on AWS and Google Cloud a monthly fee per key. Each signature is a network request to the cloud.
| Cloud | Per key | Per sign call | Source, checked 2026-10-07 |
|---|---|---|---|
AWS KMS, ECC_SECG_P256K1 | $1 a month, prorated hourly | $0.15 per 10,000 asymmetric requests in us-east-1 | AWS KMS pricing and the AWS Price List API for AWSKMS in us-east-1 |
Google Cloud KMS, EC_SIGN_SECP256K1_SHA256 at level HSM | $0.003424658 an hour per key version, about $2.50 a month, for an account's first 2,000 versions | At most $0.15 per 10,000 operations | Cloud KMS pricing, prices effective 2025-03-17 |
Azure Key Vault, EC key on P-256K in a Standard vault | No monthly fee | $0.15 per 10,000 advanced key operations in East US | Azure Key Vault pricing and the Azure Retail Prices API for Key Vault in eastus |
The Azure row is for a Standard vault. An EC-HSM key in a Premium vault, or a key in a Managed HSM, carries its own fees on the same pricing page.
At $0.15 per 10,000, one signature costs $0.000015. The plugin also reads the key's public key when it first uses the key in a run, and again after it has closed the key's signer, 5 s after the last connection closes (How hardhat-kms works); that call is billed at the same rate. Prices vary by region and change over time. The first-deploy tutorials give each cloud's cost for a test key: AWS, Google Cloud, Azure.
This project has not measured signing latency. It depends on the cloud, the region and your network path to it. To see it for your key, run with DEBUG=hardhat:kms:signer, which prints the duration of each call to the KMS (Debug output). Each KMS call may take up to timeoutMs, 30 s by default (When a KMS call times out).
When to use which
- A
.envkey, or the keystore, is fine for local networks and for throwaway testnet keys that hold nothing you would miss: Hardhat's simulated network, a local node, a Sepolia key funded from a faucet. - A KMS key fits a key that holds funds or contract roles, a key that CI or a server signs with, or a key that several people's runs share. Access is granted and revoked per identity, and every sign call lands in the cloud's audit log.
- Both in one project: a network can list
accountsandkmsAccountstogether, for example a local test key next to a KMS deployer (Add local or Ledger accounts).
A key that a person approves on a device is a third option: A KMS key or a Ledger. For the plugin's guarantees in full, read the security model.