How to Set Up S3 Same-Region Replication (SRR) for Data Redundancy

You have a production S3 bucket and want a live backup copy in the same AWS region — not for disaster recovery across regions, but for data redundancy, compliance isolation, or log aggregation into a single bucket. Same-Region Replication (SRR) handles exactly this, but the IAM role configuration is where most engineers get stuck. A misconfigured replication role fails silently: the replication rule shows as 'Enabled' in the console, but objects never appear in the destination bucket.

TL;DR: S3 Same-Region Replication Setup

StepWhat You DoCommon Mistake
1. Enable versioningBoth source and destination bucketsForgetting the destination bucket
2. Create IAM roleTrust policy for S3, permissions to read source and write destinationMissing s3:ReplicateObject on destination
3. Configure replication ruleAttach role, set destination bucket, define scopeRole ARN typo causes silent failure
4. KMS (if applicable)Add kms:Decrypt on source key, kms:Encrypt on destination keyUsing kms:GenerateDataKey instead of kms:Encrypt for destination

How S3 Same-Region Replication Works

SRR is an asynchronous, object-level replication mechanism. When a new object is written to the source bucket, S3 detects the PUT event, evaluates the replication rules, and — using the IAM role you specify — copies the object to the destination bucket. The replication role is assumed by the S3 service principal, not your application. This distinction matters for trust policy configuration.

Versioning is a hard prerequisite. SRR tracks which object versions have been replicated using version IDs. Without versioning enabled on both buckets, the replication configuration is rejected at the API level.

Replication applies only to new objects written after the rule is enabled. Existing objects are not replicated automatically. If you need to backfill existing objects, S3 Batch Replication is a separate operation.

sequenceDiagram participant Client participant S3Source as S3 Source Bucket participant S3Service as S3 Replication Engine participant IAM as IAM (Role Assumption) participant KMS as KMS (if SSE-KMS) participant S3Dest as S3 Destination Bucket Client->>S3Source: PUT Object S3Source->>S3Service: Evaluate replication rules S3Service->>IAM: AssumeRole (s3-srr-replication-role) IAM-->>S3Service: Temporary credentials S3Service->>S3Source: GetObjectVersionForReplication S3Source-->>S3Service: Object data + metadata alt SSE-KMS encrypted S3Service->>KMS: kms:Decrypt (source key) KMS-->>S3Service: Plaintext object S3Service->>KMS: kms:Encrypt (destination key) KMS-->>S3Service: Re-encrypted object end S3Service->>S3Dest: ReplicateObject S3Dest-->>S3Service: 200 OK S3Service->>S3Source: Update ReplicationStatus = COMPLETED
  1. Client PUT: An object is written to the source bucket.
  2. S3 evaluates rules: S3 checks the replication configuration to determine if the object matches any active rule (by prefix or tag filter).
  3. Role assumption: The S3 service assumes the replication IAM role to act on your behalf.
  4. Read from source: S3 reads the object and its metadata from the source bucket using the role's s3:GetObject permission.
  5. Write to destination: S3 writes the object to the destination bucket using s3:ReplicateObject.
  6. Replication status tag: The source object's replication status is updated to COMPLETED or FAILED.

Prerequisites: Versioning on Both Buckets

Before creating any replication rule, enable versioning on both the source and destination buckets. This is enforced by the S3 API — you cannot save a replication configuration on an unversioned bucket.

# Enable versioning on the source bucket
aws s3api put-bucket-versioning \
  --bucket my-source-bucket \
  --versioning-configuration Status=Enabled

# Enable versioning on the destination bucket
aws s3api put-bucket-versioning \
  --bucket my-destination-bucket \
  --versioning-configuration Status=Enabled

# Verify versioning status on both
aws s3api get-bucket-versioning --bucket my-source-bucket
aws s3api get-bucket-versioning --bucket my-destination-bucket

The output should show "Status": "Enabled" for both. If either bucket shows Suspended, replication will not function correctly.

Step 1: Create the Replication IAM Role

The replication role is assumed by the S3 service principal (s3.amazonaws.com). It needs two categories of permissions: read access on the source bucket and write access on the destination bucket. These are often collapsed into a single policy, but keeping them logically separate makes auditing easier.

First, create the trust policy document that allows S3 to assume this role:

cat > s3-replication-trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "s3.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

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

Next, create the permissions policy. The source bucket permissions allow S3 to read objects and their replication metadata. The destination bucket permissions allow S3 to write replicated objects.

🔽 Click to expand: Full replication permissions policy (replication-permissions.json)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SourceBucketRead",
      "Effect": "Allow",
      "Action": [
        "s3:GetReplicationConfiguration",
        "s3:ListBucket"
      ],
      "Resource": "arn:aws:s3:::my-source-bucket"
    },
    {
      "Sid": "SourceObjectRead",
      "Effect": "Allow",
      "Action": [
        "s3:GetObjectVersionForReplication",
        "s3:GetObjectVersionAcl",
        "s3:GetObjectVersionTagging"
      ],
      "Resource": "arn:aws:s3:::my-source-bucket/*"
    },
    {
      "Sid": "DestinationBucketWrite",
      "Effect": "Allow",
      "Action": [
        "s3:ReplicateObject",
        "s3:ReplicateDelete",
        "s3:ReplicateTags"
      ],
      "Resource": "arn:aws:s3:::my-destination-bucket/*"
    }
  ]
}
aws iam put-role-policy \
  --role-name s3-srr-replication-role \
  --policy-name s3-srr-replication-policy \
  --policy-document file://replication-permissions.json

Retrieve the role ARN — you will need it in the next step:

aws iam get-role \
  --role-name s3-srr-replication-role \
  --query 'Role.Arn' \
  --output text

Step 2: Configure the Replication Rule on the Source Bucket

The replication configuration is applied to the source bucket only. It references the destination bucket and the IAM role. The rule below replicates all objects (no prefix filter). If you need to replicate only a subset of objects, add a Filter block with a Prefix or Tag condition.

🔽 Click to expand: Replication configuration JSON (replication-config.json)
{
  "Role": "arn:aws:iam::123456789012:role/s3-srr-replication-role",
  "Rules": [
    {
      "ID": "SRR-full-bucket-backup",
      "Status": "Enabled",
      "Filter": {},
      "Destination": {
        "Bucket": "arn:aws:s3:::my-destination-bucket"
      },
      "DeleteMarkerReplication": {
        "Status": "Disabled"
      }
    }
  ]
}
aws s3api put-bucket-replication \
  --bucket my-source-bucket \
  --replication-configuration file://replication-config.json

Verify the configuration was accepted:

aws s3api get-bucket-replication --bucket my-source-bucket

A note on DeleteMarkerReplication: setting this to Disabled means that if you delete an object in the source bucket (creating a delete marker), that delete marker is not replicated. The object version remains in the destination. Whether you want this depends on your use case — for a backup bucket, keeping the destination intact on source deletion is often the right call.

Step 3: Verify Replication Is Working

The replication rule being 'Enabled' in the console does not confirm that replication is actually succeeding. Write a test object and check its replication status explicitly.

# Write a test object to the source bucket
aws s3api put-object \
  --bucket my-source-bucket \
  --key replication-test/test-object.txt \
  --body /dev/null

# Check the replication status on the source object
# (allow a few seconds for async replication to complete)
aws s3api head-object \
  --bucket my-source-bucket \
  --key replication-test/test-object.txt \
  --query 'ReplicationStatus'

A status of COMPLETED confirms the object was successfully replicated. A status of FAILED means the replication role lacks a required permission or the destination bucket configuration is blocking the write. Check S3 replication metrics and CloudWatch if you need to diagnose failures at scale.

# Confirm the object exists in the destination bucket
aws s3api head-object \
  --bucket my-destination-bucket \
  --key replication-test/test-object.txt

SSE-KMS Encrypted Buckets: Additional Permissions Required

If your source bucket uses SSE-KMS encryption, the replication role needs additional KMS permissions. This is the most common source of silent replication failures on encrypted buckets — the replication rule enables without error, but every object shows FAILED replication status.

Think of it like a courier who can pick up a sealed package (decrypt source) but needs a separate key to re-seal it in the destination warehouse (encrypt destination). Both keys must be explicitly authorized.

The required KMS actions differ by direction:

  • Source KMS key: kms:Decrypt — S3 must decrypt the object before replicating it.
  • Destination KMS key: kms:Encrypt — S3 must encrypt the object when writing it to the destination bucket.

Add the following statement to the replication permissions policy:

{
  "Sid": "KMSReplicationPermissions",
  "Effect": "Allow",
  "Action": [
    "kms:Decrypt"
  ],
  "Resource": "arn:aws:kms:us-east-1:123456789012:key/source-key-id",
  "Condition": {
    "StringLike": {
      "kms:ViaService": "s3.us-east-1.amazonaws.com",
      "kms:EncryptionContext:aws:s3:arn": "arn:aws:s3:::my-source-bucket/*"
    }
  }
},
{
  "Sid": "KMSDestinationEncrypt",
  "Effect": "Allow",
  "Action": [
    "kms:Encrypt"
  ],
  "Resource": "arn:aws:kms:us-east-1:123456789012:key/destination-key-id",
  "Condition": {
    "StringLike": {
      "kms:ViaService": "s3.us-east-1.amazonaws.com",
      "kms:EncryptionContext:aws:s3:arn": "arn:aws:s3:::my-destination-bucket/*"
    }
  }
}

You also need to specify the destination KMS key in the replication configuration's Destination block:

"Destination": {
  "Bucket": "arn:aws:s3:::my-destination-bucket",
  "EncryptionConfiguration": {
    "ReplicaKmsKeyID": "arn:aws:kms:us-east-1:123456789012:key/destination-key-id"
  }
}

Additionally, the KMS key policies for both the source and destination keys must grant the replication role permission to use them. IAM policy alone is not sufficient — the KMS key policy is an independent authorization boundary. Verify that the replication role ARN is listed as an allowed principal in each key's resource policy.

# Check the source KMS key policy
aws kms get-key-policy \
  --key-id source-key-id \
  --policy-name default \
  --query 'Policy' \
  --output text

# Check the destination KMS key policy
aws kms get-key-policy \
  --key-id destination-key-id \
  --policy-name default \
  --query 'Policy' \
  --output text

Experience Signal: The Silent Failure Pattern

Here is a failure pattern that wastes time the first time you hit it. You configure SRR, write a test object, and the replication status comes back FAILED. You check the IAM policy — source read permissions look correct. You check the destination bucket policy — no explicit deny. Everything looks fine on paper.

The actual cause: the destination bucket has Object Ownership set to 'Bucket owner enforced', which disables ACLs entirely. The replication role's policy included s3:ReplicateObject on the destination, which should be sufficient. But the original replication configuration also had AccessControlTranslation set in the destination block — a leftover from a cross-account template. When ACLs are disabled on the destination, any replication configuration that attempts to set ACLs on replicated objects fails at the object write stage.

The fix: remove the AccessControlTranslation block from the destination configuration entirely, or align the Object Ownership setting with your ACL strategy. The replication rule status in the console never reflects this — it stays 'Enabled'. The only signal is the per-object ReplicationStatus: FAILED on the source object's metadata.

Replication status is per-object, not per-rule. A rule being enabled tells you nothing about whether individual objects are actually replicating.

Replication Flow: Full Picture

graph TD A["New Object Written
to Source Bucket"] --> B{"Versioning Enabled
on Both Buckets?"}; B -- No --> Z1["Replication Config
Rejected by API"]; B -- Yes --> C{"Matches Active
Replication Rule?"}; C -- No --> Z2["Object Not Replicated"]; C -- Yes --> D["S3 Assumes
Replication IAM Role"]; D --> E{"IAM Trust Policy
Correct?"}; E -- No --> Z3["ReplicationStatus: FAILED"]; E -- Yes --> F{"Source SSE-KMS
Encrypted?"}; F -- Yes --> G["kms:Decrypt on Source Key
(IAM + KMS Key Policy)"]; G --> H{"Decrypt Authorized?"}; H -- No --> Z3; H -- Yes --> I["kms:Encrypt on Destination Key
(IAM + KMS Key Policy)"]; I --> J{"Encrypt Authorized?"}; J -- No --> Z3; J -- Yes --> K["s3:ReplicateObject
to Destination Bucket"]; F -- No --> K; K --> L["ReplicationStatus: COMPLETED"];
  1. Versioning check: Both buckets must have versioning enabled before any replication can proceed.
  2. Rule evaluation: S3 evaluates whether the new object version matches an active replication rule by prefix or tag filter.
  3. IAM role assumption: S3 assumes the replication role. If the trust policy is misconfigured, this step fails and the object status is set to FAILED.
  4. KMS decrypt (conditional): If the source object is SSE-KMS encrypted, S3 decrypts it using the source KMS key. Requires kms:Decrypt in both the IAM policy and the KMS key policy.
  5. Object write: S3 writes the object to the destination bucket. If the destination uses SSE-KMS, S3 encrypts it using the destination key. Requires kms:Encrypt in both the IAM policy and the KMS key policy.
  6. Status update: The source object's ReplicationStatus metadata is updated to COMPLETED or FAILED.

Backfilling Existing Objects with S3 Batch Replication

SRR only replicates objects written after the replication rule is created. If you need to replicate objects that already exist in the source bucket, use S3 Batch Replication. This is a separate operation that creates a Batch Operations job targeting existing object versions.

# Create a Batch Replication job for existing objects
# Requires an existing replication configuration on the source bucket
aws s3control create-job \
  --account-id 123456789012 \
  --operation '{"S3ReplicateObject":{}}' \
  --report '{"Bucket":"arn:aws:s3:::my-batch-report-bucket","Prefix":"batch-replication-reports","Format":"Report_CSV_20180820","Enabled":true,"ReportScope":"AllTasks"}' \
  --manifest-generator '{"S3JobManifestGenerator":{"SourceBucket":"arn:aws:s3:::my-source-bucket","EnableManifestOutput":false,"Filter":{"EligibleForReplication":true}}}' \
  --role-arn arn:aws:iam::123456789012:role/s3-srr-replication-role \
  --priority 10 \
  --no-confirmation-required \
  --region us-east-1

The Batch Replication job role needs the same permissions as the standard replication role, plus s3:InitiateReplication on the source bucket objects and permissions to write the job report to the report bucket. Pricing and limits for Batch Operations vary — check the official AWS documentation for current values.

Wrap-Up: S3 Same-Region Replication Checklist

Configuring S3 Same-Region Replication comes down to three things: versioning on both buckets, a correctly scoped IAM role, and — if you are using KMS — the right KMS actions (kms:Decrypt on source, kms:Encrypt on destination) in both the IAM policy and the KMS key policies.

  • Versioning enabled on source and destination
  • IAM role trust policy scoped to s3.amazonaws.com
  • Source permissions: s3:GetObjectVersionForReplication, s3:GetObjectVersionAcl, s3:GetObjectVersionTagging, s3:GetReplicationConfiguration, s3:ListBucket
  • Destination permissions: s3:ReplicateObject, s3:ReplicateDelete, s3:ReplicateTags
  • KMS: kms:Decrypt on source key, kms:Encrypt on destination key (both IAM policy and KMS key policy)
  • Verify per-object ReplicationStatus — not just rule status
  • Use S3 Batch Replication for existing objects

For the full replication configuration reference and current service quotas, see the AWS S3 Replication documentation.

Glossary

TermDefinition
SRR (Same-Region Replication)An S3 feature that asynchronously copies objects from a source bucket to a destination bucket within the same AWS region.
Replication RoleAn IAM role assumed by the S3 service to read from the source bucket and write to the destination bucket during replication.
ReplicationStatusPer-object metadata field on the source object indicating whether replication succeeded (COMPLETED) or failed (FAILED).
SSE-KMSServer-Side Encryption with AWS Key Management Service. Objects encrypted with SSE-KMS require explicit KMS permissions for replication.
S3 Batch ReplicationA separate S3 Batch Operations job type that replicates existing objects that predate the replication rule configuration.

Related Posts

Comments

Popular posts from this blog

AWS SNS Email Alerts Not Arriving? The Subscription Confirmation Trap Explained

Lambda Infinite Loop with S3: How to Prevent Recursive Triggers

S3 Public Access Denied: Why Your Public Object URL Still Returns 403