How to Set Up AWS Secrets Manager: 13 Steps, 80 Min [2026]

In 2024 alone, security researchers at GitGuardian found 23,770,171 new hardcoded secrets dumped into public GitHub repositories, according to the company’s State of Secrets Sprawl 2025 report. Verizon’s 2025 Data Breach Investigations Report puts a finer point on why that matters: 22% of breaches now involve stolen or compromised credentials, per the 2025 DBIR. Database passwords in a .env file, API keys pasted into a Slack message, a Terraform state file checked into a private repo that quietly turned public — this is how most breaches actually start.

AWS Secrets Manager is Amazon’s answer to that problem: a managed vault that stores, encrypts, rotates, and audits access to credentials so they never have to live in code or config files again. This tutorial walks through setting it up from a blank AWS account to a production-ready configuration with automatic rotation, Lambda and ECS integration, Kubernetes sync, and infrastructure-as-code — in 13 steps, roughly 80 minutes. By the end you’ll also know exactly what it costs, how it compares to Parameter Store, Azure Key Vault, and HashiCorp Vault, and how to avoid the mistakes that generate the angriest AWS bills.

Google · Preferred Sources

Don't miss new tech stories on Google

Add Tech Insider once in the Google app and our stories appear in your news suggestions.

Add Now

What Is AWS Secrets Manager, and Why It Matters in 2026

AWS Secrets Manager is a managed service for storing and retrieving credentials, API keys, tokens, and other sensitive values without hardcoding them into application source, container images, or configuration files. Every secret is encrypted at rest with AWS Key Management Service (KMS), versioned automatically, and accessible only to IAM principals explicitly granted permission. That last part matters more than it sounds — the biggest practical difference between “a secret stored somewhere” and “a secret managed properly” is that every read is authenticated, authorized, and logged.

Under the hood, Secrets Manager stores each secret as an encrypted blob (typically JSON, but it accepts any string or binary payload up to 65,536 bytes) with a set of versions attached to it. Each version carries staging labels — AWSCURRENT, AWSPENDING, and AWSPREVIOUS — that let a rotation function stage a new credential before cutting traffic over to it, so an application never sees a half-rotated secret. An AWS account can hold up to 500,000 secrets per Region, and the GetSecretValue API is rated for 10,000 requests per second per Region, which is generous enough that throttling is almost always a caching problem rather than a real ceiling (source: AWS Secrets Manager quotas).

What makes AWS Secrets Manager worth the setup time in 2026 specifically is the rotation piece. Static, never-rotated database passwords are one of the most common findings in cloud security audits, and Secrets Manager ships pre-built Lambda rotation templates for Amazon RDS (MySQL, PostgreSQL, Oracle, and SQL Server engines), Redshift, DocumentDB, Neptune, and MongoDB Atlas, plus a generic HTTP endpoint template for anything else (source: AWS rotation documentation). That turns “rotate credentials every 90 days” from a recurring engineering chore into a scheduled Lambda invocation.

There’s also a compliance angle that pushes teams toward Secrets Manager specifically rather than a homegrown solution. SOC 2, PCI-DSS, and most internal security reviews now expect evidence that credentials rotate on a schedule and that access to them is logged per-principal, not just per-application. A managed service that generates that audit trail automatically — every GetSecretValue call, every rotation event, tied to a specific IAM identity in CloudTrail — is a much easier story to tell an auditor than “we trust the deploy script.”

This guide assumes you’re setting up AWS Secrets Manager for a real application — not just clicking through the console once. We’ll build a least-privilege IAM policy first, then work through the CLI, SDK, rotation, cross-account access, replication, and integration with Lambda, ECS/Fargate, and EKS, before finishing with Terraform, monitoring, and cleanup.

Prerequisites: What You Need Before You Start

You don’t need every tool below to follow along — skip the Kubernetes prerequisites if you’re not doing Step 10, for instance. But for the full walkthrough, have these ready:

  • An AWS account with billing enabled and administrator access to create IAM policies (you’ll scope down to least privilege in Step 1)
  • AWS CLI v2 (latest 2.x release) installed and configured with aws configure
  • Python 3.9 or later with the boto3 SDK (latest version via pip install boto3) for the programmatic-access step
  • Node.js 18 LTS or later for the complete working project later in this guide
  • Terraform 1.x (latest stable release) if you plan to follow the infrastructure-as-code step
  • kubectl and Helm 3, plus an existing EKS cluster, only if you’re following the Kubernetes integration step — see our Amazon EKS setup guide if you need one
  • Basic familiarity with JSON, IAM policy syntax, and the command line
  • Budget: plan for a few dollars if you follow every step and leave resources running for a day or two; the pricing breakdown below shows exactly where that comes from

Time estimate: about 80 minutes for all 13 steps, longer if you’re also standing up a fresh RDS instance or EKS cluster to rotate against.

AWS Secrets Manager Pricing in 2026: What It Actually Costs

AWS Secrets Manager has no permanent free tier — new AWS customers get up to $200 in Free Tier credits during their first 6 months, and after that you’re billed $0.40 per secret per month plus $0.05 per 10,000 API calls, prorated hourly for partial months (source: AWS Secrets Manager pricing). That’s the number most teams get surprised by: fifteen secrets across a handful of environments comes out to $6/month before you’ve made a single API call, and cross-region replicas are billed as separate secrets at the same rate.

The obvious cheaper alternative is AWS Systems Manager Parameter Store, which is genuinely free for standard-tier parameters with no call charge under standard throughput. The catch is capacity: standard parameters cap out at 4 KB and 10,000 parameters per account per Region, with no rotation, no resource policies, and no cross-account sharing. Advanced parameters lift those limits to 8 KB and 100,000 parameters and add policies and cross-account sharing — but then you’re paying $0.05 per parameter per month plus $0.05 per 10,000 API calls, which is where a lot of the pricing gap with Secrets Manager narrows (source: AWS Systems Manager pricing, Parameter Store tier documentation).

ServicePrice per secret/version per monthPrice per 10,000 API callsFree tierNative rotation
AWS Secrets Manager$0.40$0.0530-day trial (new secrets only)Yes — RDS, Redshift, DocumentDB, Neptune, MongoDB Atlas
AWS Parameter Store (Standard)$0.00$0.00 (standard throughput)Permanently free, up to 10,000 paramsNo
AWS Parameter Store (Advanced)$0.05$0.05NoneNo
Azure Key Vault (Standard)Included in transaction fee$0.03 per 10,000 transactionsNone publishedDepends on integration
Google Cloud Secret Manager$0.06 per active version$0.036 versions + 10,000 ops/monthNo (app-managed)
HashiCorp Vault (Open Source)$0.00 (self-hosted)$0.00 (self-hosted)Fully free, self-managedYes — dynamic secrets engines

Sources: AWS, AWS Systems Manager, Microsoft Azure, Google Cloud. HashiCorp’s managed HCP Vault adds a separate subscription cost on top of the free open-source core; check HashiCorp’s current pricing page for your deployment size before budgeting.

Run the numbers on a mid-size application: three environments (dev, staging, production), each with a database credential, an API key for a payment processor, and an OAuth client secret — nine secrets total. At $0.40/month plus a modest 50,000 API calls monthly across all environments, that’s $3.60 in per-secret charges plus $0.25 in API charges, or roughly $3.85/month. Add production replicated to a second Region for disaster recovery (three more secrets) and you’re at just over $5/month. That’s a trivial line item next to the cost of a single leaked production database password, which is exactly the trade-off AWS is pricing for.

In practice, the right call is usually both: Parameter Store standard tier for configuration values and feature flags that don’t need rotation, Secrets Manager for anything that touches a database, API key, or credential that should rotate on a schedule.

Step 1: Create an IAM Policy for Least-Privilege Secrets Access

