My default · AWS · Application secrets

AWS Secrets Manager

Where every client's application secrets live. Provisioned with the app, read at runtime, never in code, config or a person's vault.

Every client secret I manage lives in Secrets Manager: database passwords, API keys, webhook secrets, third-party tokens. Created by Pulumi with the rest of the stack, read by the application at runtime, encrypted with the client's own KMS key and logged in CloudTrail every time something reads it. Nothing in the repository, nothing in environment files, nothing in someone's password manager.

That last point is the rule I hold to. There are two kinds of secret, and they belong in different places:

Secrets Manager Parameter Store (SecureString) A password manager
For Application secrets Configuration, low-risk values People's logins
Price $0.40 a secret a month, $0.05 per 10,000 reads Free (standard tier) Per user
Rotation Built in, automatic for RDS, Aurora and DocumentDB None By hand
Shared across AWS accounts Yes, with a customer managed key Advanced tier only n/a
Copied to another Region Yes No n/a
Max size 64 KB 4 KB (8 KB advanced) n/a

At 40 cents a secret, the bill is noise next to what it buys: a secret that rotates without a deploy, an audit trail of every read, and nothing to clean up when a person leaves the project. A typical client app has a dozen secrets, so a few dollars a month.

It also matters more now that AI agents work on client systems. When the code reads a secret at runtime, the value never has to appear in a prompt, a file the agent opens, or a log it reads. The agent can deploy and debug the system without ever seeing a password.

The traps are all about the gap between the secret and the code that reads it: containers that keep a rotated password until they restart, default keys that can't be shared, reads that aren't cached. Each one has a standard fix, listed below.

Use it when

  • Every secret an application uses: database passwords, API keys, webhook signing secrets, third-party tokens.
  • Database credentials that should rotate on their own: RDS, Aurora and DocumentDB master passwords rotate with no Lambda to write.
  • Secrets shared across a client's AWS accounts or copied to Melbourne for disaster recovery.
  • AI agents working on client systems: the code reads the secret at runtime, so the value never needs to appear in a prompt, a file or a log.

Don't use it when

  • Plain configuration that isn't secret, or a low-risk value you rotate by hand: Parameter Store's standard tier is free.
  • People's logins to consoles, registrars and admin panels: those belong in a password manager.

Pricing

Sydney: $0.40 per secret a month (charged by the hour), $0.05 per 10,000 API calls. Replicas are billed as separate secrets. Rotation and managed external secrets cost nothing extra.

Gotchas, with fixes

Gotcha: After a rotation, ECS tasks and long-running processes keep using the old password: ECS injects secrets only when a container starts.

Fix: Use the alternating-users rotation strategy for app users, retry once with a fresh read on an authentication failure, and redeploy from the EventBridge event Secrets Manager sends on every update.

Gotcha: A secret encrypted with the default aws/secretsmanager key can't be shared with another account, and switching keys later is painful.

Fix: Create every secret on a customer managed KMS key from day one.

Gotcha: Reading the secret on every Lambda invocation multiplies API calls by concurrency: cost, and throttling at scale.

Fix: Use the Parameters and Secrets Lambda Extension (cache up to 300 seconds) or Powertools with a cache TTL, or BatchGetSecretValue for many secrets at once.

Gotcha: Deleted secrets wait 7 to 30 days before they're gone, and the name can't be reused meanwhile, so tearing down and recreating a stack fails.

Fix: Use a recovery window of 0 for development stacks only, and keep the default window in production.

Gotcha: The value ends up in plaintext anyway: in a Lambda environment variable, a CloudFormation template or a commit.

Fix: Pass references, not values (valueFrom in ECS, {{resolve:secretsmanager:...}} in CloudFormation), and read at runtime.

Provisioning

Official Pulumi package · Official Terraform provider

@pulumi/aws secretsmanager.Secret and SecretVersion. Pulumi encrypts the value in state; secretStringWo (write-only) keeps it out of state entirely. Take the value from pulumi config --secret or a generator, never from code.

Instead, consider

Where it fits

Sources

Last reviewed by Nicolas Dao

Want this stack in your AWS account?

Cloudless Labs is a Sydney consultancy. We migrate on-premises systems to AWS, build serverless apps on this stack, and coach teams to ship with Claude Code and Codex.

Contact Us

For general inquiries, please contact us at:

For e-learning capabilities, please contact: