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, optional sharding, and high availability features. This guide focuses on three capabilities: single-shard and multi-shard cluster configurations, encryption and authentication, and log delivery to CloudWatch and Kinesis.

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

Create a single-shard cluster with automatic failover

Most Redis deployments begin with a simple replication group: one primary node and one or more read replicas for high availability.

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 automaticFailoverEnabled is true, ElastiCache promotes a replica to primary if the current primary fails. The numCacheClusters property sets the total count (primary plus replicas); with automatic failover enabled, you need at least 2. The preferredCacheClusterAzs list controls placement, with the first zone hosting the primary. The parameterGroupName determines Redis configuration; use a parameter group with cluster-enabled set to false for single-shard mode.

Enable cluster mode for horizontal scaling

Applications that need to scale beyond a single shard use cluster mode, which distributes data across multiple node groups.

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

Cluster mode shards data using hash slots. The numNodeGroups property sets the number of shards, and replicasPerNodeGroup controls how many replicas each shard has. The parameterGroupName must reference a parameter group with cluster-enabled set to true (AWS provides defaults like default.redis3.2.cluster.on). Automatic failover is required for cluster mode.

Control shard placement and keyspace distribution

For precise control over availability zone placement and hash slot distribution, you can configure each node group individually.

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 specify exactly where each shard runs and which hash slots it owns. Each configuration sets a nodeGroupId, the primaryAvailabilityZone, replicaAvailabilityZones for replicas, and the slots range (Redis uses 16,384 hash slots total). This gives you control over data locality and failure domain isolation.

Stream logs to CloudWatch and Kinesis Firehose

Production clusters often need to capture slow queries and engine logs for debugging and performance analysis.

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 and in what format. Each configuration specifies a logType (slow-log or engine-log), a destinationType (cloudwatch-logs or kinesis-firehose), the destination name, and a logFormat (text or json). Slow logs capture queries that exceed a threshold; engine logs record Redis operational events.

Secure connections with encryption and authentication

Sensitive workloads require encryption in transit and password authentication to prevent unauthorized access.

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

When transitEncryptionEnabled is true, all client connections use TLS. The authToken property sets a password for Redis AUTH; it only works when transit encryption is enabled. The authTokenUpdateStrategy controls how token changes are applied: SET requires the new token immediately, ROTATE allows both old and new tokens temporarily, and DELETE removes authentication. The subnetGroupName and securityGroupIds place the cluster in your VPC with network-level access control.

Beyond these examples

These snippets focus on specific replication group features: cluster mode and sharding configuration, encryption and authentication, and log delivery and monitoring. 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:

  • Global replication groups for multi-region deployments
  • Snapshot and backup configuration (snapshotRetentionLimit, snapshotWindow)
  • Maintenance windows and version upgrade controls
  • Multi-AZ configuration (multiAzEnabled)
  • Data tiering for r6gd node types

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 ReplicationGroup 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
What's the difference between Cluster Mode Disabled and Enabled?
The term “cluster” is confusing in ElastiCache. You can create a “Cluster Mode Disabled [Redis] Cluster” (single shard) or a “Cluster Mode Enabled” setup where data is stored in multiple shards (called “node groups”). To enable cluster mode, use a parameter group with cluster-enabled set to true, such as default.redis6.x.cluster.on.
Should I use num_cache_clusters or num_node_groups?
Use num_cache_clusters for Cluster Mode Disabled (single shard with replicas). Use num_node_groups and replicas_per_node_group for Cluster Mode Enabled (multiple shards). These parameters conflict with each other and cannot be used together.
How do I scale replicas in Cluster Mode Disabled?
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 availability zones and cluster IDs. Option 2 requires using ignoreChanges on num_cache_clusters.
Updates & Maintenance
When are configuration changes applied to my replication group?
By default, changes like engine_version are applied during the next maintenance window. Set apply_immediately to true to apply changes immediately, but this can cause brief downtime due to server reboots. Note that any changes forcing resource re-creation are always applied immediately, regardless of apply_immediately.
How should I specify the engine version?
For version 7 or higher, set major and minor version (e.g., 7.2). For version 6, you can set major.minor (e.g., 6.2) or use 6.x to get the latest minor version at creation time. For earlier versions, specify the full version (e.g., 5.0.6). The actual version used is returned in engine_version_actual.
Are minor version upgrades applied automatically?
Yes, by default. The auto_minor_version_upgrade parameter defaults to true for Redis and Valkey engines with version 6 or higher. Minor version upgrades are applied during the maintenance window.
High Availability & Failover
What are the requirements for enabling automatic failover?
Automatic failover requires: (1) Redis version 2.8.6 or later, (2) not using T1 node types, (3) num_cache_clusters must be at least 2. For T2 node types, you need Redis 3.2.4 or later with cluster mode enabled. Automatic failover is required for cluster mode enabled replication groups.
What's the relationship between automatic_failover_enabled and multi_az_enabled?
If multi_az_enabled is true, then automatic_failover_enabled must also be true, and num_cache_clusters must be at least 2. Both default to false.
Security & Encryption
What's the difference between ROTATE and SET when updating auth tokens?
When adding a new auth_token to a previously passwordless replication group, ROTATE allows both the new token and passwordless authentication. To immediately require authorization, use SET instead. If omitted, AWS defaults to ROTATE.
How do I enable encryption in transit on an existing replication group?
For zero-downtime migration, first set transit_encryption_mode to preferred, then in a subsequent apply set it to required. Note that changing transit_encryption_enabled with engine_version less than 7.0.5 will force resource replacement.
What's the default encryption behavior for Redis vs Valkey?
For at_rest_encryption_enabled: Redis defaults to false, while Valkey defaults to true. This property is immutable after creation.
Scaling & Replicas
What's the default limit for replicas per node group?
The default AWS limit is 5 replicas per node group. Higher values may be available with a quota increase. Use replicas_per_node_group to configure this (only valid when num_node_groups is set).
Can I use num_node_groups with a global replication group?
No. If global_replication_group_id is set, you cannot set num_node_groups. The global replication group controls the number of node groups.
Snapshots & Special Node Types
Why can't I enable snapshot retention on my cache.t1.micro node?
Setting snapshot_retention_limit is not supported on cache.t1.micro nodes. You’ll need to use a different node type to enable automatic snapshots.
What's required when using r6gd node types?
When using r6gd nodes, you must set data_tiering_enabled to true. Data tiering is only supported for r6gd node types, and this property is immutable after creation.

Using a different cloud?

Explore database guides for other cloud providers: