Chapter 5. Migrating Redis to Valkey


5.1. Migration compatibility matrix

Valkey is compatible with Redis OSS v7.2 and all earlier open-source Redis versions because Valkey v7.2.4 is a fork of Redis v7.2.4. Migrating from any open-source Redis version to Valkey is an effective upgrade.
Redis (CE) v7.4 and later versions are not open-source and the data files are not compatible with Valkey.
The following table provides migration options depending on the Redis version you run.

Expand
RedisValkey

6.x

7.2.x

7.2.x

7.2.x

7.2.x

8.0.x

7.4

NA

Important

Upgrading an instance is irreversible. You cannot downgrade the version of a Valkey instance.

5.2. Prerequisites

Before beginning the migration, ensure the following prerequisites are met.

  • 3scale 2.15 or higher
  • Complete the Redis migration by referring Externalizing databases for 2.16
  • A working installation of Redis with access to the configuration and data files.
  • A fresh installation of Valkey on your target server.
  • Backup of your Redis data to prevent data loss during migration.

5.3. Best Practices

  • Consider your post-migration verification steps before proceeding. This requireS you to take a snapshot of data using the api or portal and define a test plan to verify a successful migration.
  • Consider letting 3scale components process all the jobs in the background before fully scaling the instance.
  • Consider performing the migration at the time of lowest traffic as the migration affects the service.
  • Before you upgrade the version of your instance, ensure that you become familiar with the newer version to see how this version impacts your application.
  • Test Valkey configurations and migration scripts in a non-production environment to identify potential issues.

5.4. Redis to Valkey: Migration Steps

Export the following environments.

export REDIS_ON_CLUSTER_NAMESPACE=<namespace where the Redis pod is running>
export OPERATOR_NAMESPACE=<namespace where the 3scale operator is running>
export THREESCALE_NAMESPACE=<namespace where the 3scale instance is running>

Additionally, export the following replica counts. This is done to ensure that we are scaling the 3scale instance back to the replica values that we scaled it down from.

SYSTEM_MEMCACHE_REPLICA_COUNT=$(oc get deployment system-memcache -n $THREESCALE_NAMESPACE -o=jsonpath='{.spec.replicas}')
ZYNC_DATABASE_REPLICA_COUNT=$(oc get deployment zync-database -n $THREESCALE_NAMESPACE -o=jsonpath='{.spec.replicas}')
APICAST_PRODUCTION_REPLICA_COUNT=$(oc get deployment -n $THREESCALE_NAMESPACE apicast-production -o=jsonpath='{.spec.replicas}')
APICAST_STAGING_REPLICA_COUNT=$(oc get deployment apicast-staging -n $THREESCALE_NAMESPACE -o=jsonpath='{.spec.replicas}')
BACKEND_CRON_REPLICA_COUNT=$(oc get deployment backend-cron -n $THREESCALE_NAMESPACE -o=jsonpath='{.spec.replicas}')
BACKEND_LISTENER_REPLICA_COUNT=$(oc get deployment backend-listener -n $THREESCALE_NAMESPACE -o=jsonpath='{.spec.replicas}')
BACKEND_WORKER_REPLICA_COUNT=$(oc get deployment backend-worker -o=jsonpath='{.spec.replicas}')
SYSTEM_APP_REPLICA_COUNT=$(oc get deployment system-app -n $THREESCALE_NAMESPACE -o=jsonpath='{.spec.replicas}')
SYSTEM_SIDEKIQ_REPLICA_COUNT=$(oc get deployment system-sidekiq -n $THREESCALE_NAMESPACE -o=jsonpath='{.spec.replicas}')
SYSTEM_SEARCHD_REPLICA_COUNT=$(oc get deployment system-searchd -n $THREESCALE_NAMESPACE -o=jsonpath='{.spec.replicas}')
ZYNC_REPLICA_COUNT=$(oc get deployment zync -n $THREESCALE_NAMESPACE -o=jsonpath='{.spec.replicas}')
ZYNC_QUE_REPLICA_COUNT=$(oc get deployment zync-que -n $THREESCALE_NAMESPACE -o=jsonpath='{.spec.replicas}')
  • Scale down the deployment of the 3scale operator controller to prevent it from interfering with the scaling down of other pods.
