Skip to content
ExponaBLOG
AWS FOR DEVELOPERS · DAY 056 / 100

Secrets Manager vs Parameter Store in AWS for Developers

Where to store configuration and secrets.

Expona Admin7 min read

Introduction

You've built a solid application, deployed it to AWS, and now you're staring at a spreadsheet of database passwords, API keys, and configuration values scattered across your codebase. This is the moment when most teams realize they need a proper secrets management strategy. In this post, we're going to compare Secrets Manager vs Parameter Store—two AWS services that solve similar problems but with fundamentally different approaches, pricing models, and feature sets.

The question isn't really which one is "better." The question is which one fits your use case. I've made the wrong choice on both sides—I've paid unnecessarily for Secrets Manager when Parameter Store would have worked fine, and I've tried to force Parameter Store into scenarios where it simply didn't have the features I needed. Let's build the mental model so you don't repeat those mistakes.

Comparison: Secrets Manager vs Parameter Store

Let me lay out the core differences in a structured way. These two services solve overlapping problems, but they're built for different scenarios.

Feature Secrets Manager Parameter Store
Automatic rotation Built-in, managed by AWS No native rotation
Pricing model $0.40 per secret per month + API calls Free (standard tier), paid for advanced
Cross-account access Full resource-based policy support Limited, requires manual setup
Organization hierarchy Flat structure with tags Hierarchical paths (/app/db/prod/host)
Encryption at rest KMS mandatory KMS optional (parameter tier dependent)
Secret versioning Automatic, up to 100 versions Optional, manual versions only
Audit trail CloudTrail + detailed access logs CloudTrail only

The pricing difference is where most teams feel the pain first. If you're storing 50 secrets in Secrets Manager, you're looking at roughly $240 per month just for the secrets themselves, before API call costs. Parameter Store's standard tier is free. That's a $240 swing in cost for the same data stored differently.

But Secrets Manager's automatic rotation feature is a genuine problem-solver. It can rotate your database credentials, RDS passwords, and other supported secret types without your application knowing about it. Parameter Store won't do this—you'd need to build a Lambda function to handle rotation yourself.

The hierarchical organization in Parameter Store also matters in practice. You can structure secrets like /myapp/production/database/host, /myapp/production/database/port, and /myapp/staging/database/host, then retrieve entire subtrees with a single API call. Secrets Manager is flat; you retrieve individual secrets by name.

Secrets Manager: When You Need Rotation

Use Secrets Manager when you're managing credentials that should rotate automatically. The textbook case is database credentials.

Imagine you have an RDS PostgreSQL instance. Your application connects with a username and password. Every 30 days, you want that password to automatically rotate—the database updates the credential, and your application picks up the new one without restarting.

Here's how this works: you create a secret in Secrets Manager, configure a Lambda function (which AWS provides templates for), and tell Secrets Manager to rotate the secret every 30 days. When rotation time comes, the Lambda function runs, updates the password in RDS, updates the secret in Secrets Manager, and tests the new credential. Your application code doesn't need to know rotation happened.

This is the scenario where Secrets Manager's cost justifies itself. If you're managing 10 databases across production, staging, and development, and you need rotation for compliance or security policy, paying $4.80 per month per secret plus API calls is reasonable.

Another solid use case is API keys for third-party services. If you have an API key for a payment provider, an email service, or a logging platform, storing it in Secrets Manager means you have a centralized audit trail, encryption, and versioning. When you need to revoke a key and issue a new one, you can do it in one place.

Retrieving a secret from Secrets Manager is straightforward:

bash
aws secretsmanager get-secret-value \
  --secret-id prod/database/postgres \
  --region us-east-1 \
  --query SecretString \
  --output text

If your secret stores JSON (which is common for multi-value secrets like {"username": "admin", "password": "..."}), parse it after retrieval. Most SDKs handle this cleanly—the AWS SDK for .NET or Node.js will deserialize the JSON for you.

Parameter Store: Configuration at Scale

Parameter Store shines for configuration data that doesn't need rotation. Think database hostnames, feature flags, API endpoints, log levels, cache TTLs, and application settings that change between environments.

One of my projects manages configuration for a microservices platform across three environments: development, staging, and production. We have dozens of parameters: database hostnames, cache endpoints, feature flags, timeout values, batch sizes, and more. Using Parameter Store's hierarchical structure, we organize everything like this:

bash
/myapp/development/database/host = localhost
/myapp/development/database/port = 5432
/myapp/development/cache/endpoint = redis-dev:6379
/myapp/development/features/newPaymentFlow = true
/myapp/production/database/host = prod-db.internal
/myapp/production/database/port = 5432
/myapp/production/cache/endpoint = prod-redis-cluster.internal
/myapp/production/features/newPaymentFlow = false

