AWS Secrets Manager Rotation: How Automatic Secret Rotation Works

Automatic secret rotation in AWS Secrets Manager replaces the manual work of updating credentials on a schedule. Instead of setting calendar reminders to change database passwords, you configure rotation once and Secrets Manager coordinates the credential change across both the secret store and the target service.

This guide covers how rotation works, when to use managed rotation vs Lambda-based rotation, how to set up rotation for RDS and custom secrets, and how to design applications that handle rotated credentials correctly.

By the end, you will understand which rotation approach fits your use case, how to configure it, and how to avoid the common mistakes that cause rotation failures in production.

Simple explanation

In technical terms, when rotation runs it generates a new password, updates the database (or API, or whatever service uses the credential), verifies the new password works, and then promotes it to the active version. The old password stays available briefly so applications mid-request do not break.

A real example: your production app uses a PostgreSQL password stored in Secrets Manager. Every 30 days, rotation generates a new password, runs an ALTER USER command on the database, confirms the connection works, and promotes the new password to active. Your app fetches the latest password on its next connection attempt.

When to use automatic rotation

Good fit for rotation
  • RDS, Redshift, or DocumentDB credentials. Managed rotation handles these with no Lambda code required.
  • Database passwords for other engines where you can write a Lambda to run the password-change command.
  • Third-party API keys that truly need to be stored as secrets and that the provider lets you rotate programmatically.
  • Secrets used in CI/CD pipelines or Kubernetes workloads where credentials are pulled from Secrets Manager at deploy or runtime.
Not a good fit
  • AWS-to-AWS access. If your EC2 instance, Lambda function, or ECS task needs to call another AWS service, use IAM roles with STS temporary credentials instead. Storing and rotating AWS access keys adds complexity that IAM roles eliminate entirely.
  • Secrets that cannot be changed programmatically. If the target service has no API for updating the credential, rotation will fail at the setSecret step.
  • Long-lived AWS access keys that could be replaced by IAM roles. Rotating access keys in Secrets Manager is possible but is almost always the wrong solution.

Managed rotation vs Lambda rotation

Secrets Manager offers two rotation approaches. Understanding the difference prevents you from over-engineering simple cases or under-building complex ones.

Managed rotation is available for supported AWS database services (RDS, Redshift, DocumentDB). AWS handles the entire rotation process internally. You do not create, deploy, or manage a Lambda function.

Lambda-based rotation is used for everything else, and also when you need custom rotation logic for database secrets (for example, a multi-step credential change that involves external systems). You write a Lambda function that implements the four rotation steps, and Secrets Manager invokes it on schedule.

OptionBest forLambda neededOperational effortAvailability notes
Managed rotationRDS, Redshift, DocumentDB secretsNoLow. AWS manages the rotation infrastructure.Supports single-user and alternating-users strategies. Alternating users is safer for high availability.
Lambda-based rotationAPI keys, OAuth tokens, custom credentials, complex database workflowsYes. You write and maintain the function.Medium to high. You own the Lambda code, IAM permissions, VPC config, and monitoring.Availability depends on your Lambda logic. Must handle failures gracefully to avoid leaving credentials in an inconsistent state.
Common confusion

When using managed rotation for a supported database, do not specify a —rotation-lambda-arn in the CLI. Secrets Manager provisions the rotation infrastructure automatically. The —rotation-lambda-arn parameter is only for Lambda-based rotation where you provide your own function.

How Secrets Manager rotation works

Regardless of whether you use managed or Lambda-based rotation, the internal process follows four steps. Understanding these steps helps you debug rotation failures.

Version staging labels

Every secret version in Secrets Manager carries a staging label that tracks where it sits in the rotation lifecycle:

The three labels to know
  • AWSCURRENT is the active version. This is what applications receive when they call GetSecretValue.
  • AWSPENDING is the version being created during rotation. It exists only while rotation is in progress.
  • AWSPREVIOUS is the former active version. It remains valid at the target service until the next rotation, giving applications a fallback window.

The four rotation steps

