Secrets Manager vs Parameter Store

Two ways to store secrets and configuration in AWS. Parameter Store (part of SSM) is simple, free for standard params, and fine for 80% of cases. Secrets Manager is pricier but comes with automatic rotation for databases and a richer native story. Most orgs end up using both.

Quick verdict

  • Plain config + small secrets, cost-sensitive → Parameter Store (standard tier)
  • Database credentials needing automatic rotation → Secrets Manager
  • Cross-account / cross-region replication of secrets → Secrets Manager
  • Large values (> 4 KB) → Parameter Store Advanced (up to 8 KB) or Secrets Manager (up to 64 KB)

Side-by-side

AspectParameter Store (SSM)Secrets Manager
Belongs toAWS Systems ManagerStandalone service
PricingStandard tier: free. Advanced: $0.05/param/month + API calls**0.05 per 10k API calls
Max value size4 KB (standard), 8 KB (advanced)64 KB
EncryptionOptional (SecureString with KMS)Always KMS-encrypted
Automatic rotation❌ No built-inYes — Lambda-based, pre-built rotators for RDS/DocumentDB/Redshift
Cross-account sharingVia KMS key policy + IAM onlyNative via resource-based policy
Cross-region replication❌ Manual✅ Native
Versioning✅ History of values✅ Version stages (AWSCURRENT, AWSPREVIOUS, AWSPENDING)
Hierarchical paths/app/prod/db/password❌ Flat namespace
IntegrationEC2, ECS, Lambda, CloudFormation, aws ssm get-parameterRDS, ECS, Lambda, aws secretsmanager get-secret-value
CloudWatch events on change
Parameter Policies (expiration, notification)Advanced tier onlyN/A

What each one is good for

Parameter Store — ops & config first, secrets second

  • Config values — feature flags, endpoint URLs, tuning parameters
  • Hierarchical org: /services/api/prod/DATABASE_URL, /services/api/staging/DATABASE_URL — fetch with GetParametersByPath
  • Small secrets (SecureString type) where automatic rotation isn’t required
  • CloudFormation / Terraform parameterisation — dynamic references pull at deploy time
  • SSM Automation / Run Command — parameters drive runbooks

Types:

  • String — plain text
  • StringList — CSV, treated as a list
  • SecureString — KMS-encrypted value; decrypt requires kms:Decrypt on the key

Secrets Manager — secrets that rotate

  • Database credentials for RDS, Aurora, DocumentDB, Redshift — first-class rotation with pre-built Lambdas
  • Multi-user rotation — maintains two alternating users to rotate without downtime (the “alternating user” strategy)
  • Cross-region replica for DR — write once, replicate to N regions
  • Larger payloads (JSON blobs, certificates, full config bundles)
  • Cross-account access via a resource policy on the secret — cleaner than KMS-policy gymnastics

The rotation story — why Secrets Manager exists

This is the main reason to pay for Secrets Manager. For an RDS credential:

1. You define rotation: every 30 days
2. Secrets Manager invokes a Lambda at schedule:
     - Generate a new password
     - Apply it to RDS (ALTER USER ... IDENTIFIED BY ...)
     - Test the new credential
     - Promote AWSPENDING → AWSCURRENT
3. Old version remains as AWSPREVIOUS for rollback
4. Clients fetching AWSCURRENT automatically get the new value

AWS ships managed Lambdas for common databases; you can write custom rotators for anything else.

Parameter Store has no rotation primitive — you’d write the Lambda + EventBridge glue yourself. If that’s your situation, the ~$5/month per secret for 10 secrets is often cheaper than maintaining the bespoke rotation plumbing.

Namespacing and hierarchies

Parameter Store wins on shape:

/myapp/prod/db/host
/myapp/prod/db/port
/myapp/prod/db/username
/myapp/prod/db/password    ← SecureString
/myapp/prod/feature-flag/new-ui

Apps call GetParametersByPath(Path=/myapp/prod/) and get everything in one call. Secrets Manager is flat; you’d either:

  • Bundle a JSON object into one secret (then parse on the client), or
  • Name secrets with a naming convention and query multiple

For 12-factor-style config + secrets, Parameter Store’s hierarchy is more ergonomic.

IAM on both

Both are IAM-controlled on the read path:

{
  "Effect": "Allow",
  "Action": ["ssm:GetParameter", "ssm:GetParameters", "ssm:GetParametersByPath"],
  "Resource": "arn:aws:ssm:region:acct:parameter/myapp/prod/*"
}

For SecureString / Secrets Manager, the caller must also have kms:Decrypt on the KMS key used for encryption (unless it’s the default AWS-managed key, in which case the service handles it).

ECS / EKS / Lambda integrations

Both support injection at runtime — no code change needed to pull secrets:

  • ECS task definitionsecrets block referencing SSM or Secrets Manager ARN → injected as environment variable
  • EKS → External Secrets Operator, CSI driver, or IRSA + SDK call
  • Lambda → native integration, or use extensions for cached retrieval

Caching

The agent/SDK does not cache by default — a cold read hits the service each time. At high QPS, use:

  • AWS Parameters and Secrets Lambda Extension — sidecar cache for Lambda
  • AWS SDK caching libraries (aws-secretsmanager-caching-* for Java/Python/Go/JS)
  • App-level cache with TTL matching your rotation frequency

Without caching, high-volume apps can be surprised by the read cost (especially Secrets Manager’s per-call pricing).

Migration & coexistence

Common pattern:

  • Parameter Store holds config and non-rotating secrets
  • Secrets Manager holds the few credentials that rotate (DB, API keys for critical integrations)

Both tools, both integrations, pick the right one per secret. Apps often use both.

For migration direction: Parameter Store → Secrets Manager is straightforward (just get + create). Going the other way loses rotation history but is also trivial.

Common pitfalls

  1. Hardcoding Parameter Store names in code — use environment variables / task-def secrets references so tests can inject fakes.
  2. Forgetting kms:Decrypt on the KMS key for SecureString / Secrets Manager. Access denied despite correct IAM on the secret itself.
  3. Cross-account weirdness. Parameter Store has no resource policy — you need KMS-key cross-account sharing + IAM AssumeRole, which is harder. Secrets Manager’s resource policy is the cleaner door.
  4. Rotation Lambda in the wrong VPC — can’t reach the DB, rotation fails, alarms everywhere. Place it in a subnet that can reach the database.
  5. SecureString for large values — 4 KB cap blocks certificates and large JSON. Use Advanced tier (8 KB) or Secrets Manager (64 KB).
  6. AWSPENDING stuck. Rotation half-succeeded, version stage got stuck; manual cleanup required.
  7. Using Parameter Store for high-frequency reads — can exceed throughput limits. Cache. Or use Secrets Manager with caching. Or move the hot path to env vars at container start.

Decision flow

Does the value need automatic rotation?
├── yes → Secrets Manager
└── no
    Is it > 8 KB?
    ├── yes → Secrets Manager
    └── no
        Need cross-region replication / cross-account resource policy?
        ├── yes → Secrets Manager
        └── no  → Parameter Store

See also