technical#devops#security#aws#vault#environment-variables#cloud#infisical#doppler

Choosing Your Secrets Manager: 10+ Tools Compared for Security and Scale

A comprehensive comparison of 10+ secrets management providers. From AWS Secrets Manager to Infisical, we break down pricing, security, DX, and when to use each. Plus: hybrid approaches and cost analysis for startups vs enterprises.

A

Ajit Kumar

Creator, evnx

·21 min read

If you've ever shipped a project with a .env file containing production API keys, you know the panic that follows. We've all been there—committing secrets to Git (even accidentally), emailing passwords to teammates, or wrestling with "works on my machine" issues.

Managing environment variables and secrets is one of those problems that seems trivial until it isn't. What starts as a simple text file quickly becomes a security nightmare as your team grows and your infrastructure scales.

In this guide, we'll compare 10+ popular solutions for managing secrets in the cloud, from AWS Secrets Manager to emerging players like Infisical and Doppler. We'll break down real costs, implementation examples, and help you choose the right tool for your stack.

The reality check

If you're still using .env files in production without encryption or access controls, stop reading and scroll to the "When to Migrate" section. Your future self will thank you.

The Problem: Why .env Files Don't Scale

Let's start with the basics. A .env file is a simple key-value store used to configure applications:

Bash
# .env
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
API_KEY=sk_live_abc123xyz789
JWT_SECRET=super_secret_key_do_not_share
REDIS_PASSWORD=redis_prod_password_2026

