Create AWS ElastiCache Replication Groups

The aws:elasticache/replicationGroup:ReplicationGroup resource, part of the Pulumi AWS provider, defines an ElastiCache replication group: a Redis or Valkey cluster with primary and replica nodes, supporting both single-shard and multi-shard (cluster mode) topologies. This guide focuses on four capabilities: single-shard clusters with automatic failover, multi-shard cluster mode for horizontal scaling, availability zone placement and keyspace control, and log delivery and authentication.

Replication groups run in VPC subnet groups with security groups, and may reference parameter groups, CloudWatch log groups, or Kinesis Firehose streams. The examples are intentionally small. Combine them with your own VPC infrastructure, parameter groups, and monitoring targets.

Create a single-shard cluster with read replicas

Most Redis deployments begin with a single shard containing a primary node and one or more read replicas for high availability. This configuration provides automatic failover without the complexity of data sharding.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.elasticache.ReplicationGroup("example", {
    automaticFailoverEnabled: true,
    preferredCacheClusterAzs: [
        "us-west-2a",
        "us-west-2b",
    ],
    replicationGroupId: "tf-rep-group-1",
    description: "example description",
    nodeType: "cache.m4.large",
    numCacheClusters: 2,
    parameterGroupName: "default.redis3.2",
    port: 6379,
});
import pulumi
import pulumi_aws as aws

example = aws.elasticache.ReplicationGroup("example",
    automatic_failover_enabled=True,
    preferred_cache_cluster_azs=[
        "us-west-2a",
        "us-west-2b",
    ],
    replication_group_id="tf-rep-group-1",
    description="example description",
    node_type="cache.m4.large",
    num_cache_clusters=2,
    parameter_group_name="default.redis3.2",
    port=6379)
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/elasticache"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := elasticache.NewReplicationGroup(ctx, "example", &elasticache.ReplicationGroupArgs{
			AutomaticFailoverEnabled: pulumi.Bool(true),
			PreferredCacheClusterAzs: pulumi.StringArray{
				pulumi.String("us-west-2a"),
				pulumi.String("us-west-2b"),
			},
			ReplicationGroupId: pulumi.String("tf-rep-group-1"),
			Description:        pulumi.String("example description"),
			NodeType:           pulumi.String("cache.m4.large"),
			NumCacheClusters:   pulumi.Int(2),
			ParameterGroupName: pulumi.String("default.redis3.2"),
			Port:               pulumi.Int(6379),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.ElastiCache.ReplicationGroup("example", new()
    {
        AutomaticFailoverEnabled = true,
        PreferredCacheClusterAzs = new[]
        {
            "us-west-2a",
            "us-west-2b",
        },
        ReplicationGroupId = "tf-rep-group-1",
        Description = "example description",
        NodeType = "cache.m4.large",
        NumCacheClusters = 2,
        ParameterGroupName = "default.redis3.2",
        Port = 6379,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elasticache.ReplicationGroup;
import com.pulumi.aws.elasticache.ReplicationGroupArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new ReplicationGroup("example", ReplicationGroupArgs.builder()
            .automaticFailoverEnabled(true)
            .preferredCacheClusterAzs(            
                "us-west-2a",
                "us-west-2b")
            .replicationGroupId("tf-rep-group-1")
            .description("example description")
            .nodeType("cache.m4.large")
            .numCacheClusters(2)
            .parameterGroupName("default.redis3.2")
            .port(6379)
            .build());

    }
}
resources:
  example:
    type: aws:elasticache:ReplicationGroup
    properties:
      automaticFailoverEnabled: true
      preferredCacheClusterAzs:
        - us-west-2a
        - us-west-2b
      replicationGroupId: tf-rep-group-1
      description: example description
      nodeType: cache.m4.large
      numCacheClusters: 2
      parameterGroupName: default.redis3.2
      port: 6379

When the primary fails, ElastiCache automatically promotes a replica to primary. The automaticFailoverEnabled property enables this behavior, and numCacheClusters sets the total count (primary plus replicas). The preferredCacheClusterAzs list controls where nodes are placed; the first zone gets the primary. The parameterGroupName must reference a parameter group compatible with your engine version.

