Create AWS ElastiCache Serverless Caches

The aws:elasticache/serverlessCache:ServerlessCache resource, part of the Pulumi AWS provider, provisions ElastiCache Serverless caches that automatically scale capacity based on workload without managing nodes. This guide focuses on three capabilities: engine selection (Memcached, Redis, Valkey), usage limits for storage and compute, and snapshot configuration for Redis and Valkey.

Serverless caches run in VPC subnets with security groups and optionally use KMS keys for encryption. The examples are intentionally small. Combine them with your own VPC infrastructure and access controls.

Deploy a Memcached serverless cache with usage limits

Teams building session stores or simple caching layers often start with Memcached for its straightforward key-value semantics and low operational overhead.

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

const example = new aws.elasticache.ServerlessCache("example", {
    engine: "memcached",
    name: "example",
    cacheUsageLimits: {
        dataStorage: {
            maximum: 10,
            unit: "GB",
        },
        ecpuPerSeconds: [{
            maximum: 5000,
        }],
    },
    description: "Test Server",
    kmsKeyId: test.arn,
    majorEngineVersion: "1.6",
    securityGroupIds: [testAwsSecurityGroup.id],
    subnetIds: testAwsSubnet.map(__item => __item.id),
});
import pulumi
import pulumi_aws as aws