oc scale deployment threescale-operator-controller-manager-v2 -n $OPERATOR_NAMESPACE --replicas=0
  • Scale down all the pods of the 3scale deployment, except the Redis instances:
oc scale deployment/{apicast-production,apicast-staging,backend-cron,backend-listener,backend-worker,system-app,system-memcache,system-sidekiq,system-searchd,zync,zync-database,zync-que} -n $THREESCALE_NAMESPACE --replicas=0

5.4.2. Export Redis Data

  • Back up data of the backend-redis deployment:
oc cp $(oc get pods -l 'deployment=backend-redis' -n $REDIS_ON_CLUSTER_NAMESPACE -o json | jq '.items[0].metadata.name' -r):/var/lib/redis/data/dump.rdb ./backend-redis-dump.rdb
  • Back up data of the system-redis deployment:
oc cp $(oc get pods -l 'deployment=system-redis' -n $REDIS_ON_CLUSTER_NAMESPACE -o json | jq '.items[0].metadata.name' -r):/var/lib/redis/data/dump.rdb ./system-redis-dump.rdb
Note

Ensure to tweak the values accordingly to where and what your backend Redis instance is.

Connect to your Redis container(s) using redis-cli, and check the number of keys, using the INFO KEYSPACE command. This will be used later to verify that the entire database has been successfully migrated. In this example, keys=6286 indicates that there are 6,286 keys in the database.

sh-5.2$ redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> INFO KEYSPACE
# Keyspace
db1:keys=6286,expires=1,avg_ttl=53682
oc scale deployment/system-redis --replicas=0
oc scale deployment/backend-redis --replicas=0

Create or switch to the namespace where the Valkey databases are deployed.

Export the name of the namespace where the new Valkey databases will be installed into a variable. You can use an existing namespace, or create a new one. It is also possible to deploy the new database in the same namespace where 3scale is installed, but it is not recommended.

VALKEY_NAMESPACE=<database-target-namespace>
oc project $VALKEY_NAMESPACE

or

oc new-project $VALKEY_NAMESPACE

5.4.4.1. Backend Valkey resources

Create a YAML file backend-valkey-external.yaml locally with the specification of the new deployment for Valkey instance for Backend component:

kind: Deployment
apiVersion: apps/v1
metadata:
  name: backend-valkey-external
  labels:
    app: backend-valkey-external
spec:
  replicas: 1
  selector:
    matchLabels:
      deployment: backend-valkey-external
  template:
    metadata:
      labels:
        deployment: backend-valkey-external
    spec:
      restartPolicy: Always
      serviceAccountName: backend-valkey-external
      schedulerName: default-scheduler
      terminationGracePeriodSeconds: 30
      securityContext: {}
      containers:
        - resources:
            limits:
              cpu: '2'
              memory: 32Gi
            requests:
              cpu: '1'
              memory: 1Gi
          readinessProbe:
            exec:
              command:
                - container-entrypoint
                - bash
                - '-c'
                - redis-cli set liveness-probe "`date`" | grep OK
            initialDelaySeconds: 10
            timeoutSeconds: 1
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 3
          terminationMessagePath: /dev/termination-log
          name: backend-valkey-external
          livenessProbe:
            tcpSocket:
              port: 6379
            initialDelaySeconds: 10
            timeoutSeconds: 1
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 3
          env:
            - name: VALKEY_CONF
              value: /etc/valkey/valkey.conf
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - name: backend-valkey-storage-external
              mountPath: /var/lib/valkey/data
            - name: valkey-config-external
              mountPath: /etc/valkey/
          terminationMessagePolicy: File
          image: 'registry.redhat.io/rhel9/valkey-8'
      serviceAccount: backend-valkey-external
      volumes:
        - name: backend-valkey-storage-external
          persistentVolumeClaim:
            claimName: backend-valkey-storage-external
        - name: valkey-config-external
          configMap:
            name: valkey-config-external
            items:
              - key: valkey.conf
                path: valkey.conf
            defaultMode: 420
      dnsPolicy: ClusterFirst
  strategy:
    type: Recreate
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600

