Infisical vs Doppler vs HashiCorp Vault 2026
TL;DR: Infisical is the open-source secrets manager — self-hosted or cloud, developer-friendly SDKs, automatic secret rotation, and native integrations with every CI/CD platform. Doppler is the cloud-first secrets platform — universal dashboard, environment syncing, and the simplest developer onboarding for teams managing secrets across multiple services. HashiCorp Vault is the enterprise secrets engine — dynamic secrets, encryption-as-a-service, PKI, and granular access policies for complex infrastructure. In 2026: Infisical for open-source with full control, Doppler for the fastest team onboarding and simplest workflow, Vault for enterprise infrastructure with dynamic secrets and encryption needs.
Key Takeaways
- Infisical: Open-source (MIT), self-hosted or cloud. Node.js/Python/Go/Java SDKs, secret versioning, automatic rotation, Kubernetes operator. Best for teams wanting open-source secrets management with modern DX
- Doppler: Cloud-only, team-focused. Universal dashboard, CLI syncing, 30+ integrations. Best for teams that need simple, reliable secret syncing across every environment and platform
- HashiCorp Vault: Open-source (BSL) + enterprise. Dynamic secrets, transit encryption, PKI certificates, identity-based access. Best for large organizations needing dynamic secrets, encryption-as-a-service, and fine-grained access control
Infisical — Open-Source Secrets Manager
Infisical gives you a self-hosted or cloud secrets manager with developer-friendly SDKs, automatic rotation, and end-to-end encryption.
SDK Integration
// @infisical/sdk — fetch secrets at runtime
import { InfisicalSDK } from "@infisical/sdk";
const infisical = new InfisicalSDK({
siteUrl: "https://secrets.yourcompany.com", // self-hosted
});
// Authenticate with machine identity (Universal Auth)
await infisical.auth().universalAuth.login({
clientId: process.env.INFISICAL_CLIENT_ID!,
clientSecret: process.env.INFISICAL_CLIENT_SECRET!,
});
// Fetch all secrets for an environment
const secrets = await infisical.secrets().listSecrets({
environment: "production",
projectId: "proj_abc123",
secretPath: "/",
});
for (const secret of secrets.secrets) {
console.log(`${secret.secretKey}: ${secret.secretValue}`);
}
// Fetch a single secret
const dbUrl = await infisical.secrets().getSecret({
environment: "production",
projectId: "proj_abc123",
secretPath: "/",
secretName: "DATABASE_URL",
});
console.log(dbUrl.secret.secretValue);
Secret Creation and Versioning
// Create a secret
await infisical.secrets().createSecret({
environment: "production",
projectId: "proj_abc123",
secretPath: "/payments",
secretName: "STRIPE_SECRET_KEY",
secretValue: "sk_live_...",
secretComment: "Production Stripe key — rotated quarterly",
});
// Update a secret (creates a new version)
await infisical.secrets().updateSecret({
environment: "production",
projectId: "proj_abc123",
secretPath: "/payments",
secretName: "STRIPE_SECRET_KEY",
secretValue: "sk_live_new_...",
});
// Secret versioning — automatic, query history via API
// Every update creates an immutable version
// Roll back via dashboard or API
Automatic Secret Rotation
// Configure automatic rotation via API
await fetch("https://secrets.yourcompany.com/api/v1/secret-rotations", {
method: "POST",
headers: {
Authorization: `Bearer ${INFISICAL_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
workspaceId: "proj_abc123",
secretPath: "/database",
environment: "production",
provider: "postgresql",
inputs: {
host: "db.yourcompany.com",
port: 5432,
database: "production",
username1: "app_user_a",
username2: "app_user_b",
ca: "-----BEGIN CERTIFICATE-----...",
},
interval: 86400, // Rotate every 24 hours
// Infisical rotates between two users for zero-downtime
}),
});
Kubernetes Operator
# InfisicalSecret CRD — sync secrets to K8s
apiVersion: secrets.infisical.com/v1alpha1
kind: InfisicalSecret
metadata:
name: app-secrets
namespace: production
spec:
hostAPI: https://secrets.yourcompany.com
authentication:
universalAuth:
credentialsRef:
secretName: infisical-machine-identity
secretNamespace: production
managedSecretReference:
secretName: app-env
secretNamespace: production
secretType: Opaque
projectId: proj_abc123
envSlug: production
secretsPath: /
resyncInterval: 60 # Re-sync every 60 seconds
---
# Use in deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
template:
spec:
containers:
- name: api
envFrom:
- secretRef:
name: app-env # Populated by InfisicalSecret
CLI for Local Development
# Install Infisical CLI
brew install infisical/get-cli/infisical
# Login and initialize project
infisical login
infisical init
# Run commands with injected secrets
infisical run -- npm start
# Run with specific environment
infisical run --env=staging -- npm start
# Export secrets to .env file (gitignored)
infisical export --env=development > .env
# Scan git history for leaked secrets
infisical scan git-history
Doppler — Cloud-First Secrets Platform
Doppler provides a universal secrets dashboard with automatic syncing to every platform — CLI, CI/CD, cloud providers, and container orchestrators.
CLI Integration
# Install Doppler CLI
brew install dopplerhq/cli/doppler
# Authenticate and configure project
doppler login
doppler setup # interactive project + config selection
# Run with secrets injected as environment variables
doppler run -- npm start
# Run with specific config (environment)
doppler run --config=staging -- npm start
# Access individual secrets
doppler secrets get DATABASE_URL --plain
# Set a secret
doppler secrets set STRIPE_KEY "sk_live_..."
# Set multiple secrets at once
doppler secrets set API_KEY="abc123" API_SECRET="xyz789"
# Download secrets as .env
doppler secrets download --no-file --format=env > .env
SDK Integration
// Doppler REST API — no SDK needed, simple fetch
async function getSecrets(project: string, config: string): Promise<Record<string, string>> {
const response = await fetch(
`https://api.doppler.com/v3/configs/config/secrets`,
{
headers: {
Authorization: `Bearer ${process.env.DOPPLER_TOKEN}`,
"Content-Type": "application/json",
},
method: "GET",
// Query params
}
);
const data = await response.json();
const secrets: Record<string, string> = {};
for (const [key, value] of Object.entries(data.secrets)) {
secrets[key] = (value as any).computed;
}
return secrets;
}
// Typically you don't need the API — Doppler injects via CLI
// doppler run -- node server.js
// All secrets available as process.env.SECRET_NAME
Dynamic Secrets and References
# Secret references — DRY across configs
# In "production" config, reference shared secrets:
doppler secrets set DATABASE_URL '${shared.DATABASE_HOST}/production'
# Doppler resolves references at runtime:
# shared.DATABASE_HOST = "postgres://user:pass@db.company.com"
# → DATABASE_URL = "postgres://user:pass@db.company.com/production"
# Cross-project references
doppler secrets set AUTH_KEY '${projects.auth-service.production.JWT_SECRET}'
CI/CD Integration
# GitHub Actions — inject secrets from Doppler
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Doppler CLI
uses: dopplerhq/cli-action@v3
- name: Deploy with secrets
run: doppler run -- ./deploy.sh
env:
DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }}
# Or fetch specific secrets
- name: Get secrets
id: secrets
run: |
echo "db_url=$(doppler secrets get DATABASE_URL --plain)" >> $GITHUB_OUTPUT
env:
DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }}
Kubernetes Integration
# Doppler Kubernetes Operator
apiVersion: secrets.doppler.com/v1alpha1
kind: DopplerSecret
metadata:
name: app-secrets
namespace: production
spec:
tokenSecret:
name: doppler-token
managedSecret:
name: app-env
namespace: production
type: Opaque
resyncSeconds: 60
---
# Automatic restart on secret change
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
annotations:
secrets.doppler.com/reload: "true" # Auto-restart on change
spec:
template:
spec:
containers:
- name: api
envFrom:
- secretRef:
name: app-env
Webhooks for Secret Changes
// Doppler fires webhooks when secrets change
app.post("/webhooks/doppler", (req, res) => {
const event = req.body;
if (event.type === "secrets.update") {
console.log(`Project: ${event.project}`);
console.log(`Config: ${event.config}`);
console.log(`Changed by: ${event.user.email}`);
console.log(`Changed secrets: ${event.changed_secrets.join(", ")}`);
// Trigger redeployment or cache invalidation
triggerRedeploy(event.config);
}
res.status(200).send("OK");
});
HashiCorp Vault — Enterprise Secrets Engine
HashiCorp Vault provides dynamic secrets, encryption-as-a-service, PKI, and identity-based access control for complex infrastructure.
Basic Secret Operations
// node-vault SDK
import vault from "node-vault";
const client = vault({
endpoint: "https://vault.yourcompany.com",
token: process.env.VAULT_TOKEN, // or use AppRole auth
});
// Write a secret (KV v2 engine)
await client.write("secret/data/api-service", {
data: {
database_url: "postgres://user:pass@db.company.com/production",
stripe_key: "sk_live_...",
jwt_secret: "super-secret-key",
},
});
// Read a secret
const result = await client.read("secret/data/api-service");
const secrets = result.data.data;
console.log(secrets.database_url);
// KV v2 — automatic versioning
const v1 = await client.read("secret/data/api-service", { version: 1 });
const latest = await client.read("secret/data/api-service");
console.log(`Current version: ${latest.data.metadata.version}`);
AppRole Authentication
// AppRole — machine-to-machine auth (no human token needed)
const client = vault({ endpoint: "https://vault.yourcompany.com" });
// Login with AppRole credentials
const loginResult = await client.approleLogin({
role_id: process.env.VAULT_ROLE_ID!,
secret_id: process.env.VAULT_SECRET_ID!,
});
client.token = loginResult.auth.client_token;
// Token auto-renews based on TTL
// Kubernetes auth — pods authenticate via service account
const k8sClient = vault({ endpoint: "https://vault.yourcompany.com" });
const jwt = await fs.readFile(
"/var/run/secrets/kubernetes.io/serviceaccount/token",
"utf8"
);
const k8sLogin = await k8sClient.kubernetesLogin({
role: "api-service",
jwt,
});
k8sClient.token = k8sLogin.auth.client_token;
Dynamic Database Credentials
// Vault generates temporary database credentials on demand
// Configure the database secrets engine (one-time setup via CLI):
// vault write database/config/postgres \
// plugin_name=postgresql-database-plugin \
// connection_url="postgresql://{{username}}:{{password}}@db:5432/app" \
// allowed_roles="api-service" \
// username="vault_admin" password="admin_password"
// vault write database/roles/api-service \
// db_name=postgres \
// creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
// default_ttl="1h" max_ttl="24h"
// Application requests dynamic credentials:
const creds = await client.read("database/creds/api-service");
console.log(`Username: ${creds.data.username}`); // v-approle-api-ser-abc123
console.log(`Password: ${creds.data.password}`); // auto-generated
console.log(`TTL: ${creds.lease_duration}s`); // 3600 (1 hour)
// Use dynamic credentials for database connection
const pool = new Pool({
host: "db.yourcompany.com",
database: "app",
user: creds.data.username,
password: creds.data.password,
});
// Credentials auto-expire — Vault revokes them after TTL
// Renew lease if needed:
await client.write(`sys/leases/renew`, {
lease_id: creds.lease_id,
increment: 3600,
});
Transit Encryption (Encryption-as-a-Service)
// Transit engine — encrypt/decrypt without exposing keys
// Keys never leave Vault — only ciphertext crosses the network
// Encrypt sensitive data
const encrypted = await client.write("transit/encrypt/user-data", {
plaintext: Buffer.from(JSON.stringify({
ssn: "123-45-6789",
credit_card: "4111111111111111",
})).toString("base64"),
});
const ciphertext = encrypted.data.ciphertext;
// vault:v1:8SDd3WHDOjf7mq69CyCqYjBXAiQQA...
// Store ciphertext in your database safely
// Decrypt when needed
const decrypted = await client.write("transit/decrypt/user-data", {
ciphertext,
});
const plaintext = JSON.parse(
Buffer.from(decrypted.data.plaintext, "base64").toString()
);
console.log(plaintext.ssn); // 123-45-6789
// Key rotation — transparent to application
// vault write -f transit/keys/user-data/rotate
// Old ciphertext still decryptable, new encryptions use latest key
// Rewrap existing ciphertext to use latest key version:
const rewrapped = await client.write("transit/rewrap/user-data", {
ciphertext: oldCiphertext,
});
ACL Policies
# Vault policy — fine-grained access control
# API service — read-only access to its own secrets
path "secret/data/api-service/*" {
capabilities = ["read", "list"]
}
# Dynamic database credentials
path "database/creds/api-service" {
capabilities = ["read"]
}
# Transit encryption — encrypt and decrypt only
path "transit/encrypt/user-data" {
capabilities = ["update"]
}
path "transit/decrypt/user-data" {
capabilities = ["update"]
}
# Deny access to other services' secrets
path "secret/data/payment-service/*" {
capabilities = ["deny"]
}
Vault Agent Sidecar (Kubernetes)
# Vault Agent injector — auto-inject secrets into pods
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "api-service"
vault.hashicorp.com/agent-inject-secret-db: "database/creds/api-service"
vault.hashicorp.com/agent-inject-template-db: |
{{- with secret "database/creds/api-service" -}}
postgresql://{{ .Data.username }}:{{ .Data.password }}@db:5432/app
{{- end }}
vault.hashicorp.com/agent-inject-secret-config: "secret/data/api-service"
vault.hashicorp.com/agent-inject-template-config: |
{{- with secret "secret/data/api-service" -}}
export STRIPE_KEY="{{ .Data.data.stripe_key }}"
export JWT_SECRET="{{ .Data.data.jwt_secret }}"
{{- end }}
spec:
serviceAccountName: api-service
containers:
- name: api
command: ["/bin/sh", "-c", "source /vault/secrets/config && node server.js"]
# Secrets written to /vault/secrets/db and /vault/secrets/config
Feature Comparison
| Feature | Infisical | Doppler | HashiCorp Vault |
|---|---|---|---|
| Deployment | Self-hosted or cloud | Cloud only | Self-hosted or cloud (HCP) |
| License | MIT (open-source) | Proprietary | BSL (source-available) |
| Secret Storage | Encrypted KV | Encrypted KV | Multiple engines (KV, transit, PKI, DB) |
| Dynamic Secrets | Rotation (DB) | No | Full (DB, AWS, GCP, Azure, PKI) |
| Encryption-as-a-Service | No | No | Transit engine |
| PKI/Certificates | No | No | Built-in CA |
| Secret Versioning | Yes | Yes (audit log) | Yes (KV v2) |
| Secret Rotation | Auto-rotation | Manual + webhooks | Dynamic (auto-expire) |
| CLI | infisical run | doppler run | vault CLI |
| SDK Languages | Node, Python, Go, Java, .NET | REST API (no SDK) | Node, Python, Go, Java, Ruby |
| K8s Operator | Yes (InfisicalSecret CRD) | Yes (DopplerSecret CRD) | Yes (Vault Agent Injector + CSI) |
| CI/CD Integrations | GitHub, GitLab, CircleCI, etc. | 30+ native integrations | GitHub, GitLab, Jenkins, etc. |
| Access Control | RBAC + environments | RBAC + environments | ACL policies + identity |
| E2E Encryption | Yes (client-side) | In transit + at rest | In transit + at rest |
| Complexity | Low-medium | Low | High |
| Ideal For | Dev teams, startups | All team sizes | Enterprise infrastructure |
Ecosystem and Community
Infisical has built strong momentum in the developer tools space. Their GitHub repository has rapidly accumulated stars as teams look for open-source alternatives to Doppler. The Infisical Discord is active, and the team ships fast — major features like the Kubernetes operator, automatic rotation, and the web dashboard have all landed within the last 18 months. The MIT license means enterprise teams can audit, fork, and customize without licensing concerns. The self-hosted deployment has excellent documentation with Docker Compose and Kubernetes Helm chart options. For managing the infrastructure where these secrets are deployed, see Caddy vs Traefik vs Nginx Proxy Manager reverse proxies 2026.
Doppler's community is polished and commercially-oriented. The documentation is first-class, the CLI is genuinely enjoyable to use, and the dashboard is the best-designed of the three. Doppler's 30+ native integrations — including Heroku, Fly.io, Railway, GitHub Actions, and all major cloud providers — mean that teams don't need to write custom sync scripts for common use cases. The Doppler support team is responsive, and there's a well-maintained changelog. The limitation is that Doppler is cloud-only: no self-hosting, no air-gapped deployment.
HashiCorp Vault's community is the most technically sophisticated of the three. The HashiCorp community forums (discuss.hashicorp.com) have thousands of Vault threads, and the HCP Vault (managed) service has reduced the operational burden for teams that want Vault's capabilities without running it themselves. The Vault GitHub repository has 31,000+ stars and an active issue tracker. HashiCorp's acquisition by IBM in 2024 changed the licensing from MPL to BSL, which made some open-source adopters cautious, but the product roadmap has continued without disruption.
Real-World Adoption
Infisical has become the default choice for developer-focused startups and YC-stage companies in 2026. The combination of open-source license, modern developer experience, and cloud hosting option hits the right balance for teams that want control without the operational complexity of running Vault. Companies in regulated industries with data sovereignty requirements particularly value the self-hosted option — a healthcare startup can run Infisical on their own AWS account in a specific region to satisfy HIPAA requirements.
Doppler dominates adoption in medium-sized product companies — typically Series A to Series C startups with 10-100 engineers. The workflow of doppler setup → doppler run -- npm start is as simple as secrets management gets. Teams that were previously managing environment variables with a shared .env file in a password manager, or using GitHub Secrets and manually syncing to staging/production, find Doppler's model dramatically simpler. The cross-project secret references eliminate the most common duplication pattern (shared database credentials, shared API keys used by multiple services). For authentication systems that often need secrets for their OAuth credentials, see better-auth vs Lucia vs NextAuth 2026.
HashiCorp Vault is the dominant solution in enterprises with more than 100 engineers, regulated industries (financial services, healthcare, government), and organizations running complex multi-cloud or hybrid infrastructure. Dynamic database credentials — where every application pod gets a unique, short-lived database user that Vault automatically revokes — is a security posture improvement that's difficult to replicate with static secret management. The transit encryption engine is used in financial institutions for storing sensitive data (PII, payment card data) with encryption that never requires the application to hold the raw key material.
Getting Started with Secrets Management
For teams currently using plain environment variables or .env files managed by hand, any of these three tools is a significant improvement. The migration path that works best for most teams:
Start with Doppler for local development and CI/CD. The CLI's doppler run -- your-command pattern is a direct replacement for dotenv -e .env your-command, and the dashboard gives you a single source of truth for all environments. Once the team is comfortable with the workflow, evaluate whether Infisical's self-hosted model or Vault's dynamic secrets would provide additional value.
For teams already on Kubernetes, the operator pattern (InfisicalSecret CRD or DopplerSecret CRD) is the cleanest deployment — secrets get synced to native Kubernetes Secrets, and pods reference them via envFrom without any application-level secrets client code.
When to Use Each
Choose Infisical if:
- You want open-source secrets management with MIT license
- Self-hosting for data residency or compliance is required
- You need automatic secret rotation for databases
- End-to-end encryption (client-side) is important
- You want SDKs in multiple languages for runtime secret fetching
Choose Doppler if:
- You want the simplest onboarding —
doppler setupand you're done - Your team manages secrets across many services and environments
- You need 30+ native integrations without custom code
- Secret references across projects save you from duplication
- You prefer cloud-managed with no infrastructure to maintain
Choose HashiCorp Vault if:
- You need dynamic secrets (auto-generated, auto-expired DB credentials)
- Encryption-as-a-service (transit engine) is a requirement
- You need PKI certificate management
- Fine-grained ACL policies with identity-based access control matter
- You're running complex infrastructure across multiple clouds
- You need secret engines beyond KV (AWS, GCP, Azure, SSH, TOTP)
Methodology
Feature comparison based on Infisical (self-hosted + cloud), Doppler, and HashiCorp Vault 1.x documentation as of March 2026. Code examples use official SDKs where available (Infisical SDK, node-vault) and REST API calls. Dynamic secrets and transit encryption are unique Vault capabilities not offered by Infisical or Doppler. Evaluated on: deployment flexibility, secret types, rotation capabilities, developer experience, Kubernetes integration, and access control granularity.
Security Architecture and Threat Models
The three platforms have meaningfully different approaches to the threat model for secrets at rest. Infisical's client-side end-to-end encryption means that even if Infisical's servers were compromised, the attacker would get encrypted blobs rather than plaintext secrets. The encryption keys are derived from your master password and never leave your client. This is a stronger security guarantee than Doppler, which encrypts secrets at rest on their servers but holds the encryption keys. The tradeoff is that Infisical cloud can't do server-side secret transformation or injection — the client must decrypt.
Doppler's architecture trusts Doppler's servers as the security boundary — they handle encryption and decryption, which enables features like server-side secret references and template resolution. For most teams, Doppler's security model is entirely appropriate. Doppler is SOC 2 Type II certified and undergoes regular penetration testing. The threat model they protect against is unauthorized access by external parties, not insider threats at Doppler itself.
Vault's threat model is the most sophisticated of the three. Its transit encryption engine (encryption-as-a-service) means that application code never handles plaintext encryption keys — only ciphertext crosses the wire. Dynamic database credentials mean that even if an application is compromised and an attacker reads its environment variables, the database credentials they find have already expired (typically 1-hour TTL). This defense-in-depth approach is what makes Vault the standard for financial services and healthcare organizations where a compromised credential means regulatory violation.
Practical Comparison: A Node.js Microservice
Consider a production Node.js microservice that needs: a Postgres database URL, a Stripe API key, and a JWT signing secret. Here's how each platform handles it at runtime.
With Doppler, the microservice runs as doppler run -- node server.js. Doppler injects the three secrets as environment variables before the process starts. The application reads process.env.DATABASE_URL, process.env.STRIPE_KEY, and process.env.JWT_SECRET as normal. Rotation requires redeploying the service — Doppler's Kubernetes operator handles this automatically with the reload annotation.
With Infisical, the microservice either uses the Infisical CLI (infisical run -- node server.js) for the same injection pattern, or uses the @infisical/sdk to fetch secrets at startup, before any handlers are registered. The SDK approach is more flexible for secrets that need to change at runtime without restart, but adds SDK initialization latency.
With Vault, a Kubernetes sidecar (Vault Agent) handles authentication and injects secrets into a shared volume that the main container reads. Dynamic database credentials mean the Postgres URL changes every hour, and Vault Agent automatically renews the lease and updates the file before expiry. The application reads credentials from a file rather than environment variables, which enables hot-reloading of credentials without process restart.
Cost Comparison
Doppler's pricing starts at $0/month for personal projects (one project, limited environments), $8/month per user for teams, and custom pricing for enterprise. For a team of five engineers managing secrets for three services across three environments, Doppler costs approximately $40/month. There's no per-secret cost.
Infisical Cloud pricing is similar to Doppler's team tier, starting at free for individuals and $8/user/month for teams. The self-hosted option is free — you pay only for infrastructure costs (a single VPS with 2GB RAM runs Infisical adequately for small teams). For teams where infrastructure cost is acceptable, Infisical self-hosted is effectively free.
Vault is free to run self-hosted (BSL license allows internal use). HCP Vault (managed) starts at approximately $0.03 per active client per hour for the dev cluster tier — roughly $22/month for 30 clients running 24/7. Enterprise Vault with FIPS compliance, HSM support, and advanced audit logging requires a HashiCorp Enterprise License, which is typically quoted per node and can run thousands of dollars per month for large deployments.
Related: Best Node.js Background Job Libraries 2026, Best JavaScript Testing Frameworks 2026, Best Monorepo Tools 2026