This works great for local development. But consider what happens when:

  • Multiple developers need access (do you Slack the file?)
  • Production deployments require different values (how do you sync?)
  • Security audits demand rotation policies (who's tracking this?)
  • Compliance requirements mandate encryption at rest (is your Git repo encrypted?)
Bash
# The moment you realize you've made a mistake
$ git push
Enumerating objects: 15, done.
Counting objects: 100% (15/15), done.
Delta compression using up to 8 threads
Compressing objects: 100% (8/8), done.
Writing objects: 100% (9/9), 1.23 KiB | 1.23 MiB/s, done.
Total 9 (delta 6), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (6/6), completed with 3 local objects.
To github.com:your-org/your-repo.git
   8a3f2b1..9c4e5d6  main -> main

# Three minutes later...
Email from GitHub: "AWS credentials detected in your repository"

Never commit .env files

Even in private repos, this creates a single point of failure and makes secret rotation nearly impossible. GitHub scans public repos in real-time; private repos are scanned too.

The Landscape: 10 Secrets Management Providers

Here's the complete comparison matrix:

ProviderTypePrice (Starting)Setup TimeBest For
AWS Secrets ManagerManaged Service$0.40/secret/mo15 minAWS-native teams
Azure Key VaultManaged Service$1/month + ops20 minAzure/Microsoft stack
GCP Secret ManagerManaged Service$0.06/secret/mo15 minGCP workloads
HashiCorp VaultSelf-hosted/SaaSFree / $30k/yr2-4 hoursSecurity-first orgs
DopplerSaaSFree / $25/mo5 minDeveloper experience
InfisicalOpen-source/SaaSFree / $10/mo10 minModern teams, GitOps
1Password SecretsSaaS$3/user/mo10 minTeams already using 1Password
CyberArk ConjurEnterpriseCustom1-2 weeksLarge enterprises, compliance
GitLab/GitHub SecretsBuilt-inIncluded5 minCI/CD-focused workflows
SOPS (Mozilla)CLI ToolFree30 minGitOps, encryption at rest

Quick decision guide

  • Solo dev or startup: Start with Infisical or Doppler (free tiers)
  • AWS-heavy: AWS Secrets Manager
  • Multi-cloud: Vault or Infisical
  • Enterprise compliance: CyberArk or Vault Enterprise
  • GitOps workflow: SOPS + Age/GPG

Deep Dive: The Top Contenders

1. AWS Secrets Manager

AWS Secrets Manager is the managed service option for teams already invested in the AWS ecosystem.

Key Features:

  • Automatic rotation with Lambda
  • Integration with RDS, Redshift, DocumentDB
  • Fine-grained IAM policies
  • Encryption using KMS

Implementation Example:

TypeScript
// lib/secrets.ts
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";

const client = new SecretsManagerClient({
  region: process.env.AWS_REGION || "us-east-1"
});

export async function getSecret(secretName: string): Promise<Record<string, string>> {
  const command = new GetSecretValueCommand({ SecretId: secretName });
  const response = await client.send(command);

  if (!response.SecretString) {
    throw new Error("Secret is empty or binary");
  }

  return JSON.parse(response.SecretString);
}

// Usage in your app
export async function getDatabaseConfig() {
  const secrets = await getSecret("prod/db/credentials");
  return {
    host: secrets.host,
    user: secrets.username,
    password: secrets.password,
    database: secrets.dbname
  };
}

Cost Breakdown:

  • $0.40 per secret per month
  • $0.05 per 10,000 API calls
  • Example: 50 secrets + 100k calls/month = $20 + $0.50 = $20.50/month

Pro tip

Use secret versioning to maintain history. AWS keeps up to 100 versions per secret, which is invaluable for audit trails and rollbacks.

2. Azure Key Vault

Azure Key Vault provides cloud-based secret management for Microsoft Azure workloads.

Key Features:

  • Hardware Security Modules (HSM) support
  • Integration with Azure Active Directory
  • Certificate management
  • Key management for encryption

Implementation Example:

TypeScript
// lib/azure-secrets.ts
import { DefaultAzureCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";

const credential = new DefaultAzureCredential();
const vaultName = process.env.AZURE_KEY_VAULT_NAME!;
const url = `https://${vaultName}.vault.azure.net`;

const client = new SecretClient(url, credential);

export async function getSecret(secretName: string): Promise<string> {
  const latestSecret = await client.getSecret(secretName);
  return latestSecret.value!;
}

export async function setSecret(secretName: string, value: string): Promise<void> {
  await client.setSecret(secretName, value);
}

// Usage
export async function getConfig() {
  return {
    databaseUrl: await getSecret("database-url"),
    apiKey: await getSecret("api-key"),
    jwtSecret: await getSecret("jwt-secret")
  };
}

Cost Breakdown:

  • Standard tier: $1/month per vault
  • Premium tier (HSM): $6/month per vault
  • Operations: $0.03 per 10,000 transactions
  • Example: 1 vault + 50k operations = $1 + $0.15 = $1.15/month

3. Google Cloud Secret Manager

GCP's native secrets management solution with strong integration with Google Cloud services.

Key Features:

  • Automatic replication across regions
  • Version management
  • IAM integration
  • Audit logging

Implementation Example:

TypeScript
// lib/gcp-secrets.ts
import { SecretManagerServiceClient } from "@google-cloud/secret-manager";

const client = new SecretManagerServiceClient();
const projectId = process.env.GOOGLE_CLOUD_PROJECT!;

export async function accessSecret(secretId: string, version = "latest"): Promise<string> {
  const name = `projects/${projectId}/secrets/${secretId}/versions/${version}`;
  const [versionResponse] = await client.accessSecretVersion({ name });

  return versionResponse.payload?.data?.toString() || "";
}

export async function createSecret(secretId: string, value: string): Promise<void> {
  const parent = `projects/${projectId}`;

  // Create the secret if it doesn't exist
  await client.createSecret({
    parent,
    secretId,
    secret: {
      replication: {
        automatic: {}
      }
    }
  });

  // Add the first version
  await client.addSecretVersion({
    parent: `projects/${projectId}/secrets/${secretId}`,
    payload: {
      data: Buffer.from(value)
    }
  });
}

// Usage with caching
const secretCache = new Map<string, string>();

export async function getCachedSecret(secretId: string): Promise<string> {
  if (secretCache.has(secretId)) {
    return secretCache.get(secretId)!;
  }

  const value = await accessSecret(secretId);
  secretCache.set(secretId, value);

  return value;
}

Cost Breakdown:

  • $0.06 per secret per month
  • $0.03 per 10,000 API calls
  • Example: 50 secrets + 100k calls = $3 + $0.30 = $3.30/month

4. HashiCorp Vault

Vault is the gold standard for secrets management, offering dynamic secrets, encryption-as-a-service, and identity-based access control.

Key Features:

  • Dynamic secrets (generate DB creds on-demand)
  • Secret leasing and revocation
  • Multiple auth methods (LDAP, OIDC, Kubernetes, etc.)
  • Transit secrets engine (encryption as a service)

Implementation Example:

TypeScript
// lib/vault.ts
import Vault from "node-vault";

const vault = Vault({
  endpoint: process.env.VAULT_ADDR,
  token: process.env.VAULT_TOKEN,
});

export async function getDatabaseCredentials(): Promise<{
  username: string;
  password: string;
}> {
  // Dynamic secrets - these are generated on-demand
  const result = await vault.read("database/creds/readonly");

  return {
    username: result.data.username,
    password: result.data.password,
  };
}

// Auto-revoke after use
export async function withDbCredentials<T>(
  fn: (creds: { username: string; password: string }) => Promise<T>
): Promise<T> {
  const creds = await getDatabaseCredentials();
  const leaseId = (await vault.read("database/creds/readonly")).lease_id;

  try {
    return await fn(creds);
  } finally {
    // Revoke immediately after use
    await vault.revoke({ lease_id: leaseId });
  }
}

// Example: Using Vault's KV engine
export async function getAppConfig(appName: string): Promise<Record<string, string>> {
  const result = await vault.read(`kv/data/${appName}`);
  return result.data.data; // KV v2 wraps data in another data object
}

Cost Breakdown:

  • Open Source: Free (but you pay for infrastructure + ops time)
  • Enterprise: Starting at ~$30,000/year
  • Hidden costs: 20-40 hours/month for maintenance, updates, monitoring

Operational overhead

Vault is powerful but complex. You're responsible for high availability, backups, unsealing, and upgrades. Only choose self-hosted Vault if you have dedicated DevOps resources.

5. Doppler

Doppler is a developer-first secrets management platform that focuses on ease of use and great DX.

Key Features:

  • Universal secrets management
  • Environment sync across platforms
  • CLI and SDKs for all major languages
  • Audit logs and compliance

Implementation Example:

TypeScript
// lib/doppler-config.ts
import { DopplerClient } from "@doppler/node";

const doppler = new DopplerClient({
  token: process.env.DOPPLER_TOKEN,
});

export async function getSecrets(project = "myapp", environment = "production") {
  const response = await doppler.secrets.get({
    project,
    environment,
  });

  return response.secrets;
}

// Usage with TypeScript types
interface AppConfig {
  DATABASE_URL: string;
  API_KEY: string;
  JWT_SECRET: string;
  REDIS_URL: string;
}

export async function getConfig(): Promise<AppConfig> {
  const secrets = await getSecrets();

  return {
    DATABASE_URL: secrets.DATABASE_URL.value,
    API_KEY: secrets.API_KEY.value,
    JWT_SECRET: secrets.JWT_SECRET.value,
    REDIS_URL: secrets.REDIS_URL.value,
  };
}

CLI Workflow:

Bash
# Install Doppler CLI
curl -Ls --tlsv1.2 --proto "=https" --retry 3 \
  https://cli.doppler.com/install.sh | sh

# Login
doppler login

# Setup project
doppler projects setup

# Run with secrets injected
doppler run -- npm run start

# Export to .env (for local dev)
doppler secrets download --format env > .env

Cost Breakdown:

  • Free: Up to 5 users, 3 projects, 100 secrets
  • Starter: $25/month (unlimited secrets, 10 users)
  • Team: $50/month (SSO, audit logs, 25 users)
  • Enterprise: Custom pricing

6. Infisical

Infisical is an open-source, end-to-end encrypted platform for secrets management with a strong focus on developer experience and GitOps.

Key Features:

  • End-to-end encryption
  • GitOps-friendly
  • Auto-sync across environments
  • Open-source (self-host or cloud)

Implementation Example:

TypeScript
// lib/infisical.ts
import { InfisicalClient } from "@infisical/sdk";

const client = new InfisicalClient({
  siteUrl: "https://app.infisical.com", // or your self-hosted URL
  token: process.env.INFISICAL_TOKEN!,
});

export async function getSecrets(environment = "production") {
  const secrets = await client.getSecrets({
    environment,
    projectId: process.env.INFISICAL_PROJECT_ID!,
  });

  return secrets.reduce((acc, secret) => {
    acc[secret.secretKey] = secret.secretValue;
    return acc;
  }, {} as Record<string, string>);
}

// Usage with automatic refresh
export class SecretManager {
  private cache: Record<string, string> = {};
  private lastFetch: number = 0;
  private readonly CACHE_TTL = 300000; // 5 minutes

  async get(key: string): Promise<string> {
    const now = Date.now();

    if (now - this.lastFetch > this.CACHE_TTL) {
      this.cache = await getSecrets();
      this.lastFetch = now;
    }

    return this.cache[key];
  }

  invalidate() {
    this.lastFetch = 0;
  }
}

CLI Workflow:

Bash
# Install Infisical CLI
npm install -g @infisical/cli

# Login
infisical login

# Initialize project
infisical init

# Run with secrets
infisical run --env=production -- npm run start

# Import from .env
infisical secrets import --env=development --file-path=.env.local

Cost Breakdown:

  • Free (Cloud): Up to 5 users, unlimited secrets
  • Team: $10/user/month (SSO, audit logs)
  • Enterprise: Custom (self-hosting, advanced compliance)
  • Self-hosted: Free (open-source)

Why Infisical stands out

Infisical offers end-to-end encryption, meaning even Infisical can't read your secrets. They also have excellent Kubernetes integration and a growing ecosystem of integrations.

7. 1Password Secrets Automation

If your team already uses 1Password, their Secrets Automation feature provides a natural extension for application secrets.

Key Features:

  • Leverages existing 1Password infrastructure
  • Connect CLI for local development
  • Service accounts for applications
  • Audit trails

Implementation Example:

TypeScript
// lib/1password.ts
import { Client } from "@1password/secrets";

const client = await Client.create({
  serviceAccountToken: process.env.OP_SERVICE_ACCOUNT_TOKEN!,
});

export async function getSecret(secretPath: string): Promise<string> {
  const secret = await client.getSecret(secretPath);
  return secret.value;
}

// Usage
export async function getConfig() {
  return {
    databaseUrl: await getSecret("apps/myapp/database-url"),
    apiKey: await getSecret("apps/myapp/api-key"),
    jwtSecret: await getSecret("apps/myapp/jwt-secret"),
  };
}

CLI Workflow:

Bash
# Install Connect CLI
curl -fsSL https://1password.com/downloads/connect-cli.sh | sh

# Login
op signin

# List secrets
op item list --vault "My App Secrets"

# Get secret
op read "op://My App Secrets/database-url/password"

# Run with secrets
op run -- npm run start

Cost Breakdown:

  • Requires 1Password Business account: $19.95/user/month
  • Secrets Automation included in Business plan
  • Additional cost if you don't already use 1Password

8. CyberArk Conjur

CyberArk Conjur is an enterprise-grade secrets management solution designed for large organizations with strict compliance requirements.

Key Features:

  • Enterprise security and compliance
  • Policy-based access control
  • Integration with CI/CD pipelines
  • Audit and compliance reporting

Implementation Example:

TypeScript
// lib/conjur.ts
import { ConjurClient } from "@cyberark/conjur";

const client = new ConjurClient({
  conjurUrl: process.env.CONJUR_URL!,
  conjurAccount: process.env.CONJUR_ACCOUNT!,
  credentials: {
    login: process.env.CONJUR_LOGIN!,
    apiKey: process.env.CONJUR_API_KEY!,
  },
});

export async function getSecret(secretId: string): Promise<string> {
  const secret = await client.retrieveSecret(secretId);
  return secret;
}

// Usage with policy enforcement
export async function getDatabaseCredentials() {
  const username = await getSecret("prod/database/username");
  const password = await getSecret("prod/database/password");

  return { username, password };
}

Cost Breakdown:

  • Custom enterprise pricing
  • Typically starts at $50,000+/year
  • Requires dedicated infrastructure and ops team
  • Best for: Fortune 500, regulated industries (finance, healthcare)

Enterprise complexity

Conjur is powerful but requires significant setup and maintenance. Only consider if you have dedicated security teams and compliance requirements that demand it.

9. GitLab CI/CD Variables & GitHub Secrets

Built-in secrets management for CI/CD workflows, perfect for teams heavily invested in these platforms.

GitLab CI/CD Variables Example:

YAML
# .gitlab-ci.yml
variables:
  DATABASE_URL: $DATABASE_URL  # Protected variable
  API_KEY: $API_KEY

stages:
  - build
  - deploy

build:
  stage: build
  script:
    - npm install
    - npm run build
  variables:
    NODE_ENV: production

deploy:
  stage: deploy
  script:
    - echo "Deploying with API key: $API_KEY"
    - ./deploy.sh
  environment: production
  only:
    - main

GitHub Actions Secrets Example:

YAML
# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          API_KEY: ${{ secrets.API_KEY }}
          JWT_SECRET: ${{ secrets.JWT_SECRET }}

      - name: Deploy
        run: ./deploy.sh
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Cost Breakdown:

  • GitLab: Included in all plans (Free, Premium $19/user/mo, Ultimate $99/user/mo)
  • GitHub: Included in all plans (Free, Team $4/user/mo, Enterprise $21/user/mo)
  • Environment-specific secrets require Premium/Team plans or higher

10. SOPS (Secrets OPerationS)

Mozilla's SOPS is a CLI tool for managing encrypted files, perfect for GitOps workflows.

Key Features:

  • Encrypts values in YAML, JSON, ENV, and INI files
  • Supports AWS KMS, GCP KMS, Azure Key Vault, HashiCorp Vault, and PGP
  • Git-friendly (encrypts values, not keys)
  • Works with existing CI/CD pipelines

Implementation Example:

YAML
# config.enc.yaml (encrypted file - safe to commit)
database:
  url: ENC[AES256_GCM,data:abc123...,iv:xyz789...,tag:tag123,type:str]
  username: ENC[AES256_GCM,data:def456...,iv:uvw012...,tag:tag456,type:str]
  password: ENC[AES256_GCM,data:ghi789...,iv:rst345...,tag:tag789,type:str]

api:
  key: ENC[AES256_GCM,data:jkl012...,iv:opq678...,tag:tag012,type:str]
Bash
# Create .sops.yaml configuration
cat > .sops.yaml <<EOF
creation_rules:
  - path_regex: .*\.enc\.yaml$
    kms: "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
    age: age1yhm3g6w8q8z9x0y1w2v3u4t5s6r7q8p9o0n1m2l3k4j5i6h7g8f9e0d
EOF

# Encrypt a file
sops -e config.yaml > config.enc.yaml

# Decrypt a file
sops -d config.enc.yaml > config.yaml

# Edit encrypted file directly
sops config.enc.yaml

# Use in CI/CD
sops -d config.enc.yaml | kubectl apply -f -

Cost Breakdown:

  • Free and open-source
  • You pay for the KMS service you use (AWS KMS, GCP KMS, etc.)
  • Example: AWS KMS costs ~$1/month per key + $0.03 per 10,000 operations

GitOps gold standard

SOPS is the go-to choice for GitOps workflows. You can commit encrypted secrets to Git safely, and decrypt them at deployment time.

When to Use Local .env vs Cloud Secrets

Not every project needs a full-blown secrets management solution. Here's a decision framework:

Stick with Local .env When:

  • Solo projects or prototypes: You're the only one touching the code
  • Local development: Development databases, mock APIs, feature flags
  • Public/open-source repos: Where secrets should never exist anyway
  • Short-lived experiments: Weekend hacks, proof-of-concepts
Bash
# .env.local - Safe for local dev
NODE_ENV=development
DEBUG=true
MOCK_API_URL=http://localhost:3001
DATABASE_URL=postgres://localhost:5432/myapp_dev

Move to Cloud Secrets When:

  • Team collaboration: 2+ developers need access
  • Production deployments: Real user data, payment processing
  • Compliance requirements: SOC2, HIPAA, GDPR
  • Secret rotation: Security policies require regular key changes
  • Audit trails: You need to know who accessed what and when
Bash
# .env.production - Should NEVER be in your repo
DATABASE_URL=postgres://prod-user:REDACTED@prod-db:5432/app
STRIPE_SECRET_KEY=sk_live_...
AWS_SECRET_ACCESS_KEY=...

Red flags that mean migrate now

  • You're sharing .env files via Slack/Email
  • You have "dev", "staging", and "prod" in separate .env files
  • You can't remember when you last rotated a production API key
  • New developers ask "where do I get the .env file?"

The Hybrid Approach: Local Dev, Cloud Prod

The most practical approach for most teams is a hybrid model: use local .env files for development, but sync production secrets from a cloud provider.

Here's how to structure it:

project/
├── .env.example          # Template (commit this)
├── .env.local            # Local overrides (gitignored)
├── .env                  # Synced from cloud (gitignored)
├── .env.production       # Production values (gitignored)
└── lib/
    └── config.ts         # Smart config loader
TypeScript
// lib/config.ts
import dotenv from "dotenv";
import path from "path";
import { getSecret } from "./secrets"; // Your secrets manager

const env = process.env.NODE_ENV || "development";

// Load base .env file
dotenv.config({
  path: path.resolve(process.cwd(), `.env${env === "production" ? ".production" : ""}`)
});

// Load local overrides (never committed)
if (env === "development") {
  dotenv.config({
    path: path.resolve(process.cwd(), ".env.local"),
    override: true
  });
}

// In production, fetch sensitive values from cloud
export async function loadProductionSecrets() {
  if (env !== "production") return;

  const secrets = await getSecret("myapp/production");

  // Override with cloud values
  process.env.DATABASE_URL = secrets.databaseUrl;
  process.env.API_KEY = secrets.apiKey;
  process.env.JWT_SECRET = secrets.jwtSecret;
}

// Initialize
if (env === "production") {
  loadProductionSecrets().catch(console.error);
}

export const config = {
  env,
  database: {
    url: process.env.DATABASE_URL!,
    poolSize: env === "production" ? 20 : 5
  }
} as const;

Best practice

Never store production secrets in .env files, even if they're gitignored. Use environment-specific files and cloud sync.

How evnx Bridges the Gap

This is where tools like evnx (environment variable sync) come in. Evnx provides a local CLI that syncs with cloud secrets, giving you the best of both worlds.

Key Features of evnx:

  • Local CLI that feels like working with .env files
  • Automatic sync with AWS Secrets Manager, Vault, or other backends
  • Encryption at rest for local .env files
  • Team sharing with role-based access control

Workflow:

Bash
# Initialize evnx
evnx init

# Link to your cloud provider
evnx link aws --secret-id myapp/development

# Pull secrets (creates encrypted .env)
evnx pull

# Run your app
evnx run npm run dev

# Push local changes (with approval workflow)
evnx push DATABASE_URL=postgres://new-host:5432/db

# Scan for security issues
evnx scan

# [SCAN] Scanning .env files...
# [ERROR] AWS_SECRET_ACCESS_KEY matches high-entropy pattern (256-bit key)
# [ERROR] Pattern: aws_secret (confidence: high)
# [INFO] 1 secret detected. Commit blocked.

Integration Example:

TypeScript
// lib/evnx-config.ts
import { EvnxClient } from "@evnx/sdk";

const evnx = new EvnxClient({
  backend: "aws-secrets-manager",
  secretId: process.env.EVNX_SECRET_ID!,
  cacheTtl: 300 // 5 minutes
});

export async function getEnvWithFallback(key: string, fallback: string): Promise<string> {
  // Try cloud first, fallback to local .env
  const cloudValue = await evnx.get(key);
  return cloudValue || process.env[key] || fallback;
}

// Usage
export const config = {
  database: {
    url: await getEnvWithFallback("DATABASE_URL", "postgres://localhost/dev")
  }
};

Pre-commit hook

evnx includes a pre-commit hook that scans for leaked secrets before they leave your machine. It takes 180ms on cold start—fast enough that nobody disables it.

Advanced Patterns & Security Considerations

1. Secret Rotation Without Downtime

TypeScript
// lib/rotation.ts
export async function rotateDatabaseCredentials() {
  const client = new SecretsManagerClient({});

  // Step 1: Create new credentials
  const newCreds = await createNewDatabaseUser();

  // Step 2: Update secret with both old and new
  await client.send(new UpdateSecretCommand({
    SecretId: "prod/db/creds",
    SecretString: JSON.stringify({
      ...newCreds,
      previousPassword: await getCurrentPassword()
    })
  }));

  // Step 3: Update database permissions
  await grantDatabaseAccess(newCreds.username);

  // Step 4: Schedule old credential removal
  setTimeout(async () => {
    await revokeDatabaseAccess(oldUsername);
  }, 24 * 60 * 60 * 1000); // 24 hours
}

2. Caching Strategy

TypeScript
// lib/secret-cache.ts
import NodeCache from "node-cache";

const secretCache = new NodeCache({ stdTTL: 300 }); // 5 minutes

export async function getCachedSecret(secretName: string) {
  const cached = secretCache.get(secretName);
  if (cached) return cached;

  const secret = await getSecret(secretName);
  secretCache.set(secretName, secret);

  return secret;
}

// Invalidate on update
export async function updateSecret(name: string, value: any) {
  await secretsManager.updateSecret(name, value);
  secretCache.del(name); // Clear cache
}

3. Audit Logging

TypeScript
// lib/audit.ts
export async function logSecretAccess(
  secretName: string,
  userId: string,
  action: "read" | "write" | "delete"
) {
  await auditClient.putRecord({
    TableName: "SecretAuditLog",
    Item: {
      timestamp: new Date().toISOString(),
      secretName,
      userId,
      action,
      ipAddress: getClientIp(),
      userAgent: getUserAgent()
    }
  });
}

// Middleware example
export const auditSecretAccess = (secretName: string) => {
  return async (req: Request, res: Response, next: NextFunction) => {
    await logSecretAccess(secretName, req.user.id, "read");
    next();
  };
};

4. Environment-Specific Encryption

TypeScript
// lib/encrypted-env.ts
import crypto from "crypto";

const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY!; // 32 bytes
const IV_LENGTH = 16;

export function encrypt(text: string): string {
  const iv = crypto.randomBytes(IV_LENGTH);
  const cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(ENCRYPTION_KEY), iv);
  let encrypted = cipher.update(text, "utf8", "hex");
  encrypted += cipher.final("hex");
  return iv.toString("hex") + ":" + encrypted;
}

export function decrypt(text: string): string {
  const [ivHex, encrypted] = text.split(":");
  const iv = Buffer.from(ivHex, "hex");
  const decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(ENCRYPTION_KEY), iv);
  let decrypted = decipher.update(encrypted, "hex", "utf8");
  decrypted += decipher.final("utf8");
  return decrypted;
}

Cost Analysis: Startups vs Enterprises

Startup Scenario (5 developers, 3 environments)

AWS Secrets Manager:

  • 30 secrets (10 per env) × $0.40 = $12/month
  • API calls (100k/month) = $0.50
  • Total: ~$12.50/month

Infisical (Cloud):

  • Free tier covers up to 5 users
  • Total: $0/month

Doppler:

  • Free tier covers up to 5 users, 3 projects
  • Total: $0/month

HashiCorp Vault (Self-hosted):

  • Infrastructure (t3.small EC2): $15/month
  • Engineering time (5 hrs/month × $50/hr): $250
  • Total: ~$265/month (hidden ops cost!)

dotenv.space:

  • 3 projects × $10 = $30/month
  • Total: $30/month

Startup winner

For startups: Start with Infisical or Doppler's free tiers. Migrate to AWS Secrets Manager or paid plans when you scale.

Enterprise Scenario (50 developers, 5 environments, compliance requirements)

AWS Secrets Manager:

  • 500 secrets × $0.40 = $200/month
  • API calls (2M/month) = $10
  • CloudTrail logging = $50
  • Total: ~$260/month

HashiCorp Vault Enterprise:

  • License: $30,000/year = $2,500/month
  • Dedicated infra: $200/month
  • FTE for maintenance (0.5 engineer): $5,000/month
  • Total: ~$7,700/month

CyberArk Conjur:

  • License + support: ~$5,000/month
  • Infrastructure: $500/month
  • Dedicated team (2 engineers): $15,000/month
  • Total: ~$20,500/month

Infisical Enterprise:

  • 50 users × $10 = $500/month
  • Self-hosting option: $0 license + infra costs
  • Total: $500-$2,000/month (depending on hosting)

Doppler Enterprise:

  • Custom pricing, typically ~$2,000/month
  • Total: ~$2,000/month

Enterprise considerations

For enterprises, the decision isn't just about cost—it's about compliance, audit requirements, and existing infrastructure. Vault and CyberArk win on features; Infisical and Doppler win on ease of use.

Making the Decision

Here's a quick decision tree:

Are you on AWS?
├─ Yes → AWS Secrets Manager (easy integration)
└─ No → Multi-cloud or SaaS?
    ├─ Need dynamic secrets, max security → Vault
    ├─ Want simplicity, small team → Infisical or Doppler
    ├─ Already use 1Password → 1Password Secrets Automation
    ├─ GitOps workflow → SOPS
    ├─ Enterprise compliance → CyberArk Conjur
    └─ Need hybrid approach → evnx + your provider

Don't over-engineer

Start simple. You can always migrate to a more complex solution later. Most teams can start with Infisical, Doppler, or dotenv.space and scale up as needed.

Next Steps

Ready to level up your secrets management?

  1. Audit your current setup: Run grep -r "API_KEY\|PASSWORD\|SECRET" . to find exposed secrets
  2. Start small: Migrate one service (like your database credentials) first
  3. Implement rotation: Set a calendar reminder to rotate keys quarterly
  4. Add monitoring: Track secret access patterns for anomalies
  5. Document everything: Your future self (and onboarding devs) will thank you

Further Reading

Final thought

The best secrets management solution is the one your team will actually use. Don't let perfect be the enemy of secure—start with something simple and iterate as you grow.


Have questions about implementing secrets management in your stack? Drop a comment below or reach out on Twitter [@ajit].

#devops#security#aws#vault#environment-variables#cloud#infisical#doppler
A

Ajit Kumar

Creator, evnx

Developer, researcher, security advocate, and accidental AWS key pusher. I built evnx after a production incident I'd rather forget — so you don't have to repeat my mistakes. Currently obsessed with Rust, developer tooling, and zero-trust secret management.