Consider updating the following:

  • Labels, annotations and environment variables in case you had or require some custom values
  • Limits and Requests in case you had or require some custom values
  • Other values can be modified according to the requirements

Create a service account for the Backend Valkey instance and label it:

oc create serviceaccount backend-valkey-external

oc label serviceaccount backend-valkey-external app=backend-valkey-external

Create a YAML file backend-valkey-storage-external-pvc.yaml locally with the specification of the Persistent Volume Claim for the new Valkey instance:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: backend-valkey-storage-external
  labels:
    app: backend-valkey-external
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  volumeMode: Filesystem

Adjust the storage request as required.

Create a YAML file backend-valkey-service.yaml locally with the specification of the Service for the new Valkey instance:

apiVersion: v1
kind: Service
metadata:
  name: backend-valkey-service
  labels:
    app: backend-valkey-external
spec:
  type: ClusterIP
  ports:
  - port: 6379
    targetPort: 6379
    protocol: TCP
  selector:
    deployment: backend-valkey-external

5.4.4.2. System Valkey Resources

Create a YAML file system-valkey-external.yaml locally with the specification of the new deployment for Valkey instance for System component:

kind: Deployment
apiVersion: apps/v1
metadata:
  name: system-valkey-external
  labels:
    app: system-valkey-external
spec:
  replicas: 1
  selector:
    matchLabels:
      deployment: system-valkey-external
  template:
    metadata:
      labels:
        deployment: system-valkey-external
    spec:
      restartPolicy: Always
      serviceAccountName: system-valkey-external
      schedulerName: default-scheduler
      terminationGracePeriodSeconds: 30
      securityContext: {}
      containers:
        - resources:
            limits:
              cpu: '2'
              memory: 32Gi
            requests:
              cpu: '1'
              memory: 1Gi
          readinessProbe:
            exec:
              command:
                - container-entrypoint
                - bash
                - '-c'
                - redis-cli set liveness-probe "`date`" | grep OK
            initialDelaySeconds: 10
            timeoutSeconds: 1
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 3
          terminationMessagePath: /dev/termination-log
          name: system-valkey-external
          livenessProbe:
            tcpSocket:
              port: 6379
            initialDelaySeconds: 10
            timeoutSeconds: 1
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 3
          env:
            - name: VALKEY_CONF
              value: /etc/valkey/valkey.conf
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - name: system-valkey-storage-external
              mountPath: /var/lib/valkey/data
            - name: valkey-config-external
              mountPath: /etc/valkey/
          terminationMessagePolicy: File
          image: 'registry.redhat.io/rhel9/valkey-8'
      serviceAccount: system-valkey-external
      volumes:
        - name: system-valkey-storage-external
          persistentVolumeClaim:
            claimName: system-valkey-storage-external
        - name: valkey-config-external
          configMap:
            name: valkey-config-external
            items:
              - key: valkey.conf
                path: valkey.conf
            defaultMode: 420
      dnsPolicy: ClusterFirst
  strategy:
    type: Recreate
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600

Consider updating the following:

  • Labels, annotations and environment variables in case you had or require some custom values
  • Limits and Requests in case you had or require some custom values
  • Other values can be modified according to the requirements

Create a service account for the Backend Valkey instance and label it:

oc create serviceaccount system-valkey-external
oc label serviceaccount system-valkey-external app=system-valkey-external

Create a YAML file system-valkey-storage-external-pvc.yaml locally with the specification of the Persistent Volume Claim for the new Valkey instance:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: system-valkey-storage-external
  labels:
    app: system-valkey-external
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  volumeMode: Filesystem

Adjust the storage request as required.

Create a YAML file system-valkey-service.yaml locally with the specification of the Service for the new Valkey instance:

apiVersion: v1
kind: Service
metadata:
  name: system-valkey-service
  labels:
    app: system-valkey-external
spec:
  type: ClusterIP
  ports:
  - port: 6379
    targetPort: 6379
    protocol: TCP
  selector:
    deployment: system-valkey-external

Create a YAML file valkey-config-external.yaml locally with the specification of the ConfigMap for the new Valkey instances configuration.

Note

Valkey’s configuration is similar to Redis, but you must review the valkey.conf file for any changes specific to your environment.

kind: ConfigMap
apiVersion: v1
metadata:
  name: valkey-config-external
data:
  valkey.conf: |
    protected-mode no

    port 6379

    timeout 0
    tcp-keepalive 300

    daemonize no
    supervised no

    loglevel notice

    databases 16

    #save 900 1
    #save 300 10
    #save 60 10000
    save ""

    stop-writes-on-bgsave-error yes

    rdbcompression yes
    rdbchecksum yes

    dbfilename dump.rdb

    slave-serve-stale-data yes
    slave-read-only yes

    repl-diskless-sync no
    repl-disable-tcp-nodelay no

    appendonly no
    appendfilename "appendonly.aof"
    appendfsync everysec
    no-appendfsync-on-rewrite no
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    aof-load-truncated yes

    lua-time-limit 5000

    activerehashing no

    aof-rewrite-incremental-fsync yes
    dir /var/lib/valkey/data

    rename-command REPLICAOF ""
    rename-command SLAVEOF ""
Note
  • The above Redis configuration closely matches the configuration provided by the 3scale Operator, with a slight difference in being prepared to restore data from the dump file.
  • If you enabled AOF in your Valkey configuration, disable it on the first start. Otherwise, the copied RDB file will not be imported into Valkey.

At this point, you must have the following files in the local working directory.

  • backend-redis-dump.rdb
  • backend-valkey-external.yaml
  • backend-valkey-secret.yaml
  • backend-valkey-service.yaml
  • backend-valkey-storage-external-pvc.yaml
  • valkey-config-external.yaml
  • system-redis-dump.rdb
  • system-valkey-external.yaml
  • system-valkey-secret.yaml
  • system-valkey-service.yaml
  • system-valkey-storage-external-pvc.yaml

Ensure your current namespace is $VALKEY_NAMESPACE:

oc project $VALKEY_NAMESPACE

Create the resource from the YAML files created in previous steps using the oc apply:

Config map:

oc apply -f valkey-config-external.yaml

Backend:

oc apply -f backend-valkey-external.yaml
oc apply -f backend-valkey-storage-external-pvc.yaml
oc apply -f backend-valkey-service.yaml

System:

oc apply -f system-valkey-external.yaml
oc apply -f system-valkey-storage-external-pvc.yaml
oc apply -f system-valkey-service.yaml

Verify that all the resources have been created properly with the following command:

oc get deployment,pvc,svc,serviceaccount -l app=backend-valkey-external
oc get deployment,pvc,svc,serviceaccount -l app=system-valkey-external

Specifically, check that the column READY for the backend-valkey-external and system-valkey-external deployment shows 1/1.

5.4.6. Import Data into Valkey

Restore the data in the new Valkey instances using the previously created dump files:

oc cp ./backend-redis-dump.rdb $(oc get pods -l 'deployment=backend-valkey-external' -o json | jq '.items[0].metadata.name' -r):/var/lib/valkey/data/dump.rdb
oc cp ./system-redis-dump.rdb $(oc get pods -l 'deployment=system-valkey-external' -o json | jq '.items[0].metadata.name' -r):/var/lib/valkey/data/dump.rdb

Restart the Valkey deployments:

oc rollout restart deployment/backend-valkey-external
oc rollout restart deployment/system-valkey-external

Once Valkey instances are in a ready state, create an append-only file:

oc rsh $(oc get pods -l 'deployment=backend-valkey-external' -o json | jq '.items[0].metadata.name' -r) bash -c 'redis-cli BGREWRITEAOF'
oc rsh $(oc get pods -l 'deployment=system-valkey-external' -o json | jq '.items[0].metadata.name' -r) bash -c 'redis-cli BGREWRITEAOF'

After a few minutes, confirm that the AOF rewrite is complete:

oc rsh $(oc get pods -l 'deployment=backend-valkey-external' -o json | jq '.items[0].metadata.name' -r) bash -c 'redis-cli info' | grep aof_rewrite_in_progress
oc rsh $(oc get pods -l 'deployment=system-valkey-external' -o json | jq '.items[0].metadata.name' -r) bash -c 'redis-cli info' | grep aof_rewrite_in_progress

While aof_rewrite_in_progress = 1, the execution is in progress. Check periodically until aof_rewrite_in_progress = 0. Zero indicates that the execution is complete.

Edit valkey-config-external ConfigMap resource using oc edit configmap/valkey-config-external and apply the following changes:

  • uncomment the lines:
save 900 1
save 300 10
save 60 10000
  • remove the line save "" and set appendonly value to yes
appendonly yes
  • Restart the Valkey deployments:
oc rollout restart deployment/backend-valkey-external
oc rollout restart deployment/system-valkey-external

5.4.7. Update Redis secret

Before updating the 3scale secrets so that the 3scale instance starts using the new external databases, back them up:

oc get secret backend-redis -n $THREESCALE_NAMESPACE -o yaml > backend-redis-secret.yaml
oc get secret system-redis -n $THREESCALE_NAMESPACE -o yaml > system-redis-secret.yaml

Change the value of the backend-redis secret REDIS_QUEUES_URL on the cluster to:

redis://<Valkey Redis endpoint>:6379/1

Change the value of the backend-redis secret REDIS_STORAGE_URL on the cluster to:

redis://<Valkey Redis endpoint>:6379/0

Change the value of the system-redis secret URL on the cluster to:

redis://<Valkey Redis endpoint>:6379/0
oc scale deployment threescale-operator-controller-manager-v2 -n $OPERATOR_NAMESPACE --replicas=1
oc scale deployment system-memcache -n $THREESCALE_NAMESPACE --replicas=$SYSTEM_MEMCACHE_REPLICA_COUNT
oc scale deployment zync-database -n $THREESCALE_NAMESPACE --replicas=$ZYNC_DATABASE_REPLICA_COUNT
oc scale deployment apicast-production -n $THREESCALE_NAMESPACE --replicas=$APICAST_PRODUCTION_REPLICA_COUNT
oc scale deployment apicast-staging -n $THREESCALE_NAMESPACE --replicas=$APICAST_STAGING_REPLICA_COUNT
oc scale deployment backend-cron -n $THREESCALE_NAMESPACE --replicas=$BACKEND_CRON_REPLICA_COUNT
oc scale deployment backend-listener -n $THREESCALE_NAMESPACE --replicas=$BACKEND_LISTENER_REPLICA_COUNT
oc scale deployment backend-worker -n $THREESCALE_NAMESPACE --replicas=$BACKEND_WORKER_REPLICA_COUNT
oc scale deployment system-app -n $THREESCALE_NAMESPACE --replicas=$SYSTEM_APP_REPLICA_COUNT
oc scale deployment system-sidekiq -n $THREESCALE_NAMESPACE --replicas=$SYSTEM_SIDEKIQ_REPLICA_COUNT
oc scale deployment system-searchd -n $THREESCALE_NAMESPACE --replicas=$SYSTEM_SEARCHD_REPLICA_COUNT
oc scale deployment zync -n $THREESCALE_NAMESPACE --replicas=$ZYNC_REPLICA_COUNT
oc scale deployment zync-que -n $THREESCALE_NAMESPACE --replicas=$ZYNC_QUE_REPLICA_COUNT

Wait for 3scale instances to fully recover and confirm that the migration data was successful.

Once the 3scale instance state is confirmed to be correct, the on-cluster Redis instances can be deleted.

5.4.9. Validate and Test

After importing the data and configuring Valkey, it’s crucial to validate and test the new setup.

To verify that the data is migrated, determine the number of keys in the Valkey database. If the migration is successful, then the number of keys in the Valkey database match the number of keys in the Redis database that you obtained in previous step:

sh-5.2$ redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> INFO KEYSPACE
# Keyspace
db1:keys=6286,expires=1,avg_ttl=53682

Test all application features to verify that they work as expected with Valkey.

5.4.10. Restore in case of failure

If the migration is unsuccessful and you need to revert back to the previous configuration:

  • Scale entire instance down:
oc scale deployment threescale-operator-controller-manager-v2 -n $OPERATOR_NAMESPACE --replicas=0

oc scale deployment/{apicast-production,apicast-staging,backend-cron,backend-listener,backend-worker,system-app,system-memcache,system-sidekiq,system-searchd,zync,zync-database,zync-que} -n $THREESCALE_NAMESPACE --replicas=0
  • Remove the backend-redis and system-redis secrets
oc delete secret system-redis -n $THREESCALE_NAMESPACE
oc delete secret backend-redis -n $THREESCALE_NAMESPACE
  • Re-create secrets from the backed-up secrets
oc apply -f backend-redis-secret.yaml
oc apply -f system-redis-secret.yaml
  • Scale up 3scale instance
oc scale deployment threescale-operator-controller-manager-v2 -n $OPERATOR_NAMESPACE --replicas=1
oc scale deployment system-memcache -n $THREESCALE_NAMESPACE --replicas=$SYSTEM_MEMCACHE_REPLICA_COUNT
oc scale deployment zync-database -n $THREESCALE_NAMESPACE --replicas=$ZYNC_DATABASE_REPLICA_COUNT
oc scale deployment apicast-production -n $THREESCALE_NAMESPACE --replicas=$APICAST_PRODUCTION_REPLICA_COUNT
oc scale deployment apicast-staging -n $THREESCALE_NAMESPACE --replicas=$APICAST_STAGING_REPLICA_COUNT
oc scale deployment backend-cron -n $THREESCALE_NAMESPACE --replicas=$BACKEND_CRON_REPLICA_COUNT
oc scale deployment backend-listener -n $THREESCALE_NAMESPACE --replicas=$BACKEND_LISTENER_REPLICA_COUNT
oc scale deployment backend-worker -n $THREESCALE_NAMESPACE --replicas=$BACKEND_WORKER_REPLICA_COUNT
oc scale deployment system-app -n $THREESCALE_NAMESPACE --replicas=$SYSTEM_APP_REPLICA_COUNT
oc scale deployment system-sidekiq -n $THREESCALE_NAMESPACE --replicas=$SYSTEM_SIDEKIQ_REPLICA_COUNT
oc scale deployment system-searchd -n $THREESCALE_NAMESPACE --replicas=$SYSTEM_SEARCHD_REPLICA_COUNT
oc scale deployment zync -n $THREESCALE_NAMESPACE --replicas=$ZYNC_REPLICA_COUNT
oc scale deployment zync-que -n $THREESCALE_NAMESPACE --replicas=$ZYNC_QUE_REPLICA_COUNT
Red Hat logoGithubredditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat Documentation

Legal Notice

Theme

© 2026 Red Hat
Back to top