Create AWS ElastiCache Clusters

The aws:elasticache/cluster:Cluster resource, part of the Pulumi AWS provider, provisions ElastiCache clusters: Memcached clusters, single-node Redis instances, or read replicas in Redis replication groups. This guide focuses on three capabilities: engine configuration, log delivery to monitoring systems, and Outposts deployment.

Clusters run in a VPC with security groups and subnet groups. Log delivery requires CloudWatch log groups or Kinesis Firehose streams. The examples are intentionally small. Combine them with your own VPC configuration, security groups, and monitoring infrastructure.

Create a Memcached cluster for distributed caching

Applications that need to cache session data or frequently accessed objects often start with Memcached for its simple key-value storage and horizontal scaling.

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

const example = new aws.elasticache.Cluster("example", {
    clusterId: "cluster-example",
    engine: "memcached",
    nodeType: "cache.m4.large",
    numCacheNodes: 2,
    parameterGroupName: "default.memcached1.4",
    port: 11211,
});
import pulumi
import pulumi_aws as aws

example = aws.elasticache.Cluster("example",
    cluster_id="cluster-example",
    engine="memcached",
    node_type="cache.m4.large",
    num_cache_nodes=2,
    parameter_group_name="default.memcached1.4",
    port=11211)
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.NewCluster(ctx, "example", &elasticache.ClusterArgs{
			ClusterId:          pulumi.String("cluster-example"),
			Engine:             pulumi.String("memcached"),
			NodeType:           pulumi.String("cache.m4.large"),
			NumCacheNodes:      pulumi.Int(2),
			ParameterGroupName: pulumi.String("default.memcached1.4"),
			Port:               pulumi.Int(11211),
		})
		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.Cluster("example", new()
    {
        ClusterId = "cluster-example",
        Engine = "memcached",
        NodeType = "cache.m4.large",
        NumCacheNodes = 2,
        ParameterGroupName = "default.memcached1.4",
        Port = 11211,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elasticache.Cluster;
import com.pulumi.aws.elasticache.ClusterArgs;
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 Cluster("example", ClusterArgs.builder()
            .clusterId("cluster-example")
            .engine("memcached")
            .nodeType("cache.m4.large")
            .numCacheNodes(2)
            .parameterGroupName("default.memcached1.4")
            .port(11211)
            .build());

    }
}
resources:
  example:
    type: aws:elasticache:Cluster
    properties:
      clusterId: cluster-example
      engine: memcached
      nodeType: cache.m4.large
      numCacheNodes: 2
      parameterGroupName: default.memcached1.4
      port: 11211

The engine property selects Memcached as the cache engine. The numCacheNodes property sets the initial cluster size; Memcached supports 1 to 40 nodes. The nodeType determines instance size and memory capacity. The parameterGroupName controls engine-specific settings like max connections and memory allocation.

Deploy a single-node Redis instance

Teams building applications that need persistent key-value storage with data structures often choose Redis for its rich feature set and atomic operations.

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

const example = new aws.elasticache.Cluster("example", {
    clusterId: "cluster-example",
    engine: "redis",
    nodeType: "cache.m4.large",
    numCacheNodes: 1,
    parameterGroupName: "default.redis3.2",
    engineVersion: "3.2.10",
    port: 6379,
});
import pulumi
import pulumi_aws as aws

example = aws.elasticache.Cluster("example",
    cluster_id="cluster-example",
    engine="redis",
    node_type="cache.m4.large",
    num_cache_nodes=1,
    parameter_group_name="default.redis3.2",
    engine_version="3.2.10",
    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.NewCluster(ctx, "example", &elasticache.ClusterArgs{
			ClusterId:          pulumi.String("cluster-example"),
			Engine:             pulumi.String("redis"),
			NodeType:           pulumi.String("cache.m4.large"),
			NumCacheNodes:      pulumi.Int(1),
			ParameterGroupName: pulumi.String("default.redis3.2"),
			EngineVersion:      pulumi.String("3.2.10"),
			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.Cluster("example", new()
    {
        ClusterId = "cluster-example",
        Engine = "redis",
        NodeType = "cache.m4.large",
        NumCacheNodes = 1,
        ParameterGroupName = "default.redis3.2",
        EngineVersion = "3.2.10",
        Port = 6379,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elasticache.Cluster;
import com.pulumi.aws.elasticache.ClusterArgs;
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 Cluster("example", ClusterArgs.builder()
            .clusterId("cluster-example")
            .engine("redis")
            .nodeType("cache.m4.large")
            .numCacheNodes(1)
            .parameterGroupName("default.redis3.2")
            .engineVersion("3.2.10")
            .port(6379)
            .build());

    }
}
resources:
  example:
    type: aws:elasticache:Cluster
    properties:
      clusterId: cluster-example
      engine: redis
      nodeType: cache.m4.large
      numCacheNodes: 1
      parameterGroupName: default.redis3.2
      engineVersion: 3.2.10
      port: 6379

Redis clusters in standalone mode must have exactly one node (numCacheNodes: 1). The engineVersion property pins the Redis version; ElastiCache supports version-specific features like Redis 7’s improved memory efficiency. For multi-node Redis with automatic failover, use the ReplicationGroup resource instead.

Stream Redis logs to CloudWatch and Kinesis

Production Redis deployments often need to capture slow queries and engine logs for troubleshooting and performance analysis.

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

const test = new aws.elasticache.Cluster("test", {
    clusterId: "mycluster",
    engine: "redis",
    nodeType: "cache.t3.micro",
    numCacheNodes: 1,
    port: 6379,
    applyImmediately: true,
    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.Cluster("test",
    cluster_id="mycluster",
    engine="redis",
    node_type="cache.t3.micro",
    num_cache_nodes=1,
    port=6379,
    apply_immediately=True,
    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.NewCluster(ctx, "test", &elasticache.ClusterArgs{
			ClusterId:        pulumi.String("mycluster"),
			Engine:           pulumi.String("redis"),
			NodeType:         pulumi.String("cache.t3.micro"),
			NumCacheNodes:    pulumi.Int(1),
			Port:             pulumi.Int(6379),
			ApplyImmediately: pulumi.Bool(true),
			LogDeliveryConfigurations: elasticache.ClusterLogDeliveryConfigurationArray{
				&elasticache.ClusterLogDeliveryConfigurationArgs{
					Destination:     pulumi.Any(example.Name),
					DestinationType: pulumi.String("cloudwatch-logs"),
					LogFormat:       pulumi.String("text"),
					LogType:         pulumi.String("slow-log"),
				},
				&elasticache.ClusterLogDeliveryConfigurationArgs{
					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.Cluster("test", new()
    {
        ClusterId = "mycluster",
        Engine = "redis",
        NodeType = "cache.t3.micro",
        NumCacheNodes = 1,
        Port = 6379,
        ApplyImmediately = true,
        LogDeliveryConfigurations = new[]
        {
            new Aws.ElastiCache.Inputs.ClusterLogDeliveryConfigurationArgs
            {
                Destination = example.Name,
                DestinationType = "cloudwatch-logs",
                LogFormat = "text",
                LogType = "slow-log",
            },
            new Aws.ElastiCache.Inputs.ClusterLogDeliveryConfigurationArgs
            {
                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.Cluster;
import com.pulumi.aws.elasticache.ClusterArgs;
import com.pulumi.aws.elasticache.inputs.ClusterLogDeliveryConfigurationArgs;
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 Cluster("test", ClusterArgs.builder()
            .clusterId("mycluster")
            .engine("redis")
            .nodeType("cache.t3.micro")
            .numCacheNodes(1)
            .port(6379)
            .applyImmediately(true)
            .logDeliveryConfigurations(            
                ClusterLogDeliveryConfigurationArgs.builder()
                    .destination(example.name())
                    .destinationType("cloudwatch-logs")
                    .logFormat("text")
                    .logType("slow-log")
                    .build(),
                ClusterLogDeliveryConfigurationArgs.builder()
                    .destination(exampleAwsKinesisFirehoseDeliveryStream.name())
                    .destinationType("kinesis-firehose")
                    .logFormat("json")
                    .logType("engine-log")
                    .build())
            .build());

    }
}
resources:
  test:
    type: aws:elasticache:Cluster
    properties:
      clusterId: mycluster
      engine: redis
      nodeType: cache.t3.micro
      numCacheNodes: 1
      port: 6379
      applyImmediately: true
      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 routes Redis logs to external systems. Each configuration specifies a logType (slow-log or engine-log), destinationType (cloudwatch-logs or kinesis-firehose), and logFormat (text or json). Slow logs capture queries exceeding execution time thresholds; engine logs record Redis server events like replication and persistence operations.

Deploy a cluster to AWS Outposts

Applications running on AWS Outposts can place ElastiCache clusters locally to minimize latency for on-premises workloads.

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

const example = aws.outposts.getOutposts({});
const exampleGetOutpost = example.then(example => aws.outposts.getOutpost({
    id: example.ids?.[0],
}));
const exampleVpc = new aws.ec2.Vpc("example", {cidrBlock: "10.0.0.0/16"});
const exampleSubnet = new aws.ec2.Subnet("example", {
    vpcId: exampleVpc.id,
    cidrBlock: "10.0.1.0/24",
    tags: {
        Name: "my-subnet",
    },
});
const exampleSubnetGroup = new aws.elasticache.SubnetGroup("example", {
    name: "my-cache-subnet",
    subnetIds: [exampleSubnet.id],
});
const exampleCluster = new aws.elasticache.Cluster("example", {
    clusterId: "cluster-example",
    outpostMode: "single-outpost",
    preferredOutpostArn: exampleGetOutpost.then(exampleGetOutpost => exampleGetOutpost.arn),
    engine: "memcached",
    nodeType: "cache.r5.large",
    numCacheNodes: 2,
    parameterGroupName: "default.memcached1.4",
    port: 11211,
    subnetGroupName: exampleSubnetGroup.name,
});
import pulumi
import pulumi_aws as aws

example = aws.outposts.get_outposts()
example_get_outpost = aws.outposts.get_outpost(id=example.ids[0])
example_vpc = aws.ec2.Vpc("example", cidr_block="10.0.0.0/16")
example_subnet = aws.ec2.Subnet("example",
    vpc_id=example_vpc.id,
    cidr_block="10.0.1.0/24",
    tags={
        "Name": "my-subnet",
    })
example_subnet_group = aws.elasticache.SubnetGroup("example",
    name="my-cache-subnet",
    subnet_ids=[example_subnet.id])
example_cluster = aws.elasticache.Cluster("example",
    cluster_id="cluster-example",
    outpost_mode="single-outpost",
    preferred_outpost_arn=example_get_outpost.arn,
    engine="memcached",
    node_type="cache.r5.large",
    num_cache_nodes=2,
    parameter_group_name="default.memcached1.4",
    port=11211,
    subnet_group_name=example_subnet_group.name)
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := outposts.GetOutposts(ctx, &outposts.GetOutpostsArgs{}, nil)
		if err != nil {
			return err
		}
		exampleGetOutpost, err := outposts.GetOutpost(ctx, &outposts.GetOutpostArgs{
			Id: pulumi.StringRef(example.Ids[0]),
		}, nil)
		if err != nil {
			return err
		}
		exampleVpc, err := ec2.NewVpc(ctx, "example", &ec2.VpcArgs{
			CidrBlock: pulumi.String("10.0.0.0/16"),
		})
		if err != nil {
			return err
		}
		exampleSubnet, err := ec2.NewSubnet(ctx, "example", &ec2.SubnetArgs{
			VpcId:     exampleVpc.ID(),
			CidrBlock: pulumi.String("10.0.1.0/24"),
			Tags: pulumi.StringMap{
				"Name": pulumi.String("my-subnet"),
			},
		})
		if err != nil {
			return err
		}
		exampleSubnetGroup, err := elasticache.NewSubnetGroup(ctx, "example", &elasticache.SubnetGroupArgs{
			Name: pulumi.String("my-cache-subnet"),
			SubnetIds: pulumi.StringArray{
				exampleSubnet.ID(),
			},
		})
		if err != nil {
			return err
		}
		_, err = elasticache.NewCluster(ctx, "example", &elasticache.ClusterArgs{
			ClusterId:           pulumi.String("cluster-example"),
			OutpostMode:         pulumi.String("single-outpost"),
			PreferredOutpostArn: pulumi.String(exampleGetOutpost.Arn),
			Engine:              pulumi.String("memcached"),
			NodeType:            pulumi.String("cache.r5.large"),
			NumCacheNodes:       pulumi.Int(2),
			ParameterGroupName:  pulumi.String("default.memcached1.4"),
			Port:                pulumi.Int(11211),
			SubnetGroupName:     exampleSubnetGroup.Name,
		})
		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 = Aws.Outposts.GetOutposts.Invoke();

    var exampleGetOutpost = Aws.Outposts.GetOutpost.Invoke(new()
    {
        Id = example.Apply(getOutpostsResult => getOutpostsResult.Ids[0]),
    });

    var exampleVpc = new Aws.Ec2.Vpc("example", new()
    {
        CidrBlock = "10.0.0.0/16",
    });

    var exampleSubnet = new Aws.Ec2.Subnet("example", new()
    {
        VpcId = exampleVpc.Id,
        CidrBlock = "10.0.1.0/24",
        Tags = 
        {
            { "Name", "my-subnet" },
        },
    });

    var exampleSubnetGroup = new Aws.ElastiCache.SubnetGroup("example", new()
    {
        Name = "my-cache-subnet",
        SubnetIds = new[]
        {
            exampleSubnet.Id,
        },
    });

    var exampleCluster = new Aws.ElastiCache.Cluster("example", new()
    {
        ClusterId = "cluster-example",
        OutpostMode = "single-outpost",
        PreferredOutpostArn = exampleGetOutpost.Apply(getOutpostResult => getOutpostResult.Arn),
        Engine = "memcached",
        NodeType = "cache.r5.large",
        NumCacheNodes = 2,
        ParameterGroupName = "default.memcached1.4",
        Port = 11211,
        SubnetGroupName = exampleSubnetGroup.Name,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.outposts.OutpostsFunctions;
import com.pulumi.aws.outposts.inputs.GetOutpostsArgs;
import com.pulumi.aws.outposts.inputs.GetOutpostArgs;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.Subnet;
import com.pulumi.aws.ec2.SubnetArgs;
import com.pulumi.aws.elasticache.SubnetGroup;
import com.pulumi.aws.elasticache.SubnetGroupArgs;
import com.pulumi.aws.elasticache.Cluster;
import com.pulumi.aws.elasticache.ClusterArgs;
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) {
        final var example = OutpostsFunctions.getOutposts(GetOutpostsArgs.builder()
            .build());

        final var exampleGetOutpost = OutpostsFunctions.getOutpost(GetOutpostArgs.builder()
            .id(example.ids()[0])
            .build());

        var exampleVpc = new Vpc("exampleVpc", VpcArgs.builder()
            .cidrBlock("10.0.0.0/16")
            .build());

        var exampleSubnet = new Subnet("exampleSubnet", SubnetArgs.builder()
            .vpcId(exampleVpc.id())
            .cidrBlock("10.0.1.0/24")
            .tags(Map.of("Name", "my-subnet"))
            .build());

        var exampleSubnetGroup = new SubnetGroup("exampleSubnetGroup", SubnetGroupArgs.builder()
            .name("my-cache-subnet")
            .subnetIds(exampleSubnet.id())
            .build());

        var exampleCluster = new Cluster("exampleCluster", ClusterArgs.builder()
            .clusterId("cluster-example")
            .outpostMode("single-outpost")
            .preferredOutpostArn(exampleGetOutpost.arn())
            .engine("memcached")
            .nodeType("cache.r5.large")
            .numCacheNodes(2)
            .parameterGroupName("default.memcached1.4")
            .port(11211)
            .subnetGroupName(exampleSubnetGroup.name())
            .build());

    }
}
resources:
  exampleVpc:
    type: aws:ec2:Vpc
    name: example
    properties:
      cidrBlock: 10.0.0.0/16
  exampleSubnet:
    type: aws:ec2:Subnet
    name: example
    properties:
      vpcId: ${exampleVpc.id}
      cidrBlock: 10.0.1.0/24
      tags:
        Name: my-subnet
  exampleSubnetGroup:
    type: aws:elasticache:SubnetGroup
    name: example
    properties:
      name: my-cache-subnet
      subnetIds:
        - ${exampleSubnet.id}
  exampleCluster:
    type: aws:elasticache:Cluster
    name: example
    properties:
      clusterId: cluster-example
      outpostMode: single-outpost
      preferredOutpostArn: ${exampleGetOutpost.arn}
      engine: memcached
      nodeType: cache.r5.large
      numCacheNodes: 2
      parameterGroupName: default.memcached1.4
      port: 11211
      subnetGroupName: ${exampleSubnetGroup.name}
variables:
  example:
    fn::invoke:
      function: aws:outposts:getOutposts
      arguments: {}
  exampleGetOutpost:
    fn::invoke:
      function: aws:outposts:getOutpost
      arguments:
        id: ${example.ids[0]}

The outpostMode property must be set to “single-outpost” (AWS currently supports only this mode). The preferredOutpostArn identifies the Outpost where the cluster runs. The subnetGroupName places nodes in subnets associated with the Outpost’s local gateway.

Beyond these examples

These snippets focus on specific cluster-level features: Memcached and Redis engine selection, log delivery to CloudWatch and Kinesis, and Outposts deployment. They’re intentionally minimal rather than full caching solutions.

The examples typically rely on pre-existing infrastructure such as default VPC and security groups, CloudWatch log groups and Kinesis Firehose streams for log delivery, and AWS Outposts infrastructure for Outpost deployment. They focus on configuring the cluster rather than provisioning everything around it.

To keep things focused, common cluster patterns are omitted, including:

  • VPC networking and security groups (vpcSecurityGroupIds, subnetGroupName)
  • Encryption in-transit (transitEncryptionEnabled)
  • Snapshot and backup configuration (snapshotRetentionLimit, snapshotWindow)
  • Multi-AZ placement (azMode, preferredAvailabilityZones)
  • Maintenance windows and immediate changes (maintenanceWindow, applyImmediately)
  • Read replicas in replication groups (replicationGroupId)

These omissions are intentional: the goal is to illustrate how each cluster feature is wired, not provide drop-in caching modules. See the ElastiCache Cluster resource reference for all available configuration options.

Let's create AWS ElastiCache Clusters

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Updates
How does apply_immediately work and when should I use it?
By default, changes like num_cache_nodes apply during the next maintenance window, which may cause Pulumi to show a difference until then. Set apply_immediately to true to apply changes immediately, but this can cause brief downtime due to server reboots.
Why do some changes apply immediately even when apply_immediately is false?
Changes that force resource re-creation (like modifying cluster_id, engine, or port) apply immediately regardless of the apply_immediately setting.
Why does updating preferred_availability_zones show a perpetual difference?
Detecting drift of existing node availability zones is not currently supported. Updating preferred_availability_zones to migrate existing nodes will show a perpetual difference and is not recommended after initial creation.
Engine & Versioning
What cache engines are supported?
ElastiCache Cluster supports three engines: memcached, redis, and valkey. For Redis Cluster Mode Enabled replication groups, use the aws.elasticache.ReplicationGroup resource instead.
How do I specify the engine version for Redis?
For Redis 7+, use major.minor format (e.g., 7.2). For Redis 6, use major.minor (e.g., 6.2) or 6.x for the latest minor version. For Redis versions below 6, specify the full version (e.g., 5.0.6). The actual version used is returned in engine_version_actual.
What is auto_minor_version_upgrade and when can I use it?
auto_minor_version_upgrade automatically applies minor version upgrades during the maintenance window. It’s only supported for Redis engine with version 6 or higher and defaults to true.
Scaling & Availability
How many cache nodes can I have?
For Redis, num_cache_nodes must be 1. For Memcached, num_cache_nodes must be between 1 and 40. When reducing this number, the highest numbered nodes are removed.
How do I enable multi-AZ for my cluster?
Set az_mode to cross-az (default is single-az). For cross-AZ mode, num_cache_nodes must be greater than 1. Use preferred_availability_zones to specify which AZs to use.
Networking & Security
What are the requirements for enabling transit encryption?
Transit encryption requires Memcached 1.6.12+, Valkey 7.2+, or Redis 3.2.6/4.0.10+, and the cluster must be in a VPC. This property is immutable and forces resource re-creation if changed.
What are the requirements for using IPv6?
Set network_type to ipv6 or dual_stack. IPv6 requires Redis engine 6.2+ or Memcached 1.6.6, and all instances must be Nitro-based. This property is immutable.
Replication & Snapshots
How do I create a read replica and what are the limitations?
Set replication_group_id to the ID of the replication group. The replica inherits settings from the replication group and cannot specify engine_version, port, security_group_ids, or subnet_group_name. This property is immutable.
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.
Immutability & Resource Recreation
What properties force resource re-creation if changed?
The following properties are immutable and force re-creation: cluster_id, engine, availability_zone, network_type, port, preferred_outpost_arn, replication_group_id, subnet_group_name, transit_encryption_enabled, outpost_mode, snapshot_arns, and snapshot_name. For Memcached, changing node_type also forces re-creation.

Using a different cloud?

Explore database guides for other cloud providers: