Skip to content

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.

Provisioning an instance and wiring it into your deployment touches four files across two repositories:

  1. Define the instance in config/runtimes/eks/elasticache.yml (provisioner): engine version, region, a short identifier, per-environment sizing, and labels.
  2. Link it to your workload in config/runtimes/eks/workloads.yml (provisioner): references the instance by its name via a redis_instances list. Steps 1 and 2 go in one MR.
  3. Declare the dependency in your Fairway manifest (fairway.yaml): spec.infrastructure.redis.
  4. Reference the instance from your Runway deployment manifest (deployment.yaml): spec.infrastructure.redis, by its identifier.

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.

File an MR to add your instance to config/runtimes/eks/elasticache.yml in the Runway provisioner:

config/runtimes/eks/elasticache.yml
- 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-category
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.

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 runway with the access string on ~* +@all (full access, all keys). It is the only user in the group; there is no default user.
  • 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.
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):

config/runtimes/eks/workloads.yml
- runway_service_id: example-service-eks
regions:
- us-east-1
redis_instances:
- runway-redis-example

This 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:

.runway/fairway.yaml
spec:
infrastructure:
redis:
presence: REQUIRED

Do 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:

.runway/deployment.yaml
apiVersion: runway/v2
kind: RunwayManifest
metadata:
name: example-service-eks
spec:
infrastructure:
redis:
- identifier: MAIN

runwayctl 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.

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.

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.

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.

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.

  • The deploy fails with “did not find a Redis instance despite the application declaring Redis”: fairway.yaml has presence: REQUIRED but deployment.yaml has no spec.infrastructure.redis. Add it, or change the presence to OPTIONAL.
  • The deploy fails with “did not expect a Redis instance”: deployment.yaml references an instance but fairway.yaml does not declare redis.presence. Declare it as REQUIRED or OPTIONAL.
  • 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.