ElastiCache for Valkey
Runway can provision a managed Amazon ElastiCache for Valkey
instance for your EKS workload and wire the connection details into your service automatically.
Valkey is the only engine today; Redis OSS may follow.
Runway owns the lifecycle of the instance and its credentials: it creates the replication group in
the Runway AWS accounts, creates a dedicated user with a generated password, publishes the connection
details to Vault and AWS Secrets Manager, and injects them into your Helm chart through the Fairway
infrastructure.redis contract. You do not create or manage the secret yourself.
This is the EKS counterpart of Memorystore for Redis on GKE. Your Fairway and deployment manifests are the same on both runtimes; only the provisioner inventory differs.
This guide assumes you already have a working Runway for Kubernetes v2 deployment on EKS. If you do not, start with Getting Started first.
Overview
Section titled “Overview”Provisioning an instance and wiring it into your deployment touches four files across two repositories:
- Define the instance in
config/runtimes/eks/elasticache.yml(provisioner): engine version, region, a shortidentifier, per-environment sizing, and labels. - Link it to your workload in
config/runtimes/eks/workloads.yml(provisioner): references the instance by itsnamevia aredis_instanceslist. Steps 1 and 2 go in one MR. - Declare the dependency in your Fairway manifest (
fairway.yaml):spec.infrastructure.redis. - Reference the instance from your Runway deployment manifest (
deployment.yaml):spec.infrastructure.redis, by itsidentifier.
The name and the identifier are two different handles for the same instance. workloads.yml
uses the name; the deployment manifest and the secret paths use the identifier.
An instance belongs to a single service: each instance must be referenced by exactly one workload, and a workload can reference at most one instance. One replication group is created per environment you configure (staging, production, or both), each in its own AWS account, inside the Runway VPC and reachable only from the EKS cluster.
Step 1: Define the instance
Section titled “Step 1: Define the instance”File an MR to add your instance to
config/runtimes/eks/elasticache.yml
in the Runway provisioner:
- name: runway-redis-example identifier: MAIN engine: valkey engine_version: "8.1" region: us-east-1 env_configuration: staging: instance_type: 2xsmall production: instance_type: small labels: gl_service: example-service-eks # required owner_email_handle: my-team department: eng-infra department_group: eng-infra-my-group product_category: my-categoryField reference
Section titled “Field reference”| Field | Required | Notes |
|---|---|---|
name |
yes | Must start with runway-redis-, lowercase letters, digits and hyphens only, max 40 characters (an ElastiCache limit). Used as the replication group identifier and referenced from workloads.yml. |
identifier |
yes | ^[A-Z0-9_]{1,20}$. Short handle referenced from the deployment manifest and used in the secret paths. Conventional values are MAIN and CACHE. |
engine |
yes | Only valkey today. The field exists so that Redis OSS can be added later without changing the inventory layout. |
engine_version |
yes | Valkey version as major.minor, for example "8.1". Patch versions are applied automatically in the maintenance window. |
region |
yes | Must be an EKS region from config/runtimes/eks/regions.yml and one your workload is deployed to. |
labels |
yes | Infrastructure labels for cost attribution. gl_service is required; owner_email_handle, department, department_group, and product_category are optional. See the Infrastructure Standards. |
Per environment under env_configuration:
| Field | Required | Default | Notes |
|---|---|---|---|
instance_type |
yes | Load category, one of 2xsmall, xsmall, small, medium, large, xlarge, 2xlarge. See Sizing tiers. |
|
multi_az |
no | true in production, false in staging |
Spread the nodes across availability zones and enable automatic failover. Needs at least 2 nodes. |
num_cache_clusters |
no | 2 with multi_az, 1 otherwise |
Number of nodes: one primary plus replicas. 1 to 6. |
password_version |
no | 1 |
Increase to rotate the user password. See Rotating the password. |
Sizing tiers
Section titled “Sizing tiers”Each instance_type maps to a Graviton node type.
Every node in the group has this size, so a production instance with the defaults costs two nodes:
instance_type |
Node type | vCPU / memory |
|---|---|---|
2xsmall |
cache.t4g.small | 2 / 1.37 GiB |
xsmall |
cache.t4g.medium | 2 / 3.09 GiB |
small |
cache.m7g.large | 2 / 6.38 GiB |
medium |
cache.m7g.xlarge | 4 / 12.93 GiB |
large |
cache.m7g.2xlarge | 8 / 26.04 GiB |
xlarge |
cache.m7g.4xlarge | 16 / 52.26 GiB |
2xlarge |
cache.m7g.8xlarge | 32 / 103.68 GiB |
The following are fixed and not configurable:
- TLS required. Encryption in transit is enforced; plaintext connections are rejected. Data is encrypted at rest with a dedicated KMS key per instance.
- Cluster mode disabled. One primary and up to five replicas; the keyspace is not sharded.
- Authentication. A single Valkey RBAC user
runwaywith the access stringon ~* +@all(full access, all keys). It is the only user in the group; there is nodefaultuser. - Maintenance window Sunday 03:00 to 04:00 UTC, with automatic minor version upgrades. Sizing and topology changes are applied immediately after the provisioner MR merges, not in the window.
Step 2: Link the instance to your workload
Section titled “Step 2: Link the instance to your workload”In config/runtimes/eks/workloads.yml,
add a redis_instances key to your workload entry, referencing the instance by its name (not
its identifier):
- runway_service_id: example-service-eks regions: - us-east-1 redis_instances: - runway-redis-exampleThis is what tells the provisioner where to publish the connection details, and it grants your
workload’s External Secrets role access to them. Inventory validation fails the MR if an instance is
not linked to any workload, is linked to more than one, is in a region the workload is not deployed
to, or has multi_az enabled with fewer than two nodes.
Create the MR and assign it to a Runway team member (see
CODEOWNERS),
and ensure the pipeline passes before requesting review.
Step 3: Declare the dependency in your Fairway manifest
Section titled “Step 3: Declare the dependency in your Fairway manifest”In your service’s fairway.yaml, declare that the service needs Redis:
spec: infrastructure: redis: presence: REQUIREDDo not set spec.values.infrastructure.redis.secretRef or tlsSecretName yourself for a
Runway-managed instance. runwayctl sets both from the deployment manifest in Step 4 and overwrites
anything you put there with a warning. Other redis settings such as clientName, timeouts, or pool
sizing are yours to set and are preserved; see the
Fairway Redis schema.
Step 4: Reference the instance in your deployment manifest
Section titled “Step 4: Reference the instance in your deployment manifest”In your service’s deployment.yaml, add the instance under spec.infrastructure.redis, using the
identifier from elasticache.yml:
apiVersion: runway/v2kind: RunwayManifestmetadata: name: example-service-eks
spec: infrastructure: redis: - identifier: MAINrunwayctl checks the two manifests against each other at deploy time:
Fairway presence |
redis instance set |
Result |
|---|---|---|
REQUIRED |
yes | wired |
REQUIRED |
no | deploy fails |
OPTIONAL |
yes or no | wired when set |
UNUSED or unset |
yes | deploy fails |
The field is a list, but only one instance per service is supported today, by Fairway and LabKit
alike. If you list more than one, runwayctl logs a warning and wires the first.
Consuming the instance
Section titled “Consuming the instance”Runway wires the connection details into your deployment automatically; nothing to configure.
Your service reads them through LabKit’s v2/redis
package, the same way it would for Memorystore on GKE. redis.New picks up the address, username,
password, and CA from the mounted contract and connects over TLS, so no environment variables or TLS
configuration are needed in your code:
import ( "context" "log" "time"
"gitlab.com/gitlab-org/labkit/v2/redis")
func main() { ctx := context.Background()
client, err := redis.New(ctx) if err != nil { log.Fatalf("redis.New: %v", err) }
// Opens the connection pool and pings the server. if err := client.Start(ctx); err != nil { log.Fatalf("client.Start: %v", err) } defer func() { sdctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _ = client.Shutdown(sdctx) }()
// client.Redis() is a go-redis UniversalClient. rdb := client.Redis() if err := rdb.Set(ctx, "greeting", "hello", 0).Err(); err != nil { log.Fatalf("set: %v", err) } val, err := rdb.Get(ctx, "greeting").Result() if err != nil { log.Fatalf("get: %v", err) } log.Printf("greeting = %s", val)}redis.New also accepts WithRegisterer,
WithTracer, and WithLogger for metrics, tracing, and per-command logs; see the
LabKit redis package.
Everything goes to the primary endpoint, reads included. The reader endpoint is not published today.
Rotating the password
Section titled “Rotating the password”Increase password_version for the environment in elasticache.yml and merge. In one apply, the
provisioner generates a new password, applies it to the runway user, and republishes it to Vault and
Secrets Manager under the same paths. External Secrets refreshes the Kubernetes Secret within ten
minutes. Only ever increase the version.
Resizing and topology changes
Section titled “Resizing and topology changes”Change instance_type, num_cache_clusters, or multi_az for the environment and merge. The
provisioner applies the change immediately. Changing the node type replaces the nodes one by one;
expect brief failovers and, on a single-node staging instance, a short outage and loss of the cached
data. Adding or removing replicas is online.
Removing an instance
Section titled “Removing an instance”Remove the dependency from your service first: drop spec.infrastructure.redis from
deployment.yaml, set presence to OPTIONAL or remove it in fairway.yaml, and deploy. Then, in
one provisioner MR, remove the entry from elasticache.yml together with the redis_instances
reference in workloads.yml.
Troubleshooting
Section titled “Troubleshooting”- The deploy fails with “did not find a Redis instance despite the application declaring Redis”:
fairway.yamlhaspresence: REQUIREDbutdeployment.yamlhas nospec.infrastructure.redis. Add it, or change the presence toOPTIONAL. - The deploy fails with “did not expect a Redis instance”:
deployment.yamlreferences an instance butfairway.yamldoes not declareredis.presence. Declare it asREQUIREDorOPTIONAL. - Looking up the connection details as a human: read
runway/platform/<runway_service_id>/redis/<identifier>/<environment>/<region>in Vault.
Ask the Runway team in #f_runway for help.