HashiCorp Vault — Dev vs Production Mode
Vault can run in two modes: dev mode for learning and development, and production mode for real deployments. Understanding the differences is critical — dev mode shortcuts that make learning easy would be serious security risks in production.
Dev Server Mode
The dev server is a pre-configured, in-memory Vault instance designed for experimentation:
# Start Vault in dev mode
vault server -dev
# Output:
# ==> Vault server configuration:
# Api Address: http://127.0.0.1:8200
# Clu Address: https://127.0.0.1:8201
# Listener 1: tcp (addr: "127.0.0.1:8200", tls: "disabled")
# Storage: inmem
# Version: Vault v1.15.4
#
# WARNING! dev mode is enabled! In this mode, Vault runs entirely
# in-memory and starts unsealed with a single unseal key.
#
# The unseal key and root token are displayed below...
#
# Unseal Key: abc123...
# Root Token: hvs.roottoken123
Set up your shell to use the dev server:
# In a new terminal window
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='hvs.roottoken123'
# Verify connectivity
vault status
Dev Mode Characteristics
| Feature | Dev Mode |
|---|---|
| Storage | In-memory (all data lost on restart) |
| Seal status | Always unsealed |
| TLS | Disabled |
| Root token | Printed to stdout |
| KV engine | v2 enabled at secret/ by default |
| Unseal keys | Single key, printed to stdout |
| Initialization | Automatic |
| Listener | 127.0.0.1:8200 only |
⚠️ Never use dev mode in production. Data is lost on restart, there’s no TLS, and the root token is exposed.
Dev Mode with Custom Settings
You can customize the dev server:
# Use a specific root token (makes scripts reproducible)
vault server -dev -dev-root-token-id="my-root-token"
# Listen on all interfaces (for remote access in dev)
vault server -dev -dev-listen-address="0.0.0.0:8200"
# Use a specific data directory (persists across restarts)
vault server -dev -dev-root-token-id="my-root-token" \
-dev-listen-address="0.0.0.0:8200"
Production Mode
Production mode requires explicit configuration, manual initialization, and proper security:
# /etc/vault.d/vault.hcl — Production configuration
# Storage backend (Raft for HA)
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-1"
}
# HTTPS listener with TLS
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/opt/vault/tls/vault-cert.pem"
tls_key_file = "/opt/vault/tls/vault-key.pem"
}
# API address for client redirects
api_addr = "https://vault.example.com:8200"
cluster_addr = "https://vault-1.example.com:8201"
# UI enabled
ui = true
# Disable memory lock warning
disable_mlock = false
# Telemetry
telemetry {
prometheus_retention_time = "24h"
disable_hostname = true
}
Start in production mode:
# Start Vault with config file
vault server -config=/etc/vault.d/vault.hcl
# Initialize (first time only)
vault operator init -key-shares=5 -key-threshold=3
# Unseal (provide threshold number of keys)
vault operator unseal <key-1>
vault operator unseal <key-2>
vault operator unseal <key-3>
Production Mode Characteristics
| Feature | Production Mode |
|---|---|
| Storage | Persistent (Raft, Consul, etc.) |
| Seal status | Sealed on start — must be unsealed |
| TLS | Required (configure cert and key) |
| Root token | Generated during init — must be revoked after setup |
| KV engine | No engines enabled by default |
| Unseal keys | Shamir shares or auto-unseal via KMS |
| Initialization | Manual vault operator init required |
| Listener | Configurable, typically 0.0.0.0:8200 |
Side-by-Side Comparison
| Feature | Dev Mode | Production Mode |
|---|---|---|
| Purpose | Learning, testing, development | Real workloads |
| Data persistence | ❌ In-memory, lost on restart | ✅ Persistent storage |
| TLS | ❌ Disabled (HTTP) | ✅ Required (HTTPS) |
| Auto-initialized | ✅ Yes | ❌ Manual init required |
| Auto-unsealed | ✅ Yes | ❌ Manual unseal or auto-unseal |
| Root token | Displayed on stdout | Generated once, should be revoked |
| Default secrets engine | secret/ (KV v2) |
None — must enable manually |
| Audit devices | None | Required for compliance |
| HA support | ❌ No | ✅ Yes (Raft, Consul) |
From Dev to Production — Migration Checklist
When transitioning from dev to production, ensure:
✅ TLS configured with valid certificates
✅ Persistent storage backend (Raft or Consul)
✅ Auto-unseal configured (AWS KMS, Azure KV, etc.)
✅ At least 2 audit devices enabled
✅ Root token revoked after initial setup
✅ Policies created (no operations with root token)
✅ Auth methods enabled (AppRole, OIDC, etc.)
✅ Monitoring and alerting configured
✅ Backup procedures in place
✅ Memory lock enabled (disable_mlock = false)
Running Vault in Docker (Dev Mode)
For this tutorial, we’ll often use Docker for quick labs:
# Run Vault in dev mode with Docker
docker run --rm -d \
--name vault-dev \
-p 8200:8200 \
-e 'VAULT_DEV_ROOT_TOKEN_ID=my-root-token' \
-e 'VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200' \
hashicorp/vault:1.15
# Set environment variables
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='my-root-token'
# Verify
vault status
Docker Compose version:
# docker-compose.yml
services:
vault:
image: hashicorp/vault:1.15
container_name: vault-dev
ports:
- "8200:8200"
environment:
VAULT_DEV_ROOT_TOKEN_ID: my-root-token
VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
cap_add:
- IPC_LOCK
docker compose up -d
Quick Reference
| Command | Description |
|---|---|
vault server -dev |
Start dev server |
vault server -dev -dev-root-token-id="token" |
Dev with custom root token |
vault server -config=vault.hcl |
Start production server |
vault operator init |
Initialize production Vault |
vault operator unseal |
Provide unseal key share |
vault status |
Check server status and seal state |
docker run hashicorp/vault:1.15 |
Run Vault in Docker |
Next: Vault vs Alternatives