Enable cluster mode for data sharding

Applications that need to scale beyond a single shard’s capacity use cluster mode to distribute data across multiple node groups. Each shard has its own primary and replicas.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const baz = new aws.elasticache.ReplicationGroup("baz", {
    replicationGroupId: "tf-redis-cluster",
    description: "example description",
    nodeType: "cache.t2.small",
    port: 6379,
    parameterGroupName: "default.redis3.2.cluster.on",
    automaticFailoverEnabled: true,
    numNodeGroups: 2,
    replicasPerNodeGroup: 1,
});
import pulumi
import pulumi_aws as aws

baz = aws.elasticache.ReplicationGroup("baz",
    replication_group_id="tf-redis-cluster",
    description="example description",
    node_type="cache.t2.small",
    port=6379,
    parameter_group_name="default.redis3.2.cluster.on",
    automatic_failover_enabled=True,
    num_node_groups=2,
    replicas_per_node_group=1)
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/elasticache"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := elasticache.NewReplicationGroup(ctx, "baz", &elasticache.ReplicationGroupArgs{
			ReplicationGroupId:       pulumi.String("tf-redis-cluster"),
			Description:              pulumi.String("example description"),
			NodeType:                 pulumi.String("cache.t2.small"),
			Port:                     pulumi.Int(6379),
			ParameterGroupName:       pulumi.String("default.redis3.2.cluster.on"),
			AutomaticFailoverEnabled: pulumi.Bool(true),
			NumNodeGroups:            pulumi.Int(2),
			ReplicasPerNodeGroup:     pulumi.Int(1),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var baz = new Aws.ElastiCache.ReplicationGroup("baz", new()
    {
        ReplicationGroupId = "tf-redis-cluster",
        Description = "example description",
        NodeType = "cache.t2.small",
        Port = 6379,
        ParameterGroupName = "default.redis3.2.cluster.on",
        AutomaticFailoverEnabled = true,
        NumNodeGroups = 2,
        ReplicasPerNodeGroup = 1,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elasticache.ReplicationGroup;
import com.pulumi.aws.elasticache.ReplicationGroupArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var baz = new ReplicationGroup("baz", ReplicationGroupArgs.builder()
            .replicationGroupId("tf-redis-cluster")
            .description("example description")
            .nodeType("cache.t2.small")
            .port(6379)
            .parameterGroupName("default.redis3.2.cluster.on")
            .automaticFailoverEnabled(true)
            .numNodeGroups(2)
            .replicasPerNodeGroup(1)
            .build());

    }
}
resources:
  baz:
    type: aws:elasticache:ReplicationGroup
    properties:
      replicationGroupId: tf-redis-cluster
      description: example description
      nodeType: cache.t2.small
      port: 6379
      parameterGroupName: default.redis3.2.cluster.on
      automaticFailoverEnabled: true
      numNodeGroups: 2
      replicasPerNodeGroup: 1

In cluster mode, data is partitioned across shards using consistent hashing. The numNodeGroups property sets the shard count, and replicasPerNodeGroup controls how many read replicas each shard has. The parameterGroupName must reference a parameter group with cluster-enabled set to true (parameter groups ending in .cluster.on). Automatic failover is required for cluster mode.

Control shard placement and keyspace distribution

When you need precise control over where shards run and how keys are distributed, you can specify availability zones and slot ranges for each node group.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.elasticache.ReplicationGroup("example", {
    replicationGroupId: "tf-redis-cluster",
    description: "example description",
    nodeType: "cache.t2.small",
    port: 6379,
    parameterGroupName: "default.redis3.2.cluster.on",
    automaticFailoverEnabled: true,
    numNodeGroups: 2,
    nodeGroupConfigurations: [
        {
            nodeGroupId: "0001",
            primaryAvailabilityZone: "us-west-2a",
            replicaAvailabilityZones: ["us-west-2b"],
            replicaCount: 1,
            slots: "0-8191",
        },
        {
            nodeGroupId: "0002",
            primaryAvailabilityZone: "us-west-2b",
            replicaAvailabilityZones: ["us-west-2a"],
            replicaCount: 1,
            slots: "8192-16383",
        },
    ],
});
import pulumi
import pulumi_aws as aws

