Skip to content

Reduce Amazon Aurora Snapshot Backup Costs for Long-Term Retention Using AWS Backup and Amazon S3.

9 minute read
Content level: Advanced
2

Amazon Aurora automatically backs up cluster volumes and retains restore data for up to 35 days at no additional cost. Snapshots taken within this window whether automated or through AWS Backup are free. However, many organizations must retain backups well beyond 35 days to meet compliance, regulatory, or business continuity requirements. The purpose of this article is to present a practical, cost-effective strategy for long-term Aurora backup retention by combining AWS Backup with Amazon S3.

Reducing Amazon Aurora Backup Costs for Long Retention Periods

Amazon Aurora backs up cluster volumes automatically and retains restore data for the length of the backup retention period. Aurora automated backups are continuous and incremental, so you can restore to any point within the backup retention period from 1–35 days. Aurora also provides a free amount of backup usage equal to the latest cluster volume size. Snapshots taken within the automated backup retention period — whether manual or through AWS Backup — are free.

However, if you need to retain backups for more than 35 days for compliance or business continuity, costs can escalate quickly. AWS Backup snapshots of Aurora are always full backups, and each snapshot retained beyond the 35-day window incurs charges at the full GB-month rate. For a 500 GiB database with daily snapshots retained for 3 years, this can exceed $10,000 per month.

In this post, we discuss how to reduce Aurora backup costs for long retention periods by combining AWS Backup with Amazon S3 snapshot exports. This approach keeps short-term backups free within Aurora's retention window and offloads long-term retention to Amazon S3, where lifecycle policies can transition data to cost-effective storage classes like Amazon S3 Glacier.

Solution Overview

This solution uses a two-tier backup strategy: AWS Backup manages short-term Aurora snapshots within the free 35-day retention window, while an automated pipeline exports snapshots to Amazon S3 in compressed Apache Parquet format for long-term archival. Amazon EventBridge detects completed backup jobs and triggers an AWS Lambda function to initiate the S3 export automatically.

The following diagram illustrates the architecture for this solution: Architecture: Aurora Long-Term Backup Cost Optimization

Note: AWS KMS is a critical component in this architecture. Both the Lambda execution role and the IAM export role require KMS permissions. The KMS key encrypts the snapshot data exported to S3. Without proper KMS grants, the export will fail with KMSKeyNotAccessibleFault or IamRoleMissingPermissions errors.

The workflow includes the following steps:

  1. AWS Backup runs a daily backup plan and creates a full Aurora snapshot.
  2. The snapshot is retained for up to 35 days at no additional cost (within Aurora's free backup allowance).
  3. Amazon EventBridge detects the completed backup job and triggers a Lambda function.
  4. The Lambda function calls start-export-task to export the snapshot to Amazon S3 in Apache Parquet format. AWS KMS encrypts the exported data — both the Lambda role and the export role must have KMS permissions.
  5. An S3 Lifecycle policy transitions the exported data through storage tiers (Standard → Standard-IA → Glacier → Deep Archive) and expires it after the desired retention period.

Prerequisites

To implement this solution, you need the following:

  • An Amazon Aurora DB cluster
  • An Amazon S3 bucket in the same AWS Region as the Aurora cluster
  • An AWS KMS symmetric encryption key for the S3 export
  • An IAM role granting the export task access to the S3 bucket
  • AWS Backup configured with access to your Aurora cluster

Step 1: Configure Aurora Backup Retention to 35 Days

Maximize the free backup tier by setting your Aurora cluster's backup retention period to the maximum of 35 days:

aws rds modify-db-cluster \
  --db-cluster-identifier my-aurora-cluster \
  --backup-retention-period 35 \
  --apply-immediately

Warning: The --apply-immediately flag can cause an unexpected reboot of the cluster if there are other pending modifications that require a reboot. If you're running this against a production cluster, consider omitting the flag to apply the change during the next maintenance window instead, or verify there are no pending changes first with aws rds describe-db-clusters --db-cluster-identifier my-aurora-cluster --query 'DBClusters[0].PendingModifiedValues'.

This ensures that all snapshots — whether taken by Aurora automatically or by AWS Backup — are free for up to 35 days.

Step 2: Create an AWS Backup Plan with 35-Day Retention

Create a backup plan that takes daily snapshots and deletes them after 35 days. Because this aligns with Aurora's automated backup retention period, these snapshots incur zero additional backup storage charges.

Save the following as backup-plan.json:

{
  "BackupPlanName": "aurora-short-term-backup",
  "Rules": [
    {
      "RuleName": "daily-aurora-snapshot",
      "TargetBackupVaultName": "Default",
      "ScheduleExpression": "cron(0 3 * * ? *)",
      "StartWindowMinutes": 60,
      "CompletionWindowMinutes": 180,
      "Lifecycle": {
        "DeleteAfterDays": 35
      }
    }
  ]
}

Create the backup plan:

aws backup create-backup-plan --backup-plan file://backup-plan.json

Then assign your Aurora cluster to the backup plan using a resource assignment.

Step 3: Set Up the IAM Role for Snapshot Export

Create an IAM role that grants the RDS export task permission to write to your S3 bucket and use the KMS key.

Trust policy (trust-policy.json):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "export.rds.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Permission policy (export-permissions.json):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:GetBucketLocation",
        "s3:ListBucket",
        "s3:AbortMultipartUpload",
        "s3:ListMultipartUploaParts"
      ],
      "Resource": [
        "arn:aws:s3:::my-aurora-exports-bucket",
        "arn:aws:s3:::my-aurora-exports-bucket/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "kms:Encrypt",
        "kms:Decrypt",
        "kms:GenerateDataKey",
        "kms:GenerateDataKeyWithoutPlaintext",
        "kms:ReEncryptFrom",
        "kms:ReEncryptTo",
        "kms:DescribeKey",
        "kms:CreateGrant",
        "kms:RetireGrant"
      ],
      "Resource": "arn:aws:kms:<region>:<account-id>:key/<your-kms-key-id>"
    }
  ]
}

Important: The export task requires s3:GetObject and s3:DeleteObject in addition to s3:PutObject. Without these, the StartExportTask API call will fail with an IamRoleMissingPermissions error.

Create the role:

aws iam create-role \
  --role-name AuroraSnapshotExportRole \
  --assume-role-policy-document file://trust-policy.json

aws iam put-role-policy \
  --role-name AuroraSnapshotExportRole \
  --policy-name AuroraExportToS3 \
  --policy-document file://export-permissions.json

Additionally, create a KMS grant for the export role to ensure the RDS export service can use the key:

aws kms create-grant \
  --key-id <your-kms-key-id> \
  --grantee-principal arn:aws:iam::<account-id>:role/AuroraSnapshotExportRole \
  --operations Encrypt Decrypt GenerateDataKey \
    GenerateDataKeyWithoutPlaintext \
    ReEncryptFrom ReEncryptTo CreateGrant DescribeKey RetireGrant

Step 4: Automate Snapshot Exports with EventBridge and Lambda

Create an EventBridge rule that detects completed AWS Backup jobs for Aurora and triggers a Lambda function to start the S3 export.

EventBridge rule pattern:

{
  "source": ["aws.backup"],
  "detail-type": ["Backup Job State Change"],
  "detail": {
    "state": ["COMPLETED"],
    "resourceType": ["Aurora"]
  }
}

The Lambda function extracts the cluster identifier from the event, finds the latest available snapshot, and calls start-export-task. Environment variables are used for configuration so the function can be reused across clusters:

import boto3
import datetime
import os

rds = boto3.client("rds")