StepWhat happens
createSecretGenerates a new credential value and stores it as a new secret version labeled AWSPENDING.
setSecretApplies the new credential at the target service. For a database, this means running an ALTER USER or equivalent command to change the password.
testSecretVerifies the new credential works by connecting to the service with the AWSPENDING version.
finishSecretPromotes AWSPENDING to AWSCURRENT and moves the old version to AWSPREVIOUS. The rotation is now complete.

If any step fails, the rotation stops and AWSCURRENT remains unchanged. Your application continues using the existing credential. You can monitor failures through CloudTrail events and CloudWatch Logs.

How to set up rotation for RDS, Redshift, and DocumentDB

For supported database services, the setup depends on whether you use managed rotation (recommended) or Lambda-based rotation.

Managed rotation (console)

  1. Open the secret in the Secrets Manager console.
  2. Choose Set up automatic rotation.
  3. Select a rotation schedule (for example, every 30 days).
  4. Choose a rotation strategy: single user (updates the same database user) or alternating users (switches between two database users, keeping one active at all times).
  5. Save. Secrets Manager provisions the rotation infrastructure automatically.

Managed rotation (CLI)

# Enable managed rotation for an RDS secret (no Lambda ARN needed)
aws secretsmanager rotate-secret \
  --secret-id "prod/myapp/postgres" \
  --rotation-rules '{"ScheduleExpression":"rate(30 days)","Duration":"2h"}'

Notice there is no —rotation-lambda-arn parameter. For managed rotation, Secrets Manager handles the rotation function internally.

Lambda-based rotation for databases (when you need custom logic)

If you need custom behavior during rotation, such as notifying an external system or performing additional validation, you can create your own Lambda function and pass its ARN:

# Enable rotation with a custom Lambda function
aws secretsmanager rotate-secret \
  --secret-id "prod/myapp/postgres" \
  --rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:MyCustomRotation \
  --rotation-rules '{"ScheduleExpression":"rate(30 days)","Duration":"2h"}'

Prerequisites

Before rotation will work, verify these requirements:

  • Network access. The rotation process (whether managed or Lambda) must be able to reach the database. For databases in a VPC, this means the rotation infrastructure needs to be in the same VPC or have routing to it.
  • Secrets Manager API access. If rotation runs inside a private VPC subnet with no internet access, create a VPC endpoint for Secrets Manager or configure a NAT gateway so the rotation process can call the Secrets Manager API.
  • IAM permissions. The rotation process needs secretsmanager:GetSecretValue, secretsmanager:PutSecretValue, secretsmanager:UpdateSecretVersionStage, and secretsmanager:DescribeSecret on the secret.
  • KMS access. If the secret is encrypted with a customer managed key (CMK), the rotation process also needs kms:Decrypt and kms:GenerateDataKey permissions on that key.
  • Database permissions. The database user whose credentials are stored in the secret must have permission to change its own password (for single-user rotation) or create and manage the alternating user (for alternating-users rotation).
Alternating users

The alternating-users strategy creates two database users and rotates between them. While one user’s password is being changed, the other remains active. This is the safer option for high-availability applications because there is always one fully working credential. However, it requires the admin secret to have permission to manage both database users.

How to set up rotation for custom secrets

For API keys, OAuth tokens, or any credential not covered by managed rotation, you write a Lambda function that implements the four rotation steps. Secrets Manager invokes your function with these event fields:

  • SecretId is the ARN of the secret being rotated.
  • ClientRequestToken is a unique ID for this rotation attempt. It becomes the new version ID.
  • Step is one of createSecret, setSecret, testSecret, or finishSecret.

Here is a minimal Lambda structure showing the control flow:

import boto3
import json

def lambda_handler(event, context):
    client = boto3.client('secretsmanager')
    arn = event['SecretId']
    token = event['ClientRequestToken']
    step = event['Step']

    if step == "createSecret":
        # Generate new credentials and store as AWSPENDING
        current = json.loads(
            client.get_secret_value(SecretId=arn)['SecretString']
        )
        current['api_key'] = generate_new_api_key()
        client.put_secret_value(
            SecretId=arn,
            ClientRequestToken=token,
            SecretString=json.dumps(current),
            VersionStages=['AWSPENDING']
        )
    elif step == "setSecret":
        # Call the external API to register the new key
        pending = json.loads(
            client.get_secret_value(
                SecretId=arn, VersionId=token, VersionStage='AWSPENDING'
            )['SecretString']
        )
        register_key_with_provider(pending['api_key'])
    elif step == "testSecret":
        # Verify the new key works
        pending = json.loads(
            client.get_secret_value(
                SecretId=arn, VersionId=token, VersionStage='AWSPENDING'
            )['SecretString']
        )
        call_provider_api(pending['api_key'])
    elif step == "finishSecret":
        # Promote AWSPENDING to AWSCURRENT
        metadata = client.describe_secret(SecretId=arn)
        current_version = next(
            v for v, stages in metadata['VersionIdsToStages'].items()
            if 'AWSCURRENT' in stages
        )
        client.update_secret_version_stage(
            SecretId=arn, VersionStage='AWSCURRENT',
            MoveToVersionId=token, RemoveFromVersionId=current_version
        )

Testing custom rotation safely

Always test in staging first

A broken rotation function can leave credentials in an inconsistent state between Secrets Manager and your target service. Never test a new rotation Lambda directly against production.

  1. Start with a secret that points to a test or staging environment.
  2. Trigger rotation manually: aws secretsmanager rotate-secret —secret-id “test/myapp/api-key”
  3. Check CloudWatch Logs for the Lambda to verify each step completed.
  4. Confirm the AWSCURRENT version has the new credential: aws secretsmanager get-secret-value —secret-id “test/myapp/api-key”
  5. Verify your application can still authenticate using the secret.

Rotation schedules and windows

rate() expressions

The simplest option. Rotation runs at a fixed interval from the last rotation.

# Rotate every 30 days
aws secretsmanager rotate-secret \
  --secret-id "prod/myapp/database" \
  --rotation-rules '{"ScheduleExpression":"rate(30 days)"}'

cron() expressions

For precise timing, use a cron expression. All cron schedules run in UTC.

# Rotate at 2:00 AM UTC on the first Sunday of every month
aws secretsmanager rotate-secret \
  --secret-id "prod/myapp/database" \
  --rotation-rules '{"ScheduleExpression":"cron(0 2 ? * SUN#1 *)"}'

Rotation windows

The Duration parameter defines a rotation window: the time range within which rotation can start after the scheduled time. This is useful when you want rotation to happen during a low-traffic period but do not need it at an exact minute.

# Rotate every 30 days, within a 4-hour window starting at the scheduled time
aws secretsmanager rotate-secret \
  --secret-id "prod/myapp/database" \
  --rotation-rules '{"ScheduleExpression":"rate(30 days)","Duration":"4h"}'
UTC matters

If you do not specify a duration, Secrets Manager uses a default rotation window. Cron-based schedules are especially helpful when you want rotation during a maintenance window. All schedule expressions evaluate in UTC, so convert your desired local time before writing the expression.

How applications should handle rotated secrets

Configuring rotation is only half the work. Your application must be designed to handle credential changes gracefully.

The #1 cause of rotation incidents

Most rotation failures are not rotation bugs. They are application bugs. An app that reads the secret once at startup and never refreshes will break on the very first rotation. If your app caches passwords forever or never retries on auth failures, rotation will cause outages no matter how well the rotation itself is configured.

Cache with a TTL

Do not fetch the secret on every request (expensive and slow), but do not cache it forever either. A TTL of 5 to 15 minutes is a common starting point. The AWS Secrets Manager caching libraries (available for Java, Python, .NET, Go, and JavaScript) handle this automatically.

Retry on authentication failure

When a database connection fails with an authentication error, fetch a fresh secret and retry before giving up. This handles the brief window where the cached password is the old one and the database now expects the new one.

import boto3
import json
import psycopg2
import time

_cache = {'value': None, 'fetched_at': 0}
CACHE_TTL = 300  # 5 minutes

def get_secret(force_refresh=False):
    now = time.time()
    if not force_refresh and _cache['value'] and (now - _cache['fetched_at']) < CACHE_TTL:
        return _cache['value']
    client = boto3.client('secretsmanager')
    result = client.get_secret_value(SecretId='prod/myapp/postgres')
    _cache['value'] = json.loads(result['SecretString'])
    _cache['fetched_at'] = now
    return _cache['value']

def get_db_connection():
    secret = get_secret()
    for attempt in range(3):
        try:
            return psycopg2.connect(
                host=secret['host'], database=secret['dbname'],
                user=secret['username'], password=secret['password'],
                port=secret['port']
            )
        except psycopg2.OperationalError as e:
            if "password authentication failed" in str(e) and attempt < 2:
                time.sleep(1)
                secret = get_secret(force_refresh=True)
            else:
                raise

Refresh connection pools

If your application uses a connection pool, existing connections hold the old password. New connections will use whatever the pool’s credential source provides. Design your pool to:

  • Periodically cycle out old connections (most connection pool libraries have a max-lifetime setting).
  • Handle authentication errors by evicting the failed connection and fetching a fresh secret for the replacement.
Bottom line

Application design matters as much as rotation configuration. A well-configured rotation schedule paired with an app that caches credentials forever will still cause downtime.

Common mistakes

  1. Using Lambda-based rotation when managed rotation is available. For RDS, Redshift, and DocumentDB, managed rotation is simpler and has fewer moving parts. Only use a custom Lambda if you need custom rotation logic.
  2. Application caches secrets forever. An application that reads the secret once at startup and never refreshes will break on the first rotation. Always cache with a TTL and retry on authentication failures.
  3. Missing network access for the rotation process. The rotation function must reach both the target database and the Secrets Manager API. If the database is in a private VPC subnet, you need a VPC endpoint for Secrets Manager or a NAT gateway.
  4. Wrong IAM or KMS permissions. The rotation process needs permissions on the secret and, if you use a customer managed key, on the KMS key. Missing KMS permissions are a common cause of silent rotation failures.
  5. Rotating AWS access keys instead of using IAM roles. If you are rotating IAM user access keys through Secrets Manager, step back and ask whether an IAM role with STS temporary credentials would eliminate the need for stored keys entirely.
  6. Not testing rotation before going to production. Rotation has multiple dependencies (network, IAM, database permissions, application retry logic). Always trigger a manual rotation in a test environment and verify every step before enabling it on production secrets.
  7. Setting a rotation frequency too high without testing. Rotating daily means 365 rotation events per year. If your application has any weakness in handling rotation, you will find out quickly. Start with 30 or 90 days, confirm everything works, then shorten the interval if compliance requires it.

Frequently asked questions

Do I need Lambda for AWS Secrets Manager rotation?

Not always. For supported services like RDS, Redshift, and DocumentDB, AWS offers managed rotation that handles everything without you creating a Lambda function. For other secret types (API keys, OAuth tokens, custom credentials), you write a Lambda rotation function that implements the four-step rotation logic.

Does secret rotation cause downtime?

It depends on how your application handles credentials. Rotation itself is designed to keep the old credential valid (as AWSPREVIOUS) during the transition. But if your application caches credentials indefinitely or does not retry on auth failures, connections can break. The alternating-users strategy is the safest option for high availability because one user is always active. Application-side retry and cache refresh are just as important as the rotation config.

How often should I rotate secrets?

There is no single answer. Many teams start with 30 or 90 days for database passwords. Compliance frameworks may require specific intervals. The key is to test your application at whatever frequency you choose before going to production. Rotating more often reduces the exposure window but increases the chance of hitting an application bug that does not handle rotation well.

Can I rotate non-database secrets?

Yes. Any secret stored in Secrets Manager can be rotated automatically if you provide a Lambda function that knows how to change the credential at the source. This works for API keys, OAuth tokens, SSH keys, or any custom credential. The Lambda receives the same four-step event structure regardless of the secret type.

Should I store AWS access keys in Secrets Manager or use IAM roles and STS?

Use IAM roles and STS temporary credentials whenever possible. Storing AWS access keys in Secrets Manager and rotating them is a valid pattern for legacy systems that cannot use roles, but it adds operational complexity. IAM roles with STS provide short-lived credentials automatically with no rotation infrastructure to maintain.

Last verified: 30 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.