example = aws.elasticache.ReplicationGroup("example",
    replication_group_id="tf-redis-cluster",
    description="example description",
    node_type="cache.t2.small",
    port=6379,
    parameter_group_name="default.redis3.2.cluster.on",
    automatic_failover_enabled=True,
    num_node_groups=2,
    node_group_configurations=[
        {
            "node_group_id": "0001",
            "primary_availability_zone": "us-west-2a",
            "replica_availability_zones": ["us-west-2b"],
            "replica_count": 1,
            "slots": "0-8191",
        },
        {
            "node_group_id": "0002",
            "primary_availability_zone": "us-west-2b",
            "replica_availability_zones": ["us-west-2a"],
            "replica_count": 1,
            "slots": "8192-16383",
        },
    ])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/elasticache"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := elasticache.NewReplicationGroup(ctx, "example", &elasticache.ReplicationGroupArgs{
			ReplicationGroupId:       pulumi.String("tf-redis-cluster"),
			Description:              pulumi.String("example description"),
			NodeType:                 pulumi.String("cache.t2.small"),
			Port:                     pulumi.Int(6379),
			ParameterGroupName:       pulumi.String("default.redis3.2.cluster.on"),
			AutomaticFailoverEnabled: pulumi.Bool(true),
			NumNodeGroups:            pulumi.Int(2),
			NodeGroupConfigurations: elasticache.ReplicationGroupNodeGroupConfigurationArray{
				&elasticache.ReplicationGroupNodeGroupConfigurationArgs{
					NodeGroupId:             pulumi.String("0001"),
					PrimaryAvailabilityZone: pulumi.String("us-west-2a"),
					ReplicaAvailabilityZones: pulumi.StringArray{
						pulumi.String("us-west-2b"),
					},
					ReplicaCount: pulumi.Int(1),
					Slots:        pulumi.String("0-8191"),
				},
				&elasticache.ReplicationGroupNodeGroupConfigurationArgs{
					NodeGroupId:             pulumi.String("0002"),
					PrimaryAvailabilityZone: pulumi.String("us-west-2b"),
					ReplicaAvailabilityZones: pulumi.StringArray{
						pulumi.String("us-west-2a"),
					},
					ReplicaCount: pulumi.Int(1),
					Slots:        pulumi.String("8192-16383"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.ElastiCache.ReplicationGroup("example", new()
    {
        ReplicationGroupId = "tf-redis-cluster",
        Description = "example description",
        NodeType = "cache.t2.small",
        Port = 6379,
        ParameterGroupName = "default.redis3.2.cluster.on",
        AutomaticFailoverEnabled = true,
        NumNodeGroups = 2,
        NodeGroupConfigurations = new[]
        {
            new Aws.ElastiCache.Inputs.ReplicationGroupNodeGroupConfigurationArgs
            {
                NodeGroupId = "0001",
                PrimaryAvailabilityZone = "us-west-2a",
                ReplicaAvailabilityZones = new[]
                {
                    "us-west-2b",
                },
                ReplicaCount = 1,
                Slots = "0-8191",
            },
            new Aws.ElastiCache.Inputs.ReplicationGroupNodeGroupConfigurationArgs
            {
                NodeGroupId = "0002",
                PrimaryAvailabilityZone = "us-west-2b",
                ReplicaAvailabilityZones = new[]
                {
                    "us-west-2a",
                },
                ReplicaCount = 1,
                Slots = "8192-16383",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elasticache.ReplicationGroup;
import com.pulumi.aws.elasticache.ReplicationGroupArgs;
import com.pulumi.aws.elasticache.inputs.ReplicationGroupNodeGroupConfigurationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new ReplicationGroup("example", ReplicationGroupArgs.builder()
            .replicationGroupId("tf-redis-cluster")
            .description("example description")
            .nodeType("cache.t2.small")
            .port(6379)
            .parameterGroupName("default.redis3.2.cluster.on")
            .automaticFailoverEnabled(true)
            .numNodeGroups(2)
            .nodeGroupConfigurations(            
                ReplicationGroupNodeGroupConfigurationArgs.builder()
                    .nodeGroupId("0001")
                    .primaryAvailabilityZone("us-west-2a")
                    .replicaAvailabilityZones("us-west-2b")
                    .replicaCount(1)
                    .slots("0-8191")
                    .build(),
                ReplicationGroupNodeGroupConfigurationArgs.builder()
                    .nodeGroupId("0002")
                    .primaryAvailabilityZone("us-west-2b")
                    .replicaAvailabilityZones("us-west-2a")
                    .replicaCount(1)
                    .slots("8192-16383")
                    .build())
            .build());

    }
}
resources:
  example:
    type: aws:elasticache:ReplicationGroup
    properties:
      replicationGroupId: tf-redis-cluster
      description: example description
      nodeType: cache.t2.small
      port: 6379
      parameterGroupName: default.redis3.2.cluster.on
      automaticFailoverEnabled: true
      numNodeGroups: 2
      nodeGroupConfigurations:
        - nodeGroupId: '0001'
          primaryAvailabilityZone: us-west-2a
          replicaAvailabilityZones:
            - us-west-2b
          replicaCount: 1
          slots: 0-8191
        - nodeGroupId: '0002'
          primaryAvailabilityZone: us-west-2b
          replicaAvailabilityZones:
            - us-west-2a
          replicaCount: 1
          slots: 8192-16383

The nodeGroupConfigurations array lets you define each shard explicitly. The nodeGroupId identifies the shard, primaryAvailabilityZone and replicaAvailabilityZones control placement, and slots defines the keyspace range (0-16383 total). This configuration extends cluster mode by pinning shards to specific zones and defining how keys map to shards.

Stream logs to CloudWatch and Kinesis Firehose

For debugging and compliance, you can stream Redis slow logs and engine logs to CloudWatch Logs or Kinesis Firehose for analysis and archival.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const test = new aws.elasticache.ReplicationGroup("test", {
    replicationGroupId: "myreplicaciongroup",
    description: "test description",
    nodeType: "cache.t3.small",
    port: 6379,
    applyImmediately: true,
    autoMinorVersionUpgrade: false,
    maintenanceWindow: "tue:06:30-tue:07:30",
    snapshotWindow: "01:00-02:00",
    logDeliveryConfigurations: [
        {
            destination: example.name,
            destinationType: "cloudwatch-logs",
            logFormat: "text",
            logType: "slow-log",
        },
        {
            destination: exampleAwsKinesisFirehoseDeliveryStream.name,
            destinationType: "kinesis-firehose",
            logFormat: "json",
            logType: "engine-log",
        },
    ],
});
import pulumi
import pulumi_aws as aws

test = aws.elasticache.ReplicationGroup("test",
    replication_group_id="myreplicaciongroup",
    description="test description",
    node_type="cache.t3.small",
    port=6379,
    apply_immediately=True,
    auto_minor_version_upgrade=False,
    maintenance_window="tue:06:30-tue:07:30",
    snapshot_window="01:00-02:00",
    log_delivery_configurations=[
        {
            "destination": example["name"],
            "destination_type": "cloudwatch-logs",
            "log_format": "text",
            "log_type": "slow-log",
        },
        {
            "destination": example_aws_kinesis_firehose_delivery_stream["name"],
            "destination_type": "kinesis-firehose",
            "log_format": "json",
            "log_type": "engine-log",
        },
    ])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/elasticache"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := elasticache.NewReplicationGroup(ctx, "test", &elasticache.ReplicationGroupArgs{
			ReplicationGroupId:      pulumi.String("myreplicaciongroup"),
			Description:             pulumi.String("test description"),
			NodeType:                pulumi.String("cache.t3.small"),
			Port:                    pulumi.Int(6379),
			ApplyImmediately:        pulumi.Bool(true),
			AutoMinorVersionUpgrade: pulumi.Bool(false),
			MaintenanceWindow:       pulumi.String("tue:06:30-tue:07:30"),
			SnapshotWindow:          pulumi.String("01:00-02:00"),
			LogDeliveryConfigurations: elasticache.ReplicationGroupLogDeliveryConfigurationArray{
				&elasticache.ReplicationGroupLogDeliveryConfigurationArgs{
					Destination:     pulumi.Any(example.Name),
					DestinationType: pulumi.String("cloudwatch-logs"),
					LogFormat:       pulumi.String("text"),
					LogType:         pulumi.String("slow-log"),
				},
				&elasticache.ReplicationGroupLogDeliveryConfigurationArgs{
					Destination:     pulumi.Any(exampleAwsKinesisFirehoseDeliveryStream.Name),
					DestinationType: pulumi.String("kinesis-firehose"),
					LogFormat:       pulumi.String("json"),
					LogType:         pulumi.String("engine-log"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var test = new Aws.ElastiCache.ReplicationGroup("test", new()
    {
        ReplicationGroupId = "myreplicaciongroup",
        Description = "test description",
        NodeType = "cache.t3.small",
        Port = 6379,
        ApplyImmediately = true,
        AutoMinorVersionUpgrade = false,
        MaintenanceWindow = "tue:06:30-tue:07:30",
        SnapshotWindow = "01:00-02:00",
        LogDeliveryConfigurations = new[]
        {
            new Aws.ElastiCache.Inputs.ReplicationGroupLogDeliveryConfigurationArgs
            {
                Destination = example.Name,
                DestinationType = "cloudwatch-logs",
                LogFormat = "text",
                LogType = "slow-log",
            },
            new Aws.ElastiCache.Inputs.ReplicationGroupLogDeliveryConfigurationArgs
            {
                Destination = exampleAwsKinesisFirehoseDeliveryStream.Name,
                DestinationType = "kinesis-firehose",
                LogFormat = "json",
                LogType = "engine-log",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elasticache.ReplicationGroup;
import com.pulumi.aws.elasticache.ReplicationGroupArgs;
import com.pulumi.aws.elasticache.inputs.ReplicationGroupLogDeliveryConfigurationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var test = new ReplicationGroup("test", ReplicationGroupArgs.builder()
            .replicationGroupId("myreplicaciongroup")
            .description("test description")
            .nodeType("cache.t3.small")
            .port(6379)
            .applyImmediately(true)
            .autoMinorVersionUpgrade(false)
            .maintenanceWindow("tue:06:30-tue:07:30")
            .snapshotWindow("01:00-02:00")
            .logDeliveryConfigurations(            
                ReplicationGroupLogDeliveryConfigurationArgs.builder()
                    .destination(example.name())
                    .destinationType("cloudwatch-logs")
                    .logFormat("text")
                    .logType("slow-log")
                    .build(),
                ReplicationGroupLogDeliveryConfigurationArgs.builder()
                    .destination(exampleAwsKinesisFirehoseDeliveryStream.name())
                    .destinationType("kinesis-firehose")
                    .logFormat("json")
                    .logType("engine-log")
                    .build())
            .build());

    }
}
resources:
  test:
    type: aws:elasticache:ReplicationGroup
    properties:
      replicationGroupId: myreplicaciongroup
      description: test description
      nodeType: cache.t3.small
      port: 6379
      applyImmediately: true
      autoMinorVersionUpgrade: false
      maintenanceWindow: tue:06:30-tue:07:30
      snapshotWindow: 01:00-02:00
      logDeliveryConfigurations:
        - destination: ${example.name}
          destinationType: cloudwatch-logs
          logFormat: text
          logType: slow-log
        - destination: ${exampleAwsKinesisFirehoseDeliveryStream.name}
          destinationType: kinesis-firehose
          logFormat: json
          logType: engine-log

The logDeliveryConfigurations array defines where logs go. Each entry specifies a destination (log group or Firehose stream name), destinationType (cloudwatch-logs or kinesis-firehose), logFormat (text or json), and logType (slow-log or engine-log). Slow logs capture queries exceeding a threshold; engine logs capture Redis operational events.

Enable authentication and encryption in transit

Production deployments often require password authentication and encrypted connections to protect data in transit between clients and Redis nodes.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.elasticache.ReplicationGroup("example", {
    replicationGroupId: "example",
    description: "example with authentication",
    nodeType: "cache.t2.micro",
    numCacheClusters: 1,
    port: 6379,
    subnetGroupName: exampleAwsElasticacheSubnetGroup.name,
    securityGroupIds: [exampleAwsSecurityGroup.id],
    parameterGroupName: "default.redis5.0",
    engineVersion: "5.0.6",
    transitEncryptionEnabled: true,
    authToken: "abcdefgh1234567890",
    authTokenUpdateStrategy: "ROTATE",
});
import pulumi
import pulumi_aws as aws

example = aws.elasticache.ReplicationGroup("example",
    replication_group_id="example",
    description="example with authentication",
    node_type="cache.t2.micro",
    num_cache_clusters=1,
    port=6379,
    subnet_group_name=example_aws_elasticache_subnet_group["name"],
    security_group_ids=[example_aws_security_group["id"]],
    parameter_group_name="default.redis5.0",
    engine_version="5.0.6",
    transit_encryption_enabled=True,
    auth_token="abcdefgh1234567890",
    auth_token_update_strategy="ROTATE")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/elasticache"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := elasticache.NewReplicationGroup(ctx, "example", &elasticache.ReplicationGroupArgs{
			ReplicationGroupId: pulumi.String("example"),
			Description:        pulumi.String("example with authentication"),
			NodeType:           pulumi.String("cache.t2.micro"),
			NumCacheClusters:   pulumi.Int(1),
			Port:               pulumi.Int(6379),
			SubnetGroupName:    pulumi.Any(exampleAwsElasticacheSubnetGroup.Name),
			SecurityGroupIds: pulumi.StringArray{
				exampleAwsSecurityGroup.Id,
			},
			ParameterGroupName:       pulumi.String("default.redis5.0"),
			EngineVersion:            pulumi.String("5.0.6"),
			TransitEncryptionEnabled: pulumi.Bool(true),
			AuthToken:                pulumi.String("abcdefgh1234567890"),
			AuthTokenUpdateStrategy:  pulumi.String("ROTATE"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.ElastiCache.ReplicationGroup("example", new()
    {
        ReplicationGroupId = "example",
        Description = "example with authentication",
        NodeType = "cache.t2.micro",
        NumCacheClusters = 1,
        Port = 6379,
        SubnetGroupName = exampleAwsElasticacheSubnetGroup.Name,
        SecurityGroupIds = new[]
        {
            exampleAwsSecurityGroup.Id,
        },
        ParameterGroupName = "default.redis5.0",
        EngineVersion = "5.0.6",
        TransitEncryptionEnabled = true,
        AuthToken = "abcdefgh1234567890",
        AuthTokenUpdateStrategy = "ROTATE",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elasticache.ReplicationGroup;
import com.pulumi.aws.elasticache.ReplicationGroupArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new ReplicationGroup("example", ReplicationGroupArgs.builder()
            .replicationGroupId("example")
            .description("example with authentication")
            .nodeType("cache.t2.micro")
            .numCacheClusters(1)
            .port(6379)
            .subnetGroupName(exampleAwsElasticacheSubnetGroup.name())
            .securityGroupIds(exampleAwsSecurityGroup.id())
            .parameterGroupName("default.redis5.0")
            .engineVersion("5.0.6")
            .transitEncryptionEnabled(true)
            .authToken("abcdefgh1234567890")
            .authTokenUpdateStrategy("ROTATE")
            .build());

    }
}
resources:
  example:
    type: aws:elasticache:ReplicationGroup
    properties:
      replicationGroupId: example
      description: example with authentication
      nodeType: cache.t2.micro
      numCacheClusters: 1
      port: 6379
      subnetGroupName: ${exampleAwsElasticacheSubnetGroup.name}
      securityGroupIds:
        - ${exampleAwsSecurityGroup.id}
      parameterGroupName: default.redis5.0
      engineVersion: 5.0.6
      transitEncryptionEnabled: true
      authToken: abcdefgh1234567890
      authTokenUpdateStrategy: ROTATE

The transitEncryptionEnabled property enables TLS encryption for client connections. The authToken property sets the password clients must provide. The authTokenUpdateStrategy controls how token changes are applied: ROTATE allows both old and new tokens temporarily, SET requires the new token immediately. Transit encryption requires a subnet group and security groups that allow encrypted traffic.

Beyond these examples

These snippets focus on specific replication group features: single-shard and multi-shard cluster topologies, availability zone placement and keyspace distribution, and log delivery and authentication. They’re intentionally minimal rather than full Redis deployments.

The examples may reference pre-existing infrastructure such as VPC subnet groups and security groups, CloudWatch log groups and Kinesis Firehose streams, and parameter groups with cluster mode enabled. They focus on configuring the replication group rather than provisioning everything around it.

To keep things focused, common replication group patterns are omitted, including:

  • Encryption at rest (atRestEncryptionEnabled, kmsKeyId)
  • Snapshot configuration (snapshotWindow, snapshotRetentionLimit)
  • Global replication groups for multi-region deployments
  • Maintenance windows and version upgrade controls
  • Multi-AZ configuration (multiAzEnabled)
  • User group associations for RBAC

These omissions are intentional: the goal is to illustrate how each replication group feature is wired, not provide drop-in Redis modules. See the ElastiCache Replication Group resource reference for all available configuration options.

Let's create AWS ElastiCache Replication Groups

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Cluster Mode & Configuration
How do I enable cluster mode for data sharding?
Use a parameter group with cluster-enabled set to true (e.g., default.redis6.x.cluster.on), then configure num_node_groups (number of shards) and replicas_per_node_group (0-5 replicas per shard).
What's the difference between num_cache_clusters and num_node_groups?
num_cache_clusters is for Cluster Mode Disabled (single shard with replicas, default 1). num_node_groups is for Cluster Mode Enabled (multiple shards). These parameters conflict with each other.
Why is the term 'cluster' confusing for ElastiCache Replication Groups?
You can create a “Cluster Mode Disabled Redis Cluster” (single shard) or enable “Cluster Mode” (data sharding across multiple shards). The term “cluster” refers to different concepts depending on context.
Updates & Maintenance
How does apply_immediately affect updates and downtime?
By default (apply_immediately = false), changes are applied during the next maintenance window. Setting it to true applies changes immediately but can cause brief downtime as servers reboot.
Will apply_immediately prevent downtime for all changes?
No. Any attribute changes that re-create the resource will be applied immediately and cause downtime, regardless of the apply_immediately value.
How should I specify engine_version for different Redis versions?
Version 7+: use major.minor (e.g., 7.2). Version 6: use major.minor (e.g., 6.2) or 6.x for latest. Version 5 and below: specify full version (e.g., 5.0.6).
High Availability & Failover
What are the requirements for enabling automatic failover?
Automatic failover requires Redis 2.8.6+ (or 3.2.4+ for T2 nodes with cluster mode), cannot use T1 node types, and num_cache_clusters must be at least 2. It’s required for cluster mode enabled replication groups.
How many cache clusters do I need for automatic failover or multi-AZ?
If automatic_failover_enabled or multi_az_enabled is true, num_cache_clusters must be at least 2 (one primary and at least one replica).
Security & Encryption
Will enabling transit encryption on an existing replication group cause downtime?
For engine versions below 7.0.5, changing transit_encryption_enabled forces resource replacement and downtime. Version 7.0.5+ allows enabling it without replacement.
How do I enable transit encryption on an existing replication group without downtime?
Set transit_encryption_mode to preferred first (allows both encrypted and unencrypted connections), then set it to required in a subsequent apply after clients migrate.
What's the difference between ROTATE and SET auth token strategies?
ROTATE allows both the new token and passwordless authentication (gradual migration). SET immediately requires the new token (no passwordless access). Use SET when adding the initial token for immediate authorization.
What are the default encryption settings for redis vs valkey engines?
For redis engine, at_rest_encryption_enabled defaults to false. For valkey engine, it defaults to true. This property is immutable after creation.
Scaling & Replicas
How do I adjust the number of replicas in my replication group?
You have two options: (1) Adjust num_cache_clusters directly for automatic scaling, or (2) Use aws.elasticache.Cluster resources with replication_group_id for fine-grained control over AZ placement and cluster IDs (requires ignoreChanges on num_cache_clusters).
Snapshots & Backups
Can I enable snapshot retention on all node types?
No. Setting snapshot_retention_limit is not supported on cache.t1.micro nodes. Use a different node type if you need snapshot retention.

Using a different cloud?

Explore database guides for other cloud providers: