Eliminate the manual unseal that's an operational nightmare. We'll build auto-unseal with AWS KMS, GCP Cloud KMS, and Azure Key Vault, complete with a security vs practicality comparison and IAM configuration.

After building the Vault HA Cluster with Raft Integrated Storage in episode 20 — 3 nodes that replicate data to each other and automatically elect a new leader when a node dies — this episode solves the problem we deliberately left hanging: manual unseal. You already felt it yourself in practice: every node restart, someone must be present to type the Unseal Keys one by one. In the lab, this feels like an interesting ritual. In production, it's a race against time that can take the business down.
Imagine a real scenario: at 3:00 AM, a security patch requires rebooting all servers including your three Vault nodes. After the reboot completes, the whole cluster is back to sealed. Applications trying to pull dynamic database credentials start failing, then cascade to the entire system. The only savior is an operator with the Unseal Keys — and they're asleep. This is why auto-unseal isn't just convenience, it's part of availability engineering.
This episode covers how the auto-unseal mechanism works (envelope encryption), how to configure it on the three major cloud providers (AWS KMS, GCP Cloud KMS, Azure Key Vault), how its security compares to manual unseal, and the traps you must watch out for — because a wrong KMS configuration can leave Vault locked forever.
From episode 3, we know that when Vault is initialized, the data encryption key (which protects all secrets) is split into several parts using Shamir's Secret Sharing Algorithm. This key is called the root key. Vault stores this root key in encrypted form — and to decrypt it, at least a threshold (e.g. 3 of 5) of Unseal Keys is needed. Until unsealed, Vault refuses all operations: it can't read secrets, can't create tokens, can't issue certificates. In short, sealed = the system is completely dead.
The problem: who holds the Unseal Keys? If stored by many people (good separation of duties), then gathering them is even harder in an emergency. If stored by just one person, security drops drastically. There's an eternal tension between security and availability. Auto-unseal offers an elegant way out.
Auto-unseal doesn't eliminate the root key concept — it moves its custodian. The concept is known as envelope encryption:
unsealed — automatically, without humans.A real-world analogy: manual unseal is like a safe that can only be opened by a combination split into parts held by different people — secure, but troublesome. Auto-unseal is like a safe that opens itself after verifying its owner's fingerprint with a trusted third party (KMS). Secure, fast, and automatic.
What needs emphasis: KMS credentials and IAM roles actually become the primary target for attackers — because whoever can call KMS to decrypt the wrapped key can open Vault's seal. Therefore, auto-unseal's security depends heavily on the quality of access control to KMS.
The first step is setting up a Customer Master Key (CMK) in AWS KMS and granting access to the Vault instance. The safest way is giving Vault access via an IAM Role attached to the EC2 instance (not a static access key in the config file).
First, create an IAM policy that only allows KMS encrypt/decrypt operations on one specific key:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey",
"kms:ReEncryptFrom",
"kms:ReEncryptTo"
],
"Resource": "arn:aws:kms:ap-southeast-1:123456789012:key/1234abcd-5678-90ef-ghij-klmnopqrstuv"
}
]
}The least privilege principle: this policy only grants access to one KMS key, not all keys in the account. Once the role is attached to the instance, the Vault configuration only needs to add the seal "awskms" block:
seal "awskms" {
region = "ap-southeast-1"
kms_key_id = "1234abcd-5678-90ef-ghij-klmnopqrstuv"
endpoint = "https://kms.ap-southeast-1.amazonaws.com"
}
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-node-1"
retry_join {
leader_api_addr = "https://10.0.0.11:8200"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = true
}Tip
Vault automatically fetches AWS credentials from the Instance Profile / metadata service. No need to put access keys in the Vault config — which would just create new secrets to manage. This aligns with episode 19's lesson on avoiding static credentials.
In Google Cloud, the flow is similar: create a Crypto Key in Cloud KMS, grant access to the Service Account running the VM, then configure Vault using seal "gcpckms". Here are the key setup steps:
gcloud services enable cloudkms.googleapis.com
# Create the key ring and crypto key
gcloud kms keyrings create vault-keyring --location global
gcloud kms keys create vault-unseal \
--location global \
--keyring vault-keyring \
--purpose encryption
# Create a service account for the Vault instance
gcloud iam service-accounts create vault-sa \
--display-name "Vault Auto-Unseal"
# Grant encrypt/decrypt permission on that crypto key
gcloud kms keys add-iam-policy-binding vault-unseal \
--location global \
--keyring vault-keyring \
--member "serviceAccount:vault-sa@PROJECT.iam.gserviceaccount.com" \
--role "roles/cloudkms.cryptoKeyEncrypterDecrypter"Note the role used: cloudkms.cryptoKeyEncrypterDecrypter — minimal, only covering encryption and decryption. Never give a broader role like cloudkms.admin to an application service account.
Then the Vault configuration:
seal "gcpckms" {
project = "my-gcp-project"
region = "global"
key_ring = "vault-keyring"
crypto_key = "vault-unseal"
}
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-node-1"
retry_join {
leader_api_addr = "https://10.0.0.11:8200"
}
}Same as AWS, Vault reads GCP credentials from the VM's metadata server / default application credentials — not from a key file stored on disk. Make sure the VM runs the correct service account, for example when creating the instance:
gcloud compute instances create vault-node-1 \
--service-account vault-sa@PROJECT.iam.gserviceaccount.com \
--scopes https://www.googleapis.com/auth/cloudkmsIn Azure, the flow is: create a Key Vault and a Key inside it, grant access to a managed identity or service principal, then configure Vault:
seal "azurekeyvault" {
tenant_id = "11111111-2222-3333-4444-555555555555"
client_id = "66666666-7777-8888-9999-000000000000"
client_secret = "REDACTED"
vault_name = "my-vault-kv"
key_name = "vault-unseal"
environment = "AZUREPUBLICCLOUD"
}
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-node-1"
retry_join {
leader_api_addr = "https://10.0.0.11:8200"
}
}This configuration uses a service principal with client_id and client_secret. For a more secure environment, Azure provides managed identity — Vault can configure authentication via managed identity by omitting client_id/client_secret and only providing tenant_id plus the client_id of the managed identity. This service principal must have Encrypt, Decrypt, Unwrap Key, and Wrap Key permissions on that key (the Key Vault Crypto Officer role, or at minimum Key Vault Crypto User for decryption).
Warning
Note that client_secret is written in the config file — this is a secret that must be protected as tightly as the old Unseal Keys. Make sure the /etc/vault.d/server.hcl file permission is readable only by the vault user (e.g. chmod 600), or use a managed identity so there's no static secret at all.
The most visible change when using auto-unseal is in the initialization process. Note that Vault no longer issues Unseal Keys, but Recovery Keys:
export VAULT_ADDR=https://10.0.0.11:8200
vault operator initUnseal Keys : (none) ← no more!
Recovery Keys : 5
Recovery Key 1 : abc...def
Recovery Key 2 : ...
Initial Root Token : hvs.REDACTEDRecovery Keys are not used for routine unseal — they're only used for manual recovery if access to KMS is lost, or for specific operations like vault operator generate-root. Because auto-unseal is active, Vault unseals itself without human intervention:
vault statusKey Value
--- -----
Sealed false
HA Enabled trueNote the line Sealed false — even though no human typed anything. This is the magic of auto-unseal: Vault decrypts the wrapped root key via KMS at boot. To re-init across all cluster nodes, just unseal with Recovery Keys once per node (using vault operator unseal with recovery keys still works), or more practically: since the root key is the same, after one node is unsealed, other nodes can open via vault operator raft join at the setup stage.
Both approaches have their place. Here's the honest comparison:
| Criterion | Manual Unseal (Shamir) | Auto-Unseal (Cloud KMS) |
|---|---|---|
| Availability | Depends on humans at reboot | Automatic within seconds at boot |
| Security at rest | Very strong (split, offline keys) | Depends on KMS & IAM security |
| Operations | Runbook + 24/7 human on-call | No human intervention |
| Server rotation/patching | Needs time coordination | Transparent |
| Attack surface | Physical key distribution | KMS credentials become a single target |
| Best for | Lab, air-gapped, special compliance | Cloud, Kubernetes, large scale |
Important
Auto-unseal is not a replacement for separation of powers. Recovery Keys must still be held separately by several people (e.g. 3 of 5) — never store them all in one place. Their function changes from "daily door key" to "emergency fire-extinguisher key."
The auto-unseal area is one of the most classic incident sources in the Vault production world:
| Mistake | Symptom | Solution |
|---|---|---|
| Misconfigured KMS IAM (wrong role / wrong region) | Vault fails to boot with error could not unwrap key | Check the IAM policy; make sure kms_key_id, region, and endpoint are consistent |
| KMS key deleted / disabled | Vault sealed permanently — cannot recover except with Recovery Keys | Enable KMS key deletion protection; audit who can delete keys |
client_secret/key file leaked | Attacker can unseal Vault | Use instance profile / managed identity; chmod 600 the config |
| Forgetting to store Recovery Keys | No recovery path when KMS has problems | Store Recovery Keys in a separate vault (password manager), e.g. 3 of 5 |
| Inconsistent KMS config across nodes | Some nodes sealed, some not | All nodes must use the same KMS (identical key & region) |
| Dependency on KMS = new SPOF | KMS down ⇒ Vault can't unseal after restart | Ensure the KMS provider's SLA, and have a backup manual unseal procedure |
The last point needs emphasis: auto-unseal moves the dependency to Cloud KMS. If KMS is down when Vault needs to reboot, Vault stays sealed — same as waiting for a human, only now waiting for the cloud provider. For production, consider a transit seal or a combination (auto-unseal as primary, recovery keys as the emergency manual procedure written in the runbook and drilled periodically).
Caution
The worst-case scenario you must drill: KMS accidentally deleted and Recovery Keys lost. No technology can save Vault from this condition — all data is lost. That's why recovery drills aren't just documentation: they must be practiced at least once every few months, like fire drills.
In this episode 21 we covered how to eliminate manual unseal from Vault operations using auto-unseal with three cloud providers: AWS KMS, GCP Cloud KMS, and Azure Key Vault. We understood the envelope encryption mechanism that wraps the root key with a KMS key, how to prepare IAM/roles with the least privilege principle, the behavior change at init (Unseal Keys become Recovery Keys), and the honest comparison between auto-unseal and manual. Equally important, we mapped the common mistakes that can leave Vault locked forever — and how to prevent them.
Now your Vault can wake itself up after a reboot and fail over quickly. But wait — a Vault that's alive and available turns out to be not enough. How do we know who accessed what secret, when, and from where? How do we meet audit compliance demands (PCI-DSS, SOC 2) and protect Vault from its own host? That's the next big topic: Audit Logging, Security Hardening & Compliance in episode 22. Keep your enthusiasm up!