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
| Aspect | Parameter Store (SSM) | Secrets Manager |
|---|---|---|
| Belongs to | AWS Systems Manager | Standalone service |
| Pricing | Standard tier: free. Advanced: $0.05/param/month + API calls | **0.05 per 10k API calls |
| Max value size | 4 KB (standard), 8 KB (advanced) | 64 KB |
| Encryption | Optional (SecureString with KMS) | Always KMS-encrypted |
| Automatic rotation | ❌ No built-in | ✅ Yes — Lambda-based, pre-built rotators for RDS/DocumentDB/Redshift |
| Cross-account sharing | Via KMS key policy + IAM only | Native 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 |
| Integration | EC2, ECS, Lambda, CloudFormation, aws ssm get-parameter | RDS, ECS, Lambda, aws secretsmanager get-secret-value |
| CloudWatch events on change | ✅ | ✅ |
| Parameter Policies (expiration, notification) | Advanced tier only | N/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 withGetParametersByPath - 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 textStringList— CSV, treated as a listSecureString— KMS-encrypted value; decrypt requireskms:Decrypton 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 definition →
secretsblock 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
- Hardcoding Parameter Store names in code — use environment variables / task-def secrets references so tests can inject fakes.
- Forgetting
kms:Decrypton the KMS key for SecureString / Secrets Manager. Access denied despite correct IAM on the secret itself. - 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.
- 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.
- SecureString for large values — 4 KB cap blocks certificates and large JSON. Use Advanced tier (8 KB) or Secrets Manager (64 KB).
AWSPENDINGstuck. Rotation half-succeeded, version stage got stuck; manual cleanup required.- 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