Start with permissions, not the secret itself. The single most common AWS Secrets Manager misconfiguration is a policy that grants secretsmanager:GetSecretValue on Resource: "*" — every secret in the account, to every principal that happens to have that policy attached. Scope it to a path prefix and a resource tag instead:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowReadProductionAppSecrets",
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/app/*",
      "Condition": {
        "StringEquals": {
          "secretsmanager:ResourceTag/Environment": "production"
        }
      }
    }
  ]
}

Save that as secrets-read-policy.json and create it:

aws iam create-policy \
    --policy-name ProdAppSecretsReadOnly \
    --policy-document file://secrets-read-policy.json

Attach this to the IAM role your application or Lambda function actually runs as — not to individual developer users. Developers who need to create or rotate secrets should get a separate, broader policy that’s still scoped to a path prefix (prod/app/*, staging/app/*) rather than the whole account.

Step 2: Create Your First Secret in the AWS Console

Open the Secrets Manager console and choose Store a new secret. For a database credential, select Credentials for a database, choose the database engine, and enter the username and password. If you’re storing something that isn’t a database credential — an API key, an OAuth client secret — choose Other type of secret and add key-value pairs directly.

Name the secret with a path-style prefix that matches the IAM policy you just wrote — prod/app/db-credentials, for example. This isn’t cosmetic: it’s what makes path-prefix IAM conditions and cost-tracking-by-tag actually work later. On the next screen, choose whether to enable automatic rotation now (we’ll configure this properly in Step 5, so it’s fine to skip it here) and pick the KMS key to encrypt the secret — the account’s default aws/secretsmanager key is fine for most setups, or choose a customer-managed key if you need separate audit trails or cross-account key policies.

Review and store the secret. AWS immediately shows you sample code snippets in Python, Java, JavaScript, .NET, PHP, Ruby, and Go for retrieving that exact secret — worth copying since it saves you from typos in the ARN.

Step 3: Create and Retrieve Secrets with the AWS CLI

For anything beyond a one-off, the CLI is faster than clicking through the console. Create a secret with a full JSON payload in one command:

aws secretsmanager create-secret \
    --name prod/app/db-credentials \
    --description "Production database credentials for app" \
    --secret-string '{"username":"appuser","password":"CHANGE-ME","host":"mydb.abc123.us-east-1.rds.amazonaws.com","port":5432,"dbname":"appdb"}'

AWS returns the ARN, name, and version ID of the new secret:

{
    "ARN": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/app/db-credentials-a1B2c3",
    "Name": "prod/app/db-credentials",
    "VersionId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
}

Notice the ARN has a random six-character suffix appended to the name. Don’t hardcode that full ARN anywhere — if you ever force-delete and recreate the secret, the suffix changes and every hardcoded reference breaks. Reference secrets by name wherever the API accepts it.

To read it back:

aws secretsmanager get-secret-value \
    --secret-id prod/app/db-credentials \
    --query SecretString \
    --output text
{"username":"appuser","password":"CHANGE-ME","host":"mydb.abc123.us-east-1.rds.amazonaws.com","port":5432,"dbname":"appdb"}

Pipe that through jq to pull a single field (jq -r .password) in shell scripts and CI pipelines.

Step 4: Retrieve Secrets Programmatically with Python (boto3)

Application code should never shell out to the CLI. Use the SDK directly, and cache the result — every uncached call to GetSecretValue is billed and counts against the 10,000 TPS per-Region rate limit:

import boto3
import json
from botocore.exceptions import ClientError

def get_secret(secret_name, region_name="us-east-1"):
    session = boto3.session.Session()
    client = session.client(service_name="secretsmanager", region_name=region_name)
    try:
        response = client.get_secret_value(SecretId=secret_name)
    except ClientError as e:
        raise e
    return json.loads(response["SecretString"])

db_creds = get_secret("prod/app/db-credentials")
print(db_creds["username"])

For anything that calls this on every request instead of once at startup or on a timer, wrap it with an in-memory cache (a simple TTL dict, or functools.lru_cache with manual invalidation) so you’re not paying $0.05 per 10,000 calls for a value that changes maybe once a quarter.

Step 5: Enable Automatic Rotation with a Lambda Rotation Function

This is the feature that separates Secrets Manager from “an encrypted key-value store.” For supported engines — RDS MySQL, PostgreSQL, Oracle, SQL Server, plus Redshift, DocumentDB, Neptune, and MongoDB Atlas — AWS provides ready-made Lambda rotation function templates you deploy from the Serverless Application Repository directly in the console’s Edit rotation screen, or reference by ARN via CLI:

aws secretsmanager rotate-secret \
    --secret-id prod/app/db-credentials \
    --rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:SecretsManagerRDSPostgreSQLRotationSingleUser \
    --rotation-rules AutomaticallyAfterDays=30

Under the hood, rotation runs a four-step Lambda invocation: createSecret generates a new password and stages it as AWSPENDING, setSecret applies it to the actual database, testSecret verifies the new credential works, and finishSecret flips the AWSCURRENT label to the new version. If any step fails, the old AWSCURRENT version stays active and your application never sees a broken credential — which is exactly why you should test the rotation function against a non-production secret first. A rotation Lambda with the wrong VPC configuration (no route to the database, or missing a NAT gateway/VPC endpoint to reach the Secrets Manager API) will fail silently until the next scheduled run and leave the secret stuck mid-rotation.

Step 6: Set a Resource Policy for Cross-Account Access

If a second AWS account — a shared services account, a CI/CD pipeline account — needs to read this secret, attach a resource policy instead of copying the secret into both accounts:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::999988887777:role/cross-account-reader"
      },
      "Action": "secretsmanager:GetSecretValue",
      "Resource": "*"
    }
  ]
}
aws secretsmanager put-resource-policy \
    --secret-id prod/app/db-credentials \
    --resource-policy file://resource-policy.json

Resource policies are capped at 20,480 characters, which is generous enough for dozens of cross-account principals on a single secret. The account making the request (999988887777 in this example) still needs a matching identity-based policy on its side — a resource policy alone doesn’t grant access, it only permits it.

Step 7: Turn On Cross-Region Replication

For disaster recovery or multi-region active-active applications, replicate the secret to a second Region:

aws secretsmanager replicate-secret-to-regions \
    --secret-id prod/app/db-credentials \
    --add-replica-regions Region=us-west-2

Replicas are read-only copies that sync automatically whenever the primary secret changes. Remember the pricing table above: each replica bills as its own secret at $0.40/month, so replicating twenty secrets to a second Region quietly doubles that line item on your bill. You also can’t delete a primary secret while replicas exist — remove the replicas first with remove-regions-from-replication, then delete the primary.

Step 8: Inject Secrets into AWS Lambda Functions

Lambda doesn’t have a native “secrets” field in its configuration the way ECS does, so the function’s execution role needs secretsmanager:GetSecretValue and calls the SDK directly, ideally once per cold start rather than per invocation:

import boto3, json, os

_secrets_client = boto3.client("secretsmanager")
_cached_secret = None

def get_db_credentials():
    global _cached_secret
    if _cached_secret is None:
        response = _secrets_client.get_secret_value(SecretId="prod/app/db-credentials")
        _cached_secret = json.loads(response["SecretString"])
    return _cached_secret

def handler(event, context):
    creds = get_db_credentials()
    # module-level cache persists across warm invocations of the same execution environment
    return {"statusCode": 200, "body": "connected as " + creds["username"]}

The module-level cache trick works because Lambda reuses the execution environment across warm invocations, so _cached_secret only gets populated on a cold start. If you’d rather not manage caching yourself, the AWS Parameters and Secrets Lambda Extension runs a local HTTP cache alongside your function and handles TTL invalidation automatically. If we haven’t covered building the surrounding API yet, see our AWS Lambda API tutorial for the full setup.

Step 9: Inject Secrets into ECS and Fargate Task Definitions

ECS and Fargate, unlike Lambda, support secrets natively at the task definition level — the container gets the value injected as an environment variable at launch, with no SDK call in your application code at all. Reference the specific JSON key you need with the :key:: suffix on the ARN:

{
  "containerDefinitions": [
    {
      "name": "app",
      "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/app:latest",
      "secrets": [
        {
          "name": "DB_PASSWORD",
          "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/app/db-credentials:password::"
        },
        {
          "name": "DB_USERNAME",
          "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/app/db-credentials:username::"
        }
      ]
    }
  ]
}

The task execution role (not the task role) needs secretsmanager:GetSecretValue for this ARN — that’s a common point of confusion since ECS has two separate IAM roles with different jobs. Omit the :password:: suffix and ECS injects the entire JSON blob as a single environment variable string instead of one field, which is rarely what you want. See our ECS vs EKS vs Fargate comparison if you’re still deciding which compute layer to run this on.

Step 10: Sync Secrets to Kubernetes with the External Secrets Operator

If your workloads run on EKS, the cleanest pattern is the open-source External Secrets Operator, which syncs AWS Secrets Manager values into native Kubernetes Secrets on a refresh interval, authenticating via IAM Roles for Service Accounts (IRSA) instead of long-lived static credentials:

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets-store
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-store
    kind: SecretStore
  target:
    name: db-credentials
  data:
    - secretKey: password
      remoteRef:
        key: prod/app/db-credentials
        property: password

Install the operator with Helm, create the IRSA-linked service account referenced above, apply the two manifests, and Kubernetes pods can mount db-credentials as a normal Secret volume with no awareness that AWS Secrets Manager exists behind it. Need the cluster itself first? Our Amazon EKS setup guide covers that from scratch.

Step 11: Manage Secrets as Code with Terraform

Clicking through the console doesn’t scale past a handful of secrets. The aws_secretsmanager_secret, aws_secretsmanager_secret_version, and aws_secretsmanager_secret_rotation resources in the official Terraform AWS provider let you define secrets, their values, and rotation schedules alongside the rest of your infrastructure:

resource "aws_secretsmanager_secret" "db_credentials" {
  name        = "prod/app/db-credentials"
  description = "Production database credentials for app"

  tags = {
    Environment = "production"
  }
}

resource "aws_secretsmanager_secret_version" "db_credentials" {
  secret_id = aws_secretsmanager_secret.db_credentials.id
  secret_string = jsonencode({
    username = "appuser"
    password = var.db_password
    host     = aws_db_instance.app.address
    port     = 5432
    dbname   = "appdb"
  })
}

resource "aws_secretsmanager_secret_rotation" "db_credentials" {
  secret_id           = aws_secretsmanager_secret.db_credentials.id
  rotation_lambda_arn = aws_lambda_function.rotator.arn

  rotation_rules {
    automatically_after_days = 30
  }
}

Mark var.db_password as a sensitive variable so Terraform redacts it from plan and apply output, and keep the actual value out of your .tfvars file entirely — pull it from a data source or generate it with the random_password resource on first apply instead. If you’re new to the tool itself, start with our Terraform on AWS tutorial before layering secrets on top.

Step 12: Turn On CloudTrail Logging and a CloudWatch Alarm

Every Secrets Manager API call — GetSecretValue included — generates a CloudTrail event, but GetSecretValue is logged as a data event, which most accounts don’t capture by default (management events alone won’t show you who actually read a secret’s value). Enable data event logging for Secrets Manager on your trail, then build a metric filter that watches for reads of a specific secret:

aws logs put-metric-filter \
    --log-group-name CloudTrail/SecretsManagerEvents \
    --filter-name SecretRetrievedFilter \
    --filter-pattern '{ ($.eventName = "GetSecretValue") && ($.requestParameters.secretId = "prod/app/db-credentials") }' \
    --metric-transformations \
        metricName=DeletedSecretAccessed,metricNamespace=CustomSecretsManager,metricValue=1

This pattern is exactly what AWS recommends for catching access to a secret that’s scheduled for deletion — useful both as a safety net during Step 13 and as an ongoing anomaly signal if a secret gets read from somewhere it shouldn’t be. Point a CloudWatch alarm at the resulting DeletedSecretAccessed metric and route it to SNS. If you’re already running AWS CloudTrail for other services, this is just one more trail configuration, not a new pipeline.

Step 13: Clean Up — Delete Secrets Safely

Secrets Manager deliberately makes deletion hard to reverse by accident. A standard delete schedules the secret for removal after a recovery window of at least 7 days — the secret is immediately inaccessible to applications, but you can still bring it back:

aws secretsmanager delete-secret \
    --secret-id prod/app/db-credentials \
    --recovery-window-in-days 7
aws secretsmanager restore-secret --secret-id prod/app/db-credentials

For test secrets you’re certain nothing depends on, skip the recovery window entirely:

aws secretsmanager delete-secret \
    --secret-id staging/app/scratch-secret \
    --force-delete-without-recovery

There’s no charge for secrets in the recovery window, so leaving the default 7-day buffer costs nothing and gives you an escape hatch. If the secret has replicas in other Regions, remove those first with remove-regions-from-replication — Secrets Manager won’t let you delete a primary secret that still has active replicas.

AWS Secrets Manager vs Parameter Store vs Azure Key Vault vs GCP Secret Manager vs Vault

The pricing table above answers “what does it cost.” This table answers “which one should I actually use,” since cost is rarely the deciding factor once you need rotation or multi-cloud support.

FeatureAWS Secrets ManagerAWS Parameter StoreHashiCorp Vault
Max value size65,536 bytes4 KB standard / 8 KB advancedConfigurable, no hard cap
Native rotationYes, built-in Lambda templatesNoYes, dynamic secrets engines
Cross-account sharingResource policiesAdvanced tier onlyNamespaces / auth methods
Cross-region replicationBuilt-in, billed per replicaManual (copy/sync)Manual (enterprise replication add-on)
Multi-cloud supportAWS onlyAWS onlyAWS, Azure, GCP, on-prem
Best fitRotating credentials, production databasesStatic config, feature flagsMulti-cloud, dynamic short-lived credentials

Azure Key Vault and Google Cloud Secret Manager follow roughly the same shape as AWS’s two services combined into one product each — both support secrets, keys, and certificates in a single service, and both charge per-operation rather than splitting a free config-storage tier from a paid secrets tier the way AWS does. If you’re running workloads across AWS and Azure already, our AWS vs Azure comparison covers the broader platform differences beyond just secrets. And if you’d rather self-host something simpler than Vault for smaller teams or personal infrastructure, Vaultwarden is a lighter-weight, open-source option built specifically for credential storage rather than infrastructure secrets.

The decision usually comes down to how many clouds you actually run. Single-cloud AWS shops get the most value from Secrets Manager because IAM, KMS, and CloudTrail integration are already there with zero extra plumbing. Teams split across AWS, Azure, and on-prem infrastructure tend to standardize on HashiCorp Vault specifically to avoid maintaining three separate rotation and access-control models — the operational cost of running Vault yourself is traded for a single consistent interface everywhere. There’s no universally “correct” answer here; it’s a build-versus-buy decision scoped to how many platforms your secrets actually need to live on.

Complete Working Project: A Node.js API That Loads Database Credentials at Runtime

Here’s everything assembled into a minimal but complete Express API that fetches its database credentials from Secrets Manager on startup, caches them in memory, and exposes a health check that confirms the connection actually works. Start a new project:

mkdir secrets-demo-api && cd secrets-demo-api
npm init -y
npm install express pg @aws-sdk/client-secrets-manager

Create secrets.js to handle retrieval and caching:

const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");

const client = new SecretsManagerClient({ region: "us-east-1" });
let cachedSecret = null;

async function getDbCredentials() {
  if (cachedSecret) return cachedSecret;

  const command = new GetSecretValueCommand({ SecretId: "prod/app/db-credentials" });
  const response = await client.send(command);
  cachedSecret = JSON.parse(response.SecretString);
  return cachedSecret;
}

module.exports = { getDbCredentials };

Then index.js, which loads the credentials once at startup and reuses a single connection pool for every request:

const express = require("express");
const { Pool } = require("pg");
const { getDbCredentials } = require("./secrets");

const app = express();
let pool;

async function start() {
  const creds = await getDbCredentials();

  pool = new Pool({
    host: creds.host,
    port: creds.port,
    user: creds.username,
    password: creds.password,
    database: creds.dbname,
    max: 10,
  });

  app.get("/health", async (req, res) => {
    try {
      await pool.query("SELECT 1");
      res.json({ status: "ok", db: "connected" });
    } catch (err) {
      res.status(500).json({ status: "error", message: err.message });
    }
  });

  app.listen(3000, () => console.log("API listening on port 3000"));
}

start().catch((err) => {
  console.error("Failed to start: could not load secrets", err);
  process.exit(1);
});

Run it with node index.js, then confirm it against your actual secret:

curl http://localhost:3000/health
{"status":"ok","db":"connected"}

The IAM role or user running this process needs secretsmanager:GetSecretValue on that specific secret ARN — reuse the policy from Step 1. Deploy this same pattern to ECS with the Step 9 task definition (skip the SDK call entirely and read from process.env.DB_PASSWORD instead, since ECS injects it directly) or to Lambda with the Step 8 caching pattern.

Common Pitfalls When Setting Up AWS Secrets Manager

Most of the mistakes teams make with AWS Secrets Manager aren’t dramatic security failures — they’re small configuration choices that either inflate the bill or create an outage during rotation. In rough order of how often they show up:

  • Hardcoding the full secret ARN, suffix included. The six-character random suffix changes if you ever force-delete and recreate a secret with the same name. Reference secrets by name, not by pinned ARN, in application config.
  • Calling GetSecretValue on every request instead of caching. This is both a cost problem ($0.05 per 10,000 calls adds up under real traffic) and a reliability problem — you’re now dependent on the Secrets Manager API being available for every single request instead of once at startup.
  • Using Resource: "*" in IAM policies out of convenience. It’s faster to write during setup and much harder to audit six months later when you’re trying to figure out which of forty roles can read a production database password.
  • Storing non-secret configuration in Secrets Manager. Feature flags, non-sensitive environment names, and public configuration values belong in Parameter Store’s free standard tier. Paying $0.40/month per value for things that aren’t actually secret is a common source of bill creep.
  • Deploying a rotation Lambda without testing it against a non-production secret first. A rotation function with the wrong VPC subnet, a missing NAT gateway, or insufficient database permissions can leave a secret stuck mid-rotation, and you won’t find out until the scheduled rotation window.
  • Forgetting cross-region replicas are billed separately. Replicating fifteen secrets to a second Region for disaster recovery adds another $6/month, not a discounted “backup” rate.
  • Omitting the JSON key suffix in ECS valueFrom ARNs. Without :password:: at the end, ECS injects the entire secret JSON blob as one environment variable string instead of the single field you wanted.

Troubleshooting AWS Secrets Manager: 10 Common Errors and Fixes

These are the errors and symptoms that come up most often once you move past the console basics and into real integrations. Most trace back to one of three root causes: an IAM policy that’s missing a permission on one side of the call, a KMS key policy that wasn’t updated when the IAM policy was, or a networking path (VPC, NAT gateway, security group) that a Lambda or ECS task can’t actually reach. Check those three first before assuming the service itself is misbehaving.

Error or symptomLikely causeFix
AccessDeniedException on GetSecretValueIAM identity-based policy missing the action, or resource policy doesn’t grant itCheck both the caller’s IAM policy and the secret’s resource policy; both must allow the call
ResourceNotFoundExceptionSecret deleted, still in recovery window, or you’re querying the wrong RegionConfirm the Region in your client matches where the secret lives; restore if it’s in the recovery window
DecryptionFailureIAM principal lacks kms:Decrypt on the KMS key used to encrypt the secretAdd kms:Decrypt permission for that specific key, not just secretsmanager:GetSecretValue
ThrottlingExceptionExceeding the 50 TPS write quota, or calling PutSecretValue faster than once every 10 minutesAdd exponential backoff and retries; batch updates instead of calling PutSecretValue repeatedly
Rotation stuck “in progress”Rotation Lambda timed out, has no network path to the database, or lacks database permissionsCheck Lambda CloudWatch Logs for the specific step (createSecret/setSecret/testSecret/finishSecret) that failed
Secret value is empty or malformed in ECS containerMissing JSON key suffix on valueFrom, or wrong IAM role given permissionsAdd the :key:: suffix to the ARN; verify the task execution role (not the task role) has GetSecretValue
“A secret with this name already exists” on createA previous secret with the same name is still inside its recovery windowRestore and reuse it, wait out the recovery window, or force-delete it first if it’s disposable
External Secrets Operator shows SecretSyncedErrorIRSA misconfigured — missing OIDC trust policy or wrong service account annotationVerify the EKS cluster’s OIDC provider is registered and the IAM role trust policy matches the service account
Unexpectedly high monthly billOrphaned test/scratch secrets never cleaned up, each billed at $0.40/month regardless of useRun ListSecrets periodically and tag everything with an owner and environment for auditing
CloudTrail shows no GetSecretValue eventsOnly management events are enabled; GetSecretValue is a data eventEnable data event logging for Secrets Manager specifically on your CloudTrail trail

Advanced Tips for Production-Grade Secrets Management

Once the basics are running, these are the changes that separate a “it works” setup from one that holds up under audit and scale:

  • Move from per-secret IAM policies to attribute-based access control (ABAC). Tag every secret with Environment, Team, and App, then write one policy per team that references secretsmanager:ResourceTag/* conditions instead of maintaining a growing list of hardcoded ARNs.
  • Use VPC interface endpoints (PrivateLink) for Secrets Manager. Lambda and ECS tasks in private subnets can reach the Secrets Manager API without a NAT gateway or any path to the public internet, which is both cheaper and reduces the network attack surface.
  • Deploy the AWS Parameters and Secrets Lambda Extension instead of hand-rolling a caching layer — it runs a local HTTP server inside the Lambda execution environment and handles TTL-based cache invalidation for you.
  • Design applications around staging labels, not raw values. Reading AWSCURRENT explicitly (rather than assuming “whatever GetSecretValue returns”) makes zero-downtime rotation actually zero-downtime, since your app and the rotation Lambda agree on what “current” means.
  • Feed Secrets Manager into AWS Config or Security Hub. A custom Config rule that flags secrets without rotation enabled, or without a rotation event in the last 90 days, turns “we should rotate credentials” into something a dashboard actually tracks.
  • Centralize secrets in a dedicated security account for multi-account organizations, and share into workload accounts via resource policies plus AWS Resource Access Manager, instead of duplicating the same database credential in five accounts with five separate rotation schedules to keep in sync.

Frequently Asked Questions

Is AWS Secrets Manager free?

No, not on an ongoing basis. New secrets get a 30-day free trial, and after that AWS bills $0.40 per secret per month plus $0.05 per 10,000 API calls (source: AWS pricing). For static configuration that doesn’t need rotation, AWS Systems Manager Parameter Store’s standard tier is permanently free.

What’s the real difference between AWS Secrets Manager and Parameter Store?

Native automatic rotation, cross-region replication, and resource-based policies for cross-account access are all exclusive to Secrets Manager. Parameter Store’s advantage is the standard tier being free indefinitely, at the cost of no rotation and lower size and count limits (4 KB, 10,000 parameters) unless you upgrade to the paid advanced tier (8 KB, 100,000 parameters).

What is the maximum size of a secret in AWS Secrets Manager?

65,536 bytes (64 KB) per secret value, per the official AWS Secrets Manager quotas documentation. That’s enough for a JSON blob with dozens of fields, but not for large certificate bundles or binary files — use S3 with a Secrets Manager pointer for those.

Can I use AWS Secrets Manager with Kubernetes?

Yes, most commonly through the open-source External Secrets Operator (covered in Step 10), which syncs values into native Kubernetes Secrets on a refresh interval. The AWS Secrets and Configuration Provider (ASCP) for the Secrets Store CSI Driver is the other common route, mounting secrets as files directly into pods instead of creating a Kubernetes Secret object.

Which databases support automatic rotation out of the box?

Amazon RDS for MySQL, PostgreSQL, Oracle, and SQL Server, plus Redshift, DocumentDB, Neptune, and MongoDB Atlas all have pre-built Lambda rotation templates (source: AWS rotation documentation). Anything else needs a custom rotation Lambda built against the generic four-step rotation contract.

Does AWS Secrets Manager encrypt data at rest?

Yes, every secret is encrypted with AWS KMS, either the account’s default aws/secretsmanager key or a customer-managed key you specify. The IAM principal reading the secret needs both secretsmanager:GetSecretValue and kms:Decrypt on that specific key.

Can I recover a secret after deleting it?

Yes, as long as you didn’t use --force-delete-without-recovery. The default recovery window is a minimum of 7 days, during which restore-secret brings it back fully intact. There’s no charge for secrets sitting in the recovery window.

How much does cross-region replication cost?

Each replica is billed as a full separate secret at the standard $0.40/month rate — there’s no discounted “replica” pricing tier. Replicating ten secrets to one additional Region adds roughly $4/month on top of the primary cost.

Getting AWS Secrets Manager running takes an afternoon. Getting it running well — least-privilege IAM from the start, rotation tested before it’s relied on, replicas and API calls accounted for in the budget, and CloudTrail actually watching who reads what — is the difference between a service that quietly does its job and one that shows up as a surprise line item or, worse, a surprise breach report. Start with Step 1’s IAM policy, resist the urge to widen it “just for now,” and the rest of this setup holds up under an audit a lot better than a shared .env file ever did.

Related Coverage

For more cloud infrastructure tutorials and comparisons, browse the full Cloud Computing section.

Nadia Dubois

Nadia Dubois

AI & Innovation Editor

Nadia Dubois is the AI & Innovation Editor at Tech Insider, where she tracks the rapid evolution of artificial intelligence, from foundation models to real-world enterprise deployment. She previously covered AI and startups for La Tribune and contributed to MIT Technology Review's European coverage. Nadia specializes in generative AI, AI regulation, and the intersection of technology and European industrial policy. She holds a dual degree in Computational Linguistics and Journalism from Sciences Po Paris.

View all articles