Install and configure Vault for secrets management with high availability

Intermediate 45 min Apr 01, 2026 589 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Set up HashiCorp Vault with Consul backend for production-grade secrets management. Configure clustering, authentication, policies, and security hardening for enterprise environments.

Prerequisites

  • Root or sudo access
  • At least 2GB RAM per server
  • Network connectivity between cluster nodes
  • Basic understanding of TLS certificates

What this solves

HashiCorp Vault provides centralized secrets management for applications and infrastructure. This tutorial sets up a production-ready Vault cluster with high availability using Consul as the storage backend, complete with authentication methods, policies, and security hardening.

Step-by-step installation

Add HashiCorp repository

Install the official HashiCorp repository to get the latest stable versions of Vault and Consul.

wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo

Install Vault and Consul

Install both Vault for secrets management and Consul for the highly available storage backend.

sudo apt install -y vault consul
sudo dnf install -y vault consul

Create system users and directories

Create dedicated users for Vault and Consul with minimal privileges and proper directory structure.

sudo useradd --system --home /etc/consul.d --shell /bin/false consul
sudo useradd --system --home /etc/vault.d --shell /bin/false vault
sudo mkdir -p /opt/consul/data /opt/vault/data /etc/consul.d /etc/vault.d
sudo chown -R consul:consul /opt/consul /etc/consul.d
sudo chown -R vault:vault /opt/vault /etc/vault.d
sudo chmod 750 /opt/consul /opt/vault /etc/consul.d /etc/vault.d

Generate TLS certificates

Create self-signed certificates for internal communication. Replace with proper CA certificates in production.

sudo mkdir -p /etc/vault.d/tls /etc/consul.d/tls
cd /tmp
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=US/ST=CA/L=SF/O=Example/CN=vault.example.com" -keyout vault-key.pem -out vault-cert.pem
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=US/ST=CA/L=SF/O=Example/CN=consul.example.com" -keyout consul-key.pem -out consul-cert.pem
sudo mv vault-*.pem /etc/vault.d/tls/
sudo mv consul-*.pem /etc/consul.d/tls/
sudo chown -R vault:vault /etc/vault.d/tls
sudo chown -R consul:consul /etc/consul.d/tls
sudo chmod 600 /etc/vault.d/tls/.pem /etc/consul.d/tls/.pem

Configure Consul cluster

Set up Consul as the storage backend with encryption and ACLs enabled for security.

datacenter = "dc1"
data_dir = "/opt/consul/data"
log_level = "INFO"
server = true
bootstrap_expect = 3
bind_addr = "0.0.0.0"
client_addr = "0.0.0.0"
retry_join = ["203.0.113.10", "203.0.113.11", "203.0.113.12"]
ui_config {
  enabled = true
}
connect {
  enabled = true
}
ports {
  grpc = 8502
  https = 8501
}
tls {
  defaults {
    ca_file = "/etc/consul.d/tls/consul-cert.pem"
    cert_file = "/etc/consul.d/tls/consul-cert.pem"
    key_file = "/etc/consul.d/tls/consul-key.pem"
    verify_incoming = false
    verify_outgoing = true
  }
  https {
    verify_incoming = false
  }
}
acl = {
  enabled = true
  default_policy = "deny"
  enable_token_persistence = true
}

Configure Vault with Consul backend

Create the main Vault configuration using Consul for storage and enabling the UI.

storage "consul" {
  address = "127.0.0.1:8500"
  path    = "vault/"
  scheme  = "https"
  tls_cert_file = "/etc/vault.d/tls/vault-cert.pem"
  tls_key_file  = "/etc/vault.d/tls/vault-key.pem"
  tls_ca_file   = "/etc/consul.d/tls/consul-cert.pem"
}

listener "tcp" {
  address     = "0.0.0.0:8200"
  tls_cert_file = "/etc/vault.d/tls/vault-cert.pem"
  tls_key_file  = "/etc/vault.d/tls/vault-key.pem"
  tls_min_version = "tls12"
}

api_addr = "https://203.0.113.10:8200"
cluster_addr = "https://203.0.113.10:8201"
ui = true
max_lease_ttl = "8760h"
default_lease_ttl = "8760h"
disable_mlock = false

telemetry {
  prometheus_retention_time = "24h"
  disable_hostname = true
}

Create systemd service files

Set up proper systemd services with security restrictions and dependency management.

[Unit]
Description=Consul
Documentation=https://www.consul.io/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/consul.d/consul.hcl

[Service]
Type=notify
User=consul
Group=consul
ExecStart=/usr/bin/consul agent -config-dir=/etc/consul.d/
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
LimitNOFILE=65536
LimitNPROC=32768
NoNewPrivileges=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
ReadWritePaths=/opt/consul/data

[Install]
WantedBy=multi-user.target
[Unit]
Description=HashiCorp Vault
Documentation=https://www.vaultproject.io/docs/
Requires=network-online.target
After=network-online.target consul.service
ConditionFileNotEmpty=/etc/vault.d/vault.hcl
StartLimitIntervalSec=60
StartLimitBurst=3

[Service]
Type=notify
User=vault
Group=vault
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
PrivateDevices=yes
SecureBits=keep-caps
AmbientCapabilities=CAP_IPC_LOCK
Capabilities=CAP_IPC_LOCK+ep
CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK
NoNewPrivileges=yes
ExecStart=/usr/bin/vault server -config=/etc/vault.d/vault.hcl
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
KillSignal=SIGINT
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
LimitNOFILE=65536
LimitMEMLOCK=infinity

[Install]
WantedBy=multi-user.target

Configure firewall rules

Open the necessary ports for Vault and Consul communication while maintaining security.

sudo ufw allow 8200/tcp comment "Vault API"
sudo ufw allow 8201/tcp comment "Vault cluster"
sudo ufw allow 8300/tcp comment "Consul server RPC"
sudo ufw allow 8301/tcp comment "Consul LAN serf"
sudo ufw allow 8302/tcp comment "Consul WAN serf"
sudo ufw allow 8500/tcp comment "Consul HTTP API"
sudo ufw allow 8501/tcp comment "Consul HTTPS API"
sudo ufw allow 8502/tcp comment "Consul gRPC"
sudo firewall-cmd --permanent --add-port=8200/tcp --add-port=8201/tcp
sudo firewall-cmd --permanent --add-port=8300/tcp --add-port=8301/tcp
sudo firewall-cmd --permanent --add-port=8302/tcp --add-port=8500/tcp
sudo firewall-cmd --permanent --add-port=8501/tcp --add-port=8502/tcp
sudo firewall-cmd --reload

Start and enable services

Start Consul first, then Vault, and enable both services to start automatically on boot.

sudo systemctl daemon-reload
sudo systemctl enable --now consul
sudo systemctl status consul
sleep 10
sudo systemctl enable --now vault
sudo systemctl status vault

Initialize Vault cluster

Initialize the Vault cluster and securely store the unseal keys and root token.

export VAULT_ADDR='https://127.0.0.1:8200'
export VAULT_CACERT='/etc/vault.d/tls/vault-cert.pem'
vault operator init -key-shares=5 -key-threshold=3 > /tmp/vault-init.txt
sudo mv /tmp/vault-init.txt /etc/vault.d/vault-init.txt
sudo chown vault:vault /etc/vault.d/vault-init.txt
sudo chmod 600 /etc/vault.d/vault-init.txt
Warning: Store the unseal keys and root token in a secure location immediately. Without these, you cannot access your Vault cluster.

Unseal Vault

Use three of the five unseal keys to unlock the Vault cluster for operation.

vault operator unseal $(grep 'Unseal Key 1:' /etc/vault.d/vault-init.txt | awk '{print $NF}')
vault operator unseal $(grep 'Unseal Key 2:' /etc/vault.d/vault-init.txt | awk '{print $NF}')
vault operator unseal $(grep 'Unseal Key 3:' /etc/vault.d/vault-init.txt | awk '{print $NF}')
vault status

Configure authentication and policies

Set up userpass authentication and create a basic policy for application access.

export VAULT_TOKEN=$(grep 'Initial Root Token:' /etc/vault.d/vault-init.txt | awk '{print $NF}')
vault auth enable userpass
vault policy write app-policy - << EOF
path "secret/data/myapp/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/metadata/myapp/*" {
  capabilities = ["list"]
}
EOF
vault write auth/userpass/users/appuser password="SecureP@ssw0rd123" policies="app-policy"

Enable secrets engines and audit logging

Enable the KV secrets engine and configure audit logging to a file for security compliance.

vault secrets enable -path=secret kv-v2
vault audit enable file file_path=/var/log/vault-audit.log
sudo touch /var/log/vault-audit.log
sudo chown vault:vault /var/log/vault-audit.log
sudo chmod 640 /var/log/vault-audit.log

Verify your setup

Test the Vault installation by checking cluster status, storing a secret, and retrieving it.

vault status
vault auth -method=userpass username=appuser password="SecureP@ssw0rd123"
vault kv put secret/myapp/database username="dbuser" password="dbpass123"
vault kv get secret/myapp/database
curl -k https://localhost:8200/v1/sys/health | jq
Note: The cluster is sealed by default after restart. You must unseal it with three keys each time the service restarts unless you configure auto-unsealing.

Security hardening

Configure log rotation

Set up log rotation for Vault audit logs to prevent disk space issues.

/var/log/vault-audit.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    postrotate
        /bin/systemctl reload vault
    endscript
    su vault vault
}

Enable monitoring integration

Configure Vault to export metrics for monitoring systems like Prometheus.

vault write sys/config/ui header_value="X-Custom-Header=vault-monitoring"
vault read sys/metrics?format=prometheus

Consider integrating with Grafana and Prometheus for comprehensive monitoring.

Backup and recovery procedures

Create backup script

Set up automated backups of Vault data through Consul snapshots.

#!/bin/bash
BACKUP_DIR="/opt/backups/vault"
DATE=$(date +%Y%m%d-%H%M%S)

mkdir -p $BACKUP_DIR
consul snapshot save $BACKUP_DIR/consul-backup-$DATE.snap

Keep only last 7 days of backups

find $BACKUP_DIR -name "consul-backup-*.snap" -mtime +7 -delete echo "Vault backup completed: $BACKUP_DIR/consul-backup-$DATE.snap"
sudo chmod +x /usr/local/bin/vault-backup.sh
sudo mkdir -p /opt/backups/vault
sudo chown consul:consul /opt/backups/vault

Schedule automated backups

Add a cron job to run backups daily during low-usage hours.

echo "0 2   * consul /usr/local/bin/vault-backup.sh" | sudo tee -a /etc/crontab

For production environments, consider using MinIO object storage for backup retention and HAProxy for load balancing multiple Vault instances.

Common issues

SymptomCauseFix
Vault sealed after restartNormal behavior - Vault seals on restartRun unseal process with 3 keys: vault operator unseal KEY
Permission denied errorsIncorrect file ownership or permissionsCheck ownership: sudo chown -R vault:vault /etc/vault.d /opt/vault
TLS certificate errorsSelf-signed certificates not trustedUse -tls-skip-verify flag or add VAULT_SKIP_VERIFY=true
Consul connection refusedConsul not running or wrong portCheck Consul status: sudo systemctl status consul
High availability not workingConsul cluster not formed properlyVerify all nodes in cluster: consul members
Memory lock failuresInsufficient privileges for mlockCheck systemd capabilities and LimitMEMLOCK=infinity

Next steps

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle infrastructure security hardening for businesses that depend on uptime. From initial setup to ongoing operations.