def handler(event, context):
    detail = event.get("detail", {})
    resource_arn = detail.get("resourceArn", "")
    state = detail.get("state", "")
    resource_type = detail.get("resourceType", "")

    print(f"Event received: type={resource_type}, state={state}, arn={resource_arn}")

    if state != "COMPLETED":
        return

    # Get the recovery point ARN which is the snapshot ARN
    backup_job_id = detail.get("backupJobId", "")

    # Find the snapshot created by this backup job
    cluster_id = resource_arn.split(":")[-1]
    snapshots = rds.describe_db_cluster_snapshots(
        DBClusterIdentifier=cluster_id,
        SnapshotType="awsbackup",
    )["DBClusterSnapshots"]

    if not snapshots:
        print(f"No snapshots found for {cluster_id}")
        return

    # Get the latest available snapshot
    available = [s for s in snapshots if s["Status"] == "available"]
    if not available:
        print("No available snapshots")
        return

    snapshot = sorted(available, key=lambda s: s["SnapshotCreateTime"])[-1]
    snapshot_arn = snapshot["DBClusterSnapshotArn"]
    timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")

    print(f"Exporting snapshot: {snapshot_arn}")
    try:
        rds.start_export_task(
            ExportTaskIdentifier=f"aurora-export-{timestamp}",
            SourceArn=snapshot_arn,
            S3BucketName=os.environ["S3_BUCKET"],
            S3Prefix="aurora-exports/",
            IamRoleArn=os.environ["EXPORT_ROLE_ARN"],
            KmsKeyId=os.environ["KMS_KEY_ARN"],
        )
        print(f"Export started: aurora-export-{timestamp}")
    except rds.exceptions.ClientError as e:
        if "already being exported" in str(e) or "ExportTaskAlreadyExists" in str(e):
            print(f"Export already exists for this snapshot, skipping")
        else:
            raise

Important: The Lambda execution role must also have KMS permissions (kms:Encrypt, kms:Decrypt, kms:GenerateDataKey, kms:DescribeKey, kms:CreateGrant) on the KMS key. Without these, the StartExportTask call will fail with a KMSKeyNotAccessibleFault error, even though the export IAM role has the correct permissions. This is because the calling principal (Lambda) must also be authorized to use the KMS key.

Step 5: Apply S3 Lifecycle Policies for Tiered Storage

Configure an S3 Lifecycle policy to automatically transition exported data to cheaper storage classes and expire it after your desired retention period:

aws s3api put-bucket-lifecycle-configuration \
  --bucket my-aurora-exports-bucket \
  --lifecycle-configuration '{
    "Rules": [
      {
        "ID": "aurora-export-lifecycle",
        "Status": "Enabled",
        "Filter": { "Prefix": "aurora-exports/" },
        "Transitions": [
          { "Days": 30, "StorageClass": "STANDARD_IA" },
          { "Days": 90, "StorageClass": "GLACIER" },
          { "Days": 365, "StorageClass": "DEEP_ARCHIVE" }
        ],
        "Expiration": { "Days": 1825 }
      }
    ]
  }'

Step 6: Restore Data from S3 When Needed

To access exported data archived in S3 Glacier storage classes, use the restore-object command. The following example restores an object for 25 days:

aws s3api restore-object \
  --bucket my-aurora-exports-bucket \
  --key aurora-exports/example-file.parquet \
  --restore-request '{"Days":25,"GlacierJobParameters":{"Tier":"Standard"}}'

To monitor the restore status:

aws s3api head-object \
  --bucket my-aurora-exports-bucket \
  --key aurora-exports/example-file.parquet

Because the exported data is in Parquet format, you can also query it directly using Amazon Athena or Amazon Redshift Spectrum without restoring a full Aurora cluster.

Conclusion

In this post, we explored how to reduce the cost of long-term Aurora backup retention by combining AWS Backup with Amazon S3 snapshot exports. This approach provides the best of both worlds: the operational convenience of AWS Backup for recent recovery points (free within the 35-day retention window), and the cost efficiency of Amazon S3 tiered storage for compliance and archival needs.

Key benefits of this approach:

  • Zero cost for short-term backups — Aurora snapshots within the 35-day retention period are free
  • Up to 99% cost reduction for long-term retention — S3 Glacier Deep Archive costs a fraction of Aurora snapshot storage
  • Queryable archives — exported Parquet data can be queried directly with Amazon Athena or Redshift Spectrum without restoring a full cluster
  • Fully automated — EventBridge and Lambda handle the export pipeline with no manual intervention

Test the procedure outlined in this post and share your feedback in the comments section.

References

  1. Exporting DB cluster snapshot data to Amazon S3
  2. Understanding Amazon Aurora backup storage usage
  3. Working with supported services in AWS Backup
  4. Metering, costs, and billing for AWS Backup
  5. Amazon Aurora Pricing