example = aws.elasticache.ServerlessCache("example",
    engine="memcached",
    name="example",
    cache_usage_limits={
        "data_storage": {
            "maximum": 10,
            "unit": "GB",
        },
        "ecpu_per_seconds": [{
            "maximum": 5000,
        }],
    },
    description="Test Server",
    kms_key_id=test["arn"],
    major_engine_version="1.6",
    security_group_ids=[test_aws_security_group["id"]],
    subnet_ids=[__item["id"] for __item in test_aws_subnet])
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 {
var splat0 []interface{}
for _, val0 := range testAwsSubnet {
splat0 = append(splat0, val0.Id)
}
_, err := elasticache.NewServerlessCache(ctx, "example", &elasticache.ServerlessCacheArgs{
Engine: pulumi.String("memcached"),
Name: pulumi.String("example"),
CacheUsageLimits: &elasticache.ServerlessCacheCacheUsageLimitsArgs{
DataStorage: &elasticache.ServerlessCacheCacheUsageLimitsDataStorageArgs{
Maximum: pulumi.Int(10),
Unit: pulumi.String("GB"),
},
EcpuPerSeconds: elasticache.ServerlessCacheCacheUsageLimitsEcpuPerSecondArray{
&elasticache.ServerlessCacheCacheUsageLimitsEcpuPerSecondArgs{
Maximum: pulumi.Int(5000),
},
},
},
Description: pulumi.String("Test Server"),
KmsKeyId: pulumi.Any(test.Arn),
MajorEngineVersion: pulumi.String("1.6"),
SecurityGroupIds: pulumi.StringArray{
testAwsSecurityGroup.Id,
},
SubnetIds: toPulumiArray(splat0),
})
if err != nil {
return err
}
return nil
})
}
func toPulumiArray(arr []) pulumi.Array {
var pulumiArr pulumi.Array
for _, v := range arr {
pulumiArr = append(pulumiArr, pulumi.(v))
}
return pulumiArr
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.ElastiCache.ServerlessCache("example", new()
    {
        Engine = "memcached",
        Name = "example",
        CacheUsageLimits = new Aws.ElastiCache.Inputs.ServerlessCacheCacheUsageLimitsArgs
        {
            DataStorage = new Aws.ElastiCache.Inputs.ServerlessCacheCacheUsageLimitsDataStorageArgs
            {
                Maximum = 10,
                Unit = "GB",
            },
            EcpuPerSeconds = new[]
            {
                new Aws.ElastiCache.Inputs.ServerlessCacheCacheUsageLimitsEcpuPerSecondArgs
                {
                    Maximum = 5000,
                },
            },
        },
        Description = "Test Server",
        KmsKeyId = test.Arn,
        MajorEngineVersion = "1.6",
        SecurityGroupIds = new[]
        {
            testAwsSecurityGroup.Id,
        },
        SubnetIds = testAwsSubnet.Select(__item => __item.Id).ToList(),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elasticache.ServerlessCache;
import com.pulumi.aws.elasticache.ServerlessCacheArgs;
import com.pulumi.aws.elasticache.inputs.ServerlessCacheCacheUsageLimitsArgs;
import com.pulumi.aws.elasticache.inputs.ServerlessCacheCacheUsageLimitsDataStorageArgs;
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 ServerlessCache("example", ServerlessCacheArgs.builder()
            .engine("memcached")
            .name("example")
            .cacheUsageLimits(ServerlessCacheCacheUsageLimitsArgs.builder()
                .dataStorage(ServerlessCacheCacheUsageLimitsDataStorageArgs.builder()
                    .maximum(10)
                    .unit("GB")
                    .build())
                .ecpuPerSeconds(ServerlessCacheCacheUsageLimitsEcpuPerSecondArgs.builder()
                    .maximum(5000)
                    .build())
                .build())
            .description("Test Server")
            .kmsKeyId(test.arn())
            .majorEngineVersion("1.6")
            .securityGroupIds(testAwsSecurityGroup.id())
            .subnetIds(testAwsSubnet.stream().map(element -> element.id()).collect(toList()))
            .build());

    }
}

The engine property selects the cache type; Memcached provides simple key-value storage without persistence. The cacheUsageLimits block sets maximum storage (dataStorage) and compute capacity (ecpuPerSeconds), which ElastiCache uses to scale automatically. The majorEngineVersion specifies the Memcached version to run.

Deploy Redis with automated snapshots

Applications requiring persistence and point-in-time recovery use Redis with daily snapshots to protect against data loss.

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

const example = new aws.elasticache.ServerlessCache("example", {
    engine: "redis",
    name: "example",
    cacheUsageLimits: {
        dataStorage: {
            maximum: 10,
            unit: "GB",
        },
        ecpuPerSeconds: [{
            maximum: 5000,
        }],
    },
    dailySnapshotTime: "09:00",
    description: "Test Server",
    kmsKeyId: test.arn,
    majorEngineVersion: "7",
    snapshotRetentionLimit: 1,
    securityGroupIds: [testAwsSecurityGroup.id],
    subnetIds: testAwsSubnet.map(__item => __item.id),
});
import pulumi
import pulumi_aws as aws

example = aws.elasticache.ServerlessCache("example",
    engine="redis",
    name="example",
    cache_usage_limits={
        "data_storage": {
            "maximum": 10,
            "unit": "GB",
        },
        "ecpu_per_seconds": [{
            "maximum": 5000,
        }],
    },
    daily_snapshot_time="09:00",
    description="Test Server",
    kms_key_id=test["arn"],
    major_engine_version="7",
    snapshot_retention_limit=1,
    security_group_ids=[test_aws_security_group["id"]],
    subnet_ids=[__item["id"] for __item in test_aws_subnet])
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 {
var splat0 []interface{}
for _, val0 := range testAwsSubnet {
splat0 = append(splat0, val0.Id)
}
_, err := elasticache.NewServerlessCache(ctx, "example", &elasticache.ServerlessCacheArgs{
Engine: pulumi.String("redis"),
Name: pulumi.String("example"),
CacheUsageLimits: &elasticache.ServerlessCacheCacheUsageLimitsArgs{
DataStorage: &elasticache.ServerlessCacheCacheUsageLimitsDataStorageArgs{
Maximum: pulumi.Int(10),
Unit: pulumi.String("GB"),
},
EcpuPerSeconds: elasticache.ServerlessCacheCacheUsageLimitsEcpuPerSecondArray{
&elasticache.ServerlessCacheCacheUsageLimitsEcpuPerSecondArgs{
Maximum: pulumi.Int(5000),
},
},
},
DailySnapshotTime: pulumi.String("09:00"),
Description: pulumi.String("Test Server"),
KmsKeyId: pulumi.Any(test.Arn),
MajorEngineVersion: pulumi.String("7"),
SnapshotRetentionLimit: pulumi.Int(1),
SecurityGroupIds: pulumi.StringArray{
testAwsSecurityGroup.Id,
},
SubnetIds: toPulumiArray(splat0),
})
if err != nil {
return err
}
return nil
})
}
func toPulumiArray(arr []) pulumi.Array {
var pulumiArr pulumi.Array
for _, v := range arr {
pulumiArr = append(pulumiArr, pulumi.(v))
}
return pulumiArr
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.ElastiCache.ServerlessCache("example", new()
    {
        Engine = "redis",
        Name = "example",
        CacheUsageLimits = new Aws.ElastiCache.Inputs.ServerlessCacheCacheUsageLimitsArgs
        {
            DataStorage = new Aws.ElastiCache.Inputs.ServerlessCacheCacheUsageLimitsDataStorageArgs
            {
                Maximum = 10,
                Unit = "GB",
            },
            EcpuPerSeconds = new[]
            {
                new Aws.ElastiCache.Inputs.ServerlessCacheCacheUsageLimitsEcpuPerSecondArgs
                {
                    Maximum = 5000,
                },
            },
        },
        DailySnapshotTime = "09:00",
        Description = "Test Server",
        KmsKeyId = test.Arn,
        MajorEngineVersion = "7",
        SnapshotRetentionLimit = 1,
        SecurityGroupIds = new[]
        {
            testAwsSecurityGroup.Id,
        },
        SubnetIds = testAwsSubnet.Select(__item => __item.Id).ToList(),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elasticache.ServerlessCache;
import com.pulumi.aws.elasticache.ServerlessCacheArgs;
import com.pulumi.aws.elasticache.inputs.ServerlessCacheCacheUsageLimitsArgs;
import com.pulumi.aws.elasticache.inputs.ServerlessCacheCacheUsageLimitsDataStorageArgs;
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 ServerlessCache("example", ServerlessCacheArgs.builder()
            .engine("redis")
            .name("example")
            .cacheUsageLimits(ServerlessCacheCacheUsageLimitsArgs.builder()
                .dataStorage(ServerlessCacheCacheUsageLimitsDataStorageArgs.builder()
                    .maximum(10)
                    .unit("GB")
                    .build())
                .ecpuPerSeconds(ServerlessCacheCacheUsageLimitsEcpuPerSecondArgs.builder()
                    .maximum(5000)
                    .build())
                .build())
            .dailySnapshotTime("09:00")
            .description("Test Server")
            .kmsKeyId(test.arn())
            .majorEngineVersion("7")
            .snapshotRetentionLimit(1)
            .securityGroupIds(testAwsSecurityGroup.id())
            .subnetIds(testAwsSubnet.stream().map(element -> element.id()).collect(toList()))
            .build());

    }
}

Redis adds persistence features not available in Memcached. The dailySnapshotTime property schedules when ElastiCache creates backups, and snapshotRetentionLimit controls how many snapshots to keep before deleting the oldest. These properties are only valid for Redis and Valkey engines.

Deploy Valkey with snapshot retention

Valkey provides Redis-compatible caching with the same persistence and snapshot capabilities for teams seeking an open-source alternative.

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

const example = new aws.elasticache.ServerlessCache("example", {
    engine: "valkey",
    name: "example",
    cacheUsageLimits: {
        dataStorage: {
            maximum: 10,
            unit: "GB",
        },
        ecpuPerSeconds: [{
            maximum: 5000,
        }],
    },
    dailySnapshotTime: "09:00",
    description: "Test Server",
    kmsKeyId: test.arn,
    majorEngineVersion: "7",
    snapshotRetentionLimit: 1,
    securityGroupIds: [testAwsSecurityGroup.id],
    subnetIds: testAwsSubnet.map(__item => __item.id),
});
import pulumi
import pulumi_aws as aws

example = aws.elasticache.ServerlessCache("example",
    engine="valkey",
    name="example",
    cache_usage_limits={
        "data_storage": {
            "maximum": 10,
            "unit": "GB",
        },
        "ecpu_per_seconds": [{
            "maximum": 5000,
        }],
    },
    daily_snapshot_time="09:00",
    description="Test Server",
    kms_key_id=test["arn"],
    major_engine_version="7",
    snapshot_retention_limit=1,
    security_group_ids=[test_aws_security_group["id"]],
    subnet_ids=[__item["id"] for __item in test_aws_subnet])
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 {
var splat0 []interface{}
for _, val0 := range testAwsSubnet {
splat0 = append(splat0, val0.Id)
}
_, err := elasticache.NewServerlessCache(ctx, "example", &elasticache.ServerlessCacheArgs{
Engine: pulumi.String("valkey"),
Name: pulumi.String("example"),
CacheUsageLimits: &elasticache.ServerlessCacheCacheUsageLimitsArgs{
DataStorage: &elasticache.ServerlessCacheCacheUsageLimitsDataStorageArgs{
Maximum: pulumi.Int(10),
Unit: pulumi.String("GB"),
},
EcpuPerSeconds: elasticache.ServerlessCacheCacheUsageLimitsEcpuPerSecondArray{
&elasticache.ServerlessCacheCacheUsageLimitsEcpuPerSecondArgs{
Maximum: pulumi.Int(5000),
},
},
},
DailySnapshotTime: pulumi.String("09:00"),
Description: pulumi.String("Test Server"),
KmsKeyId: pulumi.Any(test.Arn),
MajorEngineVersion: pulumi.String("7"),
SnapshotRetentionLimit: pulumi.Int(1),
SecurityGroupIds: pulumi.StringArray{
testAwsSecurityGroup.Id,
},
SubnetIds: toPulumiArray(splat0),
})
if err != nil {
return err
}
return nil
})
}
func toPulumiArray(arr []) pulumi.Array {
var pulumiArr pulumi.Array
for _, v := range arr {
pulumiArr = append(pulumiArr, pulumi.(v))
}
return pulumiArr
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.ElastiCache.ServerlessCache("example", new()
    {
        Engine = "valkey",
        Name = "example",
        CacheUsageLimits = new Aws.ElastiCache.Inputs.ServerlessCacheCacheUsageLimitsArgs
        {
            DataStorage = new Aws.ElastiCache.Inputs.ServerlessCacheCacheUsageLimitsDataStorageArgs
            {
                Maximum = 10,
                Unit = "GB",
            },
            EcpuPerSeconds = new[]
            {
                new Aws.ElastiCache.Inputs.ServerlessCacheCacheUsageLimitsEcpuPerSecondArgs
                {
                    Maximum = 5000,
                },
            },
        },
        DailySnapshotTime = "09:00",
        Description = "Test Server",
        KmsKeyId = test.Arn,
        MajorEngineVersion = "7",
        SnapshotRetentionLimit = 1,
        SecurityGroupIds = new[]
        {
            testAwsSecurityGroup.Id,
        },
        SubnetIds = testAwsSubnet.Select(__item => __item.Id).ToList(),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.elasticache.ServerlessCache;
import com.pulumi.aws.elasticache.ServerlessCacheArgs;
import com.pulumi.aws.elasticache.inputs.ServerlessCacheCacheUsageLimitsArgs;
import com.pulumi.aws.elasticache.inputs.ServerlessCacheCacheUsageLimitsDataStorageArgs;
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 ServerlessCache("example", ServerlessCacheArgs.builder()
            .engine("valkey")
            .name("example")
            .cacheUsageLimits(ServerlessCacheCacheUsageLimitsArgs.builder()
                .dataStorage(ServerlessCacheCacheUsageLimitsDataStorageArgs.builder()
                    .maximum(10)
                    .unit("GB")
                    .build())
                .ecpuPerSeconds(ServerlessCacheCacheUsageLimitsEcpuPerSecondArgs.builder()
                    .maximum(5000)
                    .build())
                .build())
            .dailySnapshotTime("09:00")
            .description("Test Server")
            .kmsKeyId(test.arn())
            .majorEngineVersion("7")
            .snapshotRetentionLimit(1)
            .securityGroupIds(testAwsSecurityGroup.id())
            .subnetIds(testAwsSubnet.stream().map(element -> element.id()).collect(toList()))
            .build());

    }
}

Valkey uses identical snapshot configuration to Redis. The engine property switches to “valkey” while dailySnapshotTime and snapshotRetentionLimit work the same way. Valkey offers Redis compatibility with an open-source license.

Beyond these examples

These snippets focus on specific serverless cache features: engine selection (Memcached, Redis, Valkey), usage limits and capacity planning, and snapshot configuration and retention. They’re intentionally minimal rather than full caching solutions.

The examples reference pre-existing infrastructure such as VPC subnets and security groups, and KMS keys for encryption. They focus on configuring the cache rather than provisioning the surrounding network.

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

  • Snapshot restoration (snapshotArnsToRestores)
  • User group authentication (userGroupId)
  • Custom endpoint configuration
  • Tag-based organization and cost tracking

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

Let's create AWS ElastiCache Serverless Caches

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Engine Selection & Features
What cache engines are supported?
ElastiCache Serverless supports three engines: memcached, redis, and valkey.
Which snapshot features are engine-specific?

Snapshot features vary by engine:

  • dailySnapshotTime is supported for redis and valkey only
  • snapshotRetentionLimit is available for Redis only
  • snapshotArnsToRestores is available for Redis only
Which engines support user groups?
User groups (via userGroupId) are available for Redis and Valkey only. The default is NULL.
Cache Configuration
How do I configure cache usage limits?

Set cacheUsageLimits with two components:

  1. dataStorage with maximum and unit (e.g., 10 GB)
  2. ecpuPerSeconds with maximum (e.g., 5000)
What's the default for daily snapshot time?
The dailySnapshotTime defaults to 0 for Redis and Valkey engines.
Networking & Security
What are the VPC subnet requirements?
All subnet IDs in subnetIds must belong to the same VPC.
What security groups are required?
You must provide securityGroupIds for VPC endpoint access. If not specified, the VPC’s default security group will be used.
What encryption options are available?
You can provide a kmsKeyId (ARN of a customer managed key) for encrypting data at rest. If not provided, a default service key is used.
Can I deploy the cache across multiple VPCs?
No, all subnets specified in subnetIds must belong to the same VPC.

Using a different cloud?

Explore database guides for other cloud providers: