Introduction:
This article outlines the procedure for installing and upgrading the HashiCorp Vault using docker-compose
by disabling upgrade migration.
Prerequisites:
Docker and Docker Compose are installed on the host system.
Steps:
Installation Steps:
-
Create directory
vault-cluster
. Inside its structure:├── docker-compose.yml ├── license_txt ├── vault1 │ ├── config.hcl │ └── data/ ├── vault2 │ ├── config.hcl │ └── data/ ├── vault3 │ ├── config.hcl │ └── data/
-
Set the below ownership:
$ sudo chown -R 100:100 vault1/data vault2/data vault3/data
-
Create
docker-compose.yml
file:version: '3.8' services: vault1: image: hashicorp/vault-enterprise:1.19.3-ent #initial deploy version container_name: vault1 hostname: vault1 command: server cap_add: - IPC_LOCK user: root volumes: - ./vault1/config.hcl:/vault/config/config.hcl - ./vault1/data:/vault/data - /home/ubuntu/vault-cluster/license_txt:/vault/config/license.hclic ports: - "8200:8200" networks: - vault-net vault2: image: hashicorp/vault-enterprise:1.19.3-ent #initial deploy version container_name: vault2 hostname: vault2 command: server cap_add: - IPC_LOCK user: root volumes: - ./vault2/config.hcl:/vault/config/config.hcl - ./vault2/data:/vault/data - /home/ubuntu/vault-cluster/license_txt:/vault/config/license.hclic ports: - "8201:8200" networks: - vault-net vault3: image: hashicorp/vault-enterprise:1.19.3-ent #initial deploy version container_name: vault3 hostname: vault3 command: server cap_add: - IPC_LOCK user: root volumes: - ./vault3/config.hcl:/vault/config/config.hcl - ./vault3/data:/vault/data - /home/ubuntu/vault-cluster/license_txt:/vault/config/license.hclic ports: - "8202:8200" networks: - vault-net networks: vault-net: driver: bridge
-
Create
config.hcl
as shown below:ui = true listener "tcp" { address = "0.0.0.0:8200" tls_disable = true } storage "raft" { path = "/vault/data" node_id = "vault1" retry_join { leader_api_addr = "http://vault1:8200" } retry_join { leader_api_addr = "http://vault2:8200" } retry_join { leader_api_addr = "http://vault3:8200" } } api_addr = "http://vault1:8200" cluster_addr = "http://vault1:8201" License_path = "/vault/config/license.hclic"
Create the same file for 'vault2' and 'vault3'.
-
Start vault:
$ docker-compose up -d
-
Check the container:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 68df2128c658 hashicorp/vault-enterprise:1.18.2-ent "docker-entrypoint.s…" 17 minutes ago Up 17 minutes 0.0.0.0:8200->8200/tcp, :::8200->8200/tcp vault1 bb59af732be1 hashicorp/vault-enterprise:1.18.2-ent "docker-entrypoint.s…" 17 minutes ago Up 17 minutes 0.0.0.0:8202->8200/tcp, [::]:8202->8200/tcp vault3 9bb3fa691cc3 hashicorp/vault-enterprise:1.18.2-ent "docker-entrypoint.s…" 17 minutes ago Up 17 minutes 0.0.0.0:8201->8200/tcp, [::]:8201->8200/tcp vault2
-
Initialize and unseal the vault:
$ docker exec -it vault1 sh / # export VAULT_ADDR=http://127.0.0.1:8200 / # vault operator init -key-shares=1 -key-threshold=1 / # vault operator unseal <unseal-keys>
- Unseal all nodes using the same key and check the
raft
status:$ docker exec -it vault1 sh / # vault login <root-toke> / # vault operator raft list-peers Node Address State Voter ---- ------- ----- ----- vault1 vault1:8201 leader true vault2 vault2:8201 follower true vault3 vault3:8201 follower true
Upgradation Steps:
-
Set the
Disable Upgrade Migration = true
to prevent automated upgrade migrations. This is just a preventive approach to keep the 'autopilot' out of the picture since this is a manual upgrade procedure.$ docker exec -it vault1 sh $ vault operator raft autopilot set-config -disable-upgrade-migration=true
-
Perform the upgrade with one node at a time, starting from
non-leader
node. Remove the selected node from the HA cluster.$ docker exec -it vault3 sh / # vault operator raft remove-peer vault3 Peer removed successfully!
-
Stop the removed
non-leader
vault node :$ docker-compose stop vault3 $ docker rm <container-id>
-
Clean the data directory:
~/vault-cluster$ sudo rm -rf vault2/data/*
-
Update
docker-compose.yml
to use the new version:services: vault3: image: hashicorp/vault:<new-version> container_name: vault1 ...
-
Start the vault with the new version
$ docker-compose up -d vault3 $ docker exec -it vault3 sh / # export VAULT_ADDR=http://127.0.0.1:8200 / # vault status Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 1 Threshold 1 Unseal Progress 0/1 Unseal Nonce n/a Version 1.19.4+ent Build Date 2025-05-14T15:37:52Z Storage Type raft Removed From Cluster false HA Enabled true
-
Unseal the upgraded node with the unseal keys of the leader node, and the node is added back to the cluster with the upgraded version. Repeat the same steps for all
non-leader
nodes./ # vault operator raft list-peers Node Address State Voter ---- ------- ----- ----- vault1 vault1:8201 leader true vault3 vault3:8201 follower true vault2 vault2:8201 follower true / # / # vault operator members Host Name API Address Cluster Address Active Node Version Upgrade Version Redundancy Zone Last Echo --------- ----------- --------------- ----------- ------- --------------- --------------- --------- vault1 http://vault1:8200 https://vault1:8201 true 1.19.3 1.19.3 n/a n/a vault2 http://vault2:8200 https://vault2:8201 false 1.19.3 1.19.3 n/a 2025-06-05T15:20:41Z vault3 http://vault3:8200 https://vault3:8201 false 1.19.4 1.19.4 n/a 2025-06-05T15:20:42Z
-
Once all
non-leader
nodes are upgraded, step down the leader node to allow a new leader election.$ docker exec -it vault1 sh / # export VAULT_ADDR=http://127.0.0.1:8200 / # vault operator step-down Success! Stepped down: http://127.0.0.1:8200 / # vault operator raft list-peers Node Address State Voter ---- ------- ----- ----- vault1 vault1:8201 follower true vault3 vault3:8201 leader true ----> new leader elected vault2 vault2:8201 follower true
- Once a new leader is elected, apply the same upgrade steps to the final node as were performed on the other non-leader nodes.
References:
https://docs.docker.com/engine/install/ubuntu/