Cybersecurity & Compliance

Secrets Management at Scale: Vault vs AWS Secrets Manager vs SOPS, Rotation and Dynamic Secrets

MatterAI
MatterAI
11 min read·

Secrets Management at Scale: Vault vs AWS Secrets Manager vs SOPS, Rotation and Dynamic Secrets

Every team starts with a .env file in a gitignored directory. That works until you have more than one server, more than one environment, or more than one person. Then you need rotation, audit logs, per-service access control, and a way to survive a compromised host without rotating every secret by hand. This guide compares the three tools that solve this at scale: HashiCorp Vault, AWS Secrets Manager, and SOPS, and covers the two patterns that determine which one fits: static secrets with rotation versus dynamic, short-lived secrets.

The Two Secret Models

ModelLifecycleRotationBlast Radius
Static secretCreated once, used until rotatedManual or scheduled; downtime risk if credentials change mid-sessionLong-lived credential = wide blast radius if leaked
Dynamic secretGenerated on demand, short TTLAutomatic; each lease is a fresh credentialCompromised credential expires in minutes

Static secrets are database passwords, API keys, and TLS private keys that exist independently of who requested them. Dynamic secrets are credentials a secrets system mints on request, scoped to the requester, with a lease that expires. Dynamic secrets are strictly better for blast radius, but they require the secrets system to have write access to the backing service (it has to create the user or key). That is not always possible, so most teams run a mix.

Tool Comparison

DimensionHashiCorp VaultAWS Secrets ManagerSOPS
DeploymentSelf-hosted (or HCP Vault)Managed AWS serviceCLI tool, no server
StorageEncrypted KV store (Raft or external)AWS-managed, KMS-encryptedEncrypted files in git
Dynamic secretsFirst-class (DB, AWS, PKI, SSH, cloud)Limited (RDS rotation only)Not supported
RotationRotation functions (custom or built-in)Built-in for RDS/DocumentDB/Redshift; Lambda for customManual; re-encrypt with new key
Access controlPolicy-based (HCL), identity-basedIAM + resource policiesKMS/AWS IAM + age keys
AuditBuilt-in audit devices (file, syslog, socket)CloudTrailGit history
CostSelf-hosted is free; HCP Vault is paid0.40persecret/month+0.40 per secret/month + 0.05 per 10K API callsFree (you pay for KMS usage)
Best forDynamic secrets, multi-cloud, on-premAWS-native workloads, RDS rotationConfig files in git, GitOps

SOPS: Encrypted Secrets in Git

SOPS (Secrets OPerationS) encrypts only the values in a YAML or JSON file, leaving keys readable. The encrypted file lives in git, decrypted at deploy time by someone or something with the KMS key. There is no server, no database, no daemon.

# Encrypt a file with an AWS KMS key
sops --encrypt --kms arn:aws:kms:us-east-1:123456789012:key/abcd-1234 \
     --in-place secrets.yaml
# secrets.yaml after SOPS encryption (keys readable, values encrypted)
database:
  host: ENC[AES256_GCM,data:8J3k,iv:abc=,tag:def=,type:str]
  password: ENC[AES256_GCM,data:Kp9m,iv:ghi=,tag:jkl=,type:str]
api_key: ENC[AES256_GCM,data:Xy2v,iv:mno=,tag:pqr=,type:str]
sops:
  kms:
    - arn: arn:aws:kms:us-east-1:123456789012:key/abcd-1234
      created_at: "2026-07-28T12:00:00Z"
      enc: CiQ...
  lastmodified: "2026-07-28T12:00:00Z"
  mac: ENC[AES256_GCM,data:...,type:str]

SOPS shines for GitOps: the encrypted file is versioned, reviewable in PRs, and decrypted by your CD tool (Argo CD with the KSOPS plugin, Flux with SOPS integration). The weakness is rotation: changing a secret means re-encrypting the file and committing it, and there is no runtime to mint dynamic credentials.

# Flux Kustomization that decrypts SOPS files at reconcile time
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: app-secrets
spec:
  path: ./manifests
  decryption:
    provider: sops
  sourceRef:
    kind: GitRepository
    name: app-repo

AWS Secrets Manager: Managed, AWS-Native

Secrets Manager is a fully managed service. You store a secret (a JSON blob or key-value pair), attach a rotation Lambda, and applications fetch it via the SDK or an IAM-authenticated API call. No servers, no storage to manage.

import boto3

client = boto3.client("secretsmanager", region_name="us-east-1")

def get_secret(secret_name: str) -> dict:
    response = client.get_secret_value(SecretId=secret_name)
    import json
    return json.loads(response["SecretString"])

# Application code
db_creds = get_secret("prod/checkout-db")
conn = psycopg2.connect(
    host=db_creds["host"],
    user=db_creds["username"],
    password=db_creds["password"],
)

Built-in rotation handles RDS, DocumentDB, and Redshift: AWS provides Lambda functions that rotate the password on a schedule, alternating between two users to avoid downtime. For other secrets (API keys, third-party tokens), you write a custom rotation Lambda implementing four steps: create the new secret, test it, update the secret value, then verify the old one is retired.

# Custom rotation Lambda (simplified)
def lambda_handler(event, context):
    token = event["token"]
    step = event["step"]
    secret_name = event["secretId"]

    if step == "createSecret":
        new_password = generate_password()
        client.put_secret_value(
            SecretId=secret_name,
            SecretString=json.dumps({"password": new_password}),
            VersionStage="AWSPENDING",
            ClientRequestToken=token,
        )
    elif step == "setSecret":
        # Apply the new password to the backing service
        update_third_party_api_key(get_pending_secret(secret_name, token))
    elif step == "finishSecret":
        # Promote pending to current
        client.update_secret_version_stage(
            SecretId=secret_name,
            VersionStage="AWSCURRENT",
            MoveToVersion=token,
            RemoveFromVersion=event["clientVersionToken"],
        )

The cost model matters at scale: 0.40persecretpermonthplus0.40 per secret per month plus 0.05 per 10,000 API calls. A fleet of 200 microservices polling every minute racks up roughly $1,000/month in API calls alone. Use caching (the AWS SDK has a built-in caching layer) to cut calls by 90%+.

HashiCorp Vault: Dynamic Secrets and Multi-Cloud

Vault is the only tool here that does dynamic secrets well. When a service requests a database credential, Vault creates a real database user with a scoped grant and a TTL, returns it, and revokes it when the lease expires or is renewed.

# Enable the database secrets engine
vault secrets enable database

# Configure a PostgreSQL connection
vault write database/config/checkout-prod \
    plugin_name=postgresql-database-plugin \
    connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/checkout?sslmode=disable" \
    allowed_roles="checkout-app" \
    username="vault-admin" \
    password="..."

# Define a role: credentials valid 1 hour, max 24 hours
vault write database/roles/checkout-app \
    db_name=checkout-prod \
    creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
        GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
    default_ttl="1h" \
    max_ttl="24h"
from hvac import Client

vault = Client(url="https://vault.internal:8200")
vault.auth.kubernetes.login(role="checkout-app", jwt=get_service_account_jwt())

# Each call mints a fresh, scoped, time-limited database user
creds = vault.read("database/creds/checkout-app")
# -> {"username": "v-token-checkout-app-abc123", "password": "...", "lease_id": "...", "lease_duration": 3600}

conn = psycopg2.connect(
    host="db.internal",
    user=creds["username"],
    password=creds["password"],
)

The credential did not exist before this call and will not exist after the lease expires. If an attacker steals it, they have a user that can only SELECT and dies in an hour. Compare that to a static password that works until someone notices and manually rotates it.

Vault also does dynamic AWS IAM credentials, PKI certificates (mint a cert per service with a short TTL instead of one long-lived cert), SSH keys, and cloud KMS key access. The audit device records every request:

# Enable file audit logging
vault audit enable file file_path=/var/log/vault/audit.log

# Every secret access is now logged:
# {"time":"2026-07-28T12:00:00Z","type":"request","auth":{"entity_id":"checkout-app"},"request":{"path":"database/creds/checkout-app"}}

The cost is operational: Vault is a distributed system you must run (or pay for HCP Vault). In production you run 3-5 Vault nodes with Raft storage, an auto-unseal mechanism (KMS or transit), and monitoring. This is real infrastructure.

Kubernetes Integration

All three integrate with Kubernetes, but the patterns differ.

Vault: the Vault Agent or CSI provider injects secrets as files or env vars into pods. The pod authenticates with its service account JWT, no long-lived token needed:

# Pod annotation triggers the agent
metadata:
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "checkout-app"
    vault.hashicorp.com/agent-inject-secret-db: "database/creds/checkout-app"

AWS Secrets Manager: the External Secrets Operator fetches from Secrets Manager and syncs into a Kubernetes Secret that pods mount:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: checkout-db
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secretsmanager
    kind: ClusterSecretStore
  target:
    name: checkout-db-secret
  data:
    - secretKey: password
      remoteRef:
        key: prod/checkout-db
        property: password

SOPS: decrypted at the CD layer (Flux or Argo CD with KSOPS), producing a Kubernetes Secret before pods start. No runtime dependency on an external service.

Decision Framework

Choose SOPS when:

  • Your secrets are config files that change rarely
  • You want secrets versioned in git alongside manifests (GitOps)
  • You do not need dynamic credentials or automated rotation
  • You have no budget or appetite for running a secrets server

Choose AWS Secrets Manager when:

  • You are all-in on AWS and want zero infrastructure to manage
  • Your main rotation need is RDS/DocumentDB/Redshift passwords
  • You are fine with per-secret and per-API-call pricing at your scale
  • You do not need dynamic secrets for non-AWS resources

Choose HashiCorp Vault when:

  • You need dynamic, short-lived credentials (the strongest blast-radius reduction)
  • You operate across multiple clouds or on-prem
  • You need fine-grained, policy-based access control beyond IAM
  • You have platform engineers who can run a HA Vault cluster

A common production setup: SOPS for deploy-time config, Vault for runtime dynamic secrets (database, cloud API), and AWS Secrets Manager for the RDS rotation you do not want to build yourself. They are not mutually exclusive.

Adoption Checklist

  1. Inventory every secret: where it lives, who reads it, how often it rotates, what happens if it leaks
  2. Classify each as static-needs-rotation or dynamic-eligible; prefer dynamic where the backing service allows it
  3. Pick the tool per classification, not one tool for everything
  4. Eliminate plaintext secrets in env files, CI variables, and chat history; import them into the chosen system
  5. Enable audit logging on day one; a secrets system without audit is a liability
  6. Add caching at the application layer to control API-call costs (Secrets Manager) or Vault request load
  7. Test rotation in staging before production: a broken rotation Lambda is an outage waiting to happen
  8. Run a quarterly secret-leak scan (git-secrets, trufflehog) against your repos to catch stragglers

MatterAI builds frontier AI infrastructure for engineering teams — from inference-optimized models to autonomous coding agents and agentic code reviews.

Explore what we're building:

  • Orbital IDE — Autonomous AI coding agent with background agents and deep codebase memory
  • AI Code Reviews — Agentic pre-commit reviews across GitHub, GitLab, and Bitbucket
  • Axon Models — Frontier-grade reasoning models at 70% lower inference cost

Get started free - https://app.matterai.so


Follow us on X · LinkedIn · GitHub

Share this Guide:

Ship Faster. Ship Safer.

Join thousands of engineering teams using MatterAI to autonomously build, review, and deploy code with enterprise-grade precision.

No credit card requiredSOC 2 Type IISetup in 2 min