Your application can bootstrap itself by fetching all parameters under /myapp/{environment} in a single request, then initialize configuration from that tree. This approach keeps your codebase environment-agnostic and lets operations teams modify configuration without redeploying.

Parameter Store also stores simple secrets fine—just don't expect rotation. A database password stored in Parameter Store is encrypted at rest with KMS, encrypted in transit over HTTPS, and logged to CloudTrail. It's secure. It's just not automatically rotated.

Retrieving parameters by path is powerful:

bash
aws ssm get-parameters-by-path \
  --path /myapp/production \
  --recursive \
  --with-decryption \
  --region us-east-1

This returns all parameters under that path, decrypting SecureString types automatically. Very efficient for bootstrapping.

Inject at Runtime: ECS and Lambda Integration

Both services integrate cleanly with ECS and Lambda, letting you inject secrets into environment variables or files at runtime—no hardcoding, no configuration management overhead.

ECS task definitions support a special syntax for both Secrets Manager and Parameter Store:

bash
{
  "name": "DB_PASSWORD",
  "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/postgres-password:password::"
}

When the task starts, ECS fetches the secret from Secrets Manager and injects it as an environment variable. The task's IAM role must have permission to access that secret. This works identically for Parameter Store—just use the parameter ARN instead.

Lambda functions can fetch secrets at initialization time or during invocation. Here's a typical pattern in Node.js:

bash
const AWS = require('aws-sdk');
const ssm = new AWS.SSM();

let config;

exports.handler = async (event) => {
  if (!config) {
    const params = await ssm.getParametersByPath({
      Path: '/myapp/production',
      Recursive: true,
      WithDecryption: true
    }).promise();
    
    config = {};
    params.Parameters.forEach(p => {
      config[p.Name.split('/').pop()] = p.Value;
    });
  }
  
  // Use config
  return { statusCode: 200, body: 'OK' };
};

I'd recommend fetching at initialization (outside the handler) because Lambda containers are reused. Fetch once per cold start, reuse the connection on warm starts. This saves API calls and reduces latency.

One practical detail: always use WithDecryption: true for SecureString parameters or encrypted secrets. Without it, you'll get the encrypted blob, not the plaintext.

Common Mistakes

Mistake 1: Using Secrets Manager for everything. I've seen teams store environment configuration (like database hostnames) in Secrets Manager because "security." The cost adds up, and Parameter Store handles this case better. Reserve Secrets Manager for credentials that genuinely need rotation or are sensitive enough to warrant the extra cost.

Mistake 2: Forgetting to grant IAM permissions. Your ECS task or Lambda function needs an IAM role with secretsmanager:GetSecretValue or ssm:GetParameter permissions scoped to the secrets/parameters it actually needs. I've deployed tasks that silently fail to fetch secrets because the role was missing permissions. Always check CloudWatch logs for "AccessDenied" errors.

Mistake 3: Fetching secrets inside the request handler on every invocation. Lambda containers are reused. Fetch secrets at initialization time (module scope in Node.js, static fields in C#), then reuse them. This cuts API calls and latency.

Mistake 4: Not versioning your parameters. Parameter Store doesn't auto-version like Secrets Manager does. If you're making frequent changes to a critical parameter, enable versioning manually or use a naming convention like /app/database/host/v1 so you can roll back if needed.

Mistake 5: Assuming rotation works automatically without testing. If you set up Secrets Manager rotation, actually test the rotation before deploying to production. The rotation Lambda needs database access, network connectivity, and permission to both read and update the secret. I've had rotation fail silently because the Lambda couldn't reach the database.

Key Takeaways

  • Secrets Manager is for credentials that need automatic rotation: database passwords, API keys that expire, RDS credentials. Accept the $0.40/month per secret cost for the rotation convenience and compliance posture.
  • Parameter Store is for configuration and simple secrets: hostnames, feature flags, non-rotating API keys, environment-specific settings. Use the hierarchical paths to organize by environment and component.
  • Both services encrypt at rest with KMS, encrypt in transit with HTTPS, and integrate seamlessly with ECS task definitions and Lambda. Choose based on rotation needs and cost, not security features alone.
  • Fetch secrets and parameters at Lambda initialization time, not in the handler. Reuse the data on warm starts to reduce API calls and latency.
  • Always verify IAM role permissions. "AccessDenied" errors on secret/parameter fetches are silent in most applications—they just show up in CloudWatch logs.
  • Test rotation in a staging environment before enabling it on production secrets. Rotation failures are easy to miss and can cascade into application errors.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles