Vault Migration: File to Raft Storage
Step-by-step guide to migrate Vault from file storage to raft storage using
vault operator migrate.
Overview
The vault operator migrate command converts data in-place from file to raft format, preserving
everything:
- KV secrets
- PKI root CA keys
- Auth methods
- Policies
1. Backup Data
Stop Vault and backup the PVC data (guarantees consistency):
# Scale down vault to stop writes
kubectl --context=grigri -n vault scale statefulset vault --replicas=0
# Wait for pod to terminate
kubectl --context=grigri -n vault wait --for=delete pod/vault-0 --timeout=120s
# Create backup pod mounting the PVC
cat <<EOF | kubectl --context=grigri apply -f -
apiVersion: v1
kind: Pod
metadata:
name: vault-backup
namespace: vault
spec:
containers:
- name: backup
image: busybox
args: [sleep, "1000000"]
volumeMounts:
- name: vault-data
mountPath: /data-source
volumes:
- name: vault-data
persistentVolumeClaim:
claimName: vault-file-vault-0
EOF
# Wait for backup pod
kubectl --context=grigri -n vault wait --for=condition=ready pod/vault-backup --timeout=60s
# Create backup tarball
kubectl --context=grigri -n vault exec vault-backup -- tar czf /tmp/vault-backup.tar.gz -C /data-source .
# Copy backup to local machine
kubectl --context=grigri -n vault cp vault-backup:/tmp/vault-backup.tar.gz ./vault-backup.tar.gz
# Cleanup backup pod
kubectl --context=grigri -n vault delete pod vault-backup
2. Migrate Data Format
Start Vault temporarily to run the migration:
# Scale vault back up
kubectl --context=grigri -n vault scale statefulset vault --replicas=1
kubectl --context=grigri -n vault wait --for=condition=ready pod/vault-0 --timeout=120s
# Get root token
TOKEN=$(kubectl --context=grigri -n vault get secret vault-unseal-keys -o jsonpath='{.data.vault-root}' | base64 -d)
# Create migration config and run migration
kubectl --context=grigri -n vault exec vault-0 -- sh -c "
cat > /tmp/migrate.hcl << 'EOF'
storage_source \"file\" {
path = \"/vault/file\"
}
storage_destination \"raft\" {
path = \"/vault/file\"
node_id = \"vault-0\"
}
cluster_addr = \"https://vault.vault:8201\"
EOF
VAULT_SKIP_VERIFY=true VAULT_TOKEN=$TOKEN \
vault operator migrate -config=/tmp/migrate.hcl
"
Expected output:
Migration successfully completed!
3. Update Vault Configuration
Edit platform/vault/templates/vault.yaml:
config:
storage:
raft:
path: "${ .Env.VAULT_STORAGE_FILE }"
node_id: "vault-0"
listener:
tcp:
address: "0.0.0.0:8200"
tls_cert_file: /vault/tls/server.crt
tls_key_file: /vault/tls/server.key
cluster_address: "0.0.0.0:8201"
api_addr: "https://vault.vault:8200"
cluster_addr: "https://vault.vault:8201"
ui: true
Commit and push (ArgoCD will sync):
git add platform/vault/templates/vault.yaml
git commit -m "feat(vault): migrate from file to raft storage"
git push
4. Redeploy Vault
# Delete pod to pick up new config
kubectl --context=grigri -n vault delete pod vault-0
# Wait for vault to be ready
kubectl --context=grigri -n vault wait --for=condition=ready pod/vault-0 --timeout=120s
5. Verify Migration
TOKEN=$(kubectl --context=grigri -n vault get secret vault-unseal-keys -o jsonpath='{.data.vault-root}' | base64 -d)
kubectl --context=grigri -n vault exec vault-0 -- sh -c "
export VAULT_SKIP_VERIFY=true VAULT_TOKEN=$TOKEN
echo '=== Vault Status ==='
vault status
echo ''
echo '=== KV Secrets ==='
vault kv list secret/
echo ''
echo '=== PKI Mounts ==='
vault secrets list | grep pki
echo ''
echo '=== Raft Snapshot Test ==='
vault operator raft snapshot save /tmp/test.snap && echo 'Raft snapshot OK'
"
Expected status output:
Storage Type raft
HA Enabled true
Raft Committed Index XXX
Raft Applied Index XXX
ArgoCD Considerations
Vault modifies pod labels dynamically (vault-active, vault-sealed, etc.). ArgoCD may try to sync
these back. Add to your Application manifest:
spec:
ignoreDifferences:
- kind: Pod
name: vault-0
jsonPointers:
- /metadata/labels/vault-active
- /metadata/labels/vault-sealed
- /metadata/labels/vault-initialized
Quick Reference
# Get root token
kubectl --context=grigri -n vault get secret vault-unseal-keys -o jsonpath='{.data.vault-root}' | base64 -d
# Check Vault status
kubectl --context=grigri -n vault exec vault-0 -- vault status
# Create raft snapshot
kubectl --context=grigri -n vault exec vault-0 -- sh -c "
VAULT_SKIP_VERIFY=true VAULT_TOKEN=<token> \
vault operator raft snapshot save /vault/file/backup.snap
"
# Restore raft snapshot
kubectl --context=grigri -n vault exec vault-0 -- sh -c "
VAULT_SKIP_VERIFY=true VAULT_TOKEN=<token> \
vault operator raft snapshot restore /vault/file/backup.snap
"
# Force leader step-down (for HA)
kubectl --context=grigri -n vault exec vault-0 -- sh -c "
VAULT_SKIP_VERIFY=true VAULT_TOKEN=<token> vault operator step-down
"