diff --git a/.drone/drone.yml b/.drone/drone.yml
deleted file mode 100644
index e26f433..0000000
--- a/.drone/drone.yml
+++ /dev/null
@@ -1,168 +0,0 @@
-kind: pipeline
-type: docker
-name: linux-amd64
-
-platform:
-  arch: amd64
-  os: linux
-
-steps:
-- name: test
-  image: docker.io/drycc/go-dev
-  pull: always
-  privileged: true
-  commands:
-  - make test
-  environment:
-    VERSION: ${DRONE_TAG:-latest}-linux-amd64
-    DEV_REGISTRY: ${DEV_REGISTRY:-docker.io}
-    DRYCC_REGISTRY: ${DRYCC_REGISTRY:-docker.io}
-  when:
-    event:
-    - push
-    - tag
-    - pull_request
-  volumes:
-  - name: image_registries
-    path: /etc/containers/registries.conf
-
-- name: publish
-  image: docker.io/drycc/go-dev
-  pull: always
-  privileged: true
-  commands:
-  - echo $DOCKER_PASSWORD | docker login $DRYCC_REGISTRY --username $DOCKER_USERNAME --password-stdin
-  - make docker-build docker-immutable-push
-  environment:
-    VERSION: ${DRONE_TAG:-latest}-linux-amd64
-    DEV_REGISTRY:
-      from_secret: dev_registry
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-    DOCKER_USERNAME:
-      from_secret: docker_username
-    DOCKER_PASSWORD:
-      from_secret: docker_password
-  when:
-    event:
-    - push
-    - tag
-  volumes:
-  - name: image_registries
-    path: /etc/containers/registries.conf
-
-trigger:
-  event:
-  - push
-  - tag
-  - pull_request
-
-volumes:
-- name: image_registries
-  host:
-    path: /etc/containers/registries.conf
-
----
-kind: pipeline
-type: docker
-name: linux-arm64
-
-platform:
-  arch: arm64
-  os: linux
-
-steps:
-- name: publish
-  image: docker.io/drycc/go-dev
-  pull: always
-  privileged: true
-  commands:
-  - echo $DOCKER_PASSWORD | docker login $DRYCC_REGISTRY --username $DOCKER_USERNAME --password-stdin
-  - make docker-build docker-immutable-push
-  environment:
-    VERSION: ${DRONE_TAG:-latest}-linux-arm64
-    DEV_REGISTRY:
-      from_secret: dev_registry
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-    DOCKER_USERNAME:
-      from_secret: docker_username
-    DOCKER_PASSWORD:
-      from_secret: docker_password
-  volumes:
-  - name: image_registries
-    path: /etc/containers/registries.conf
-
-trigger:
-  event:
-  - push
-  - tag
-
-volumes:
-- name: image_registries
-  host:
-    path: /etc/containers/registries.conf
-
----
-kind: pipeline
-type: docker
-name: manifest
-
-steps:
-- name: generate manifest
-  image: docker.io/library/alpine
-  pull: always
-  commands:
-  - sed -i "s/docker.io/$${DRYCC_REGISTRY}/g" .drone/manifest.tmpl
-  environment:
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-
-- name: publish
-  image: plugins/manifest
-  settings:
-    spec: .drone/manifest.tmpl
-    username:
-      from_secret: docker_username
-    password:
-      from_secret: docker_password
-  environment:
-    DEV_REGISTRY:
-      from_secret: dev_registry
-    DRYCC_REGISTRY:
-      from_secret: drycc_registry
-
-trigger:
-  event:
-  - push
-  - tag
-
-depends_on:
-- linux-amd64
-- linux-arm64
-
----
-kind: pipeline
-type: docker
-name: chart
-
-steps:
-- name: generate chart
-  image: docker.io/drycc/python-dev
-  commands:
-  - IMAGE_TAG=$([ ! -z $DRONE_TAG ] && echo \"${DRONE_TAG:1}\" || echo \"canary\")
-  - sed -i "s/image_tag:\ \"canary\"/image_tag:\ $IMAGE_TAG/g" charts/redis/values.yaml
-  - helm package charts/redis --version ${DRONE_TAG:-v1.0.0}
-  - curl -u $CHARTMUSEUM_USERNAME:$CHARTMUSEUM_PASSWORD -F chart=@redis-${DRONE_TAG:-v1.0.0}.tgz "$CHARTMUSEUM_API/api/$([ -z $DRONE_TAG ] && echo testing || echo stable)/charts"
-  environment:
-    CHARTMUSEUM_USERNAME:
-      from_secret: chartmuseum_username
-    CHARTMUSEUM_PASSWORD:
-      from_secret: chartmuseum_password
-    CHARTMUSEUM_API:
-      from_secret: chartmuseum_api
-
-trigger:
-  event:
-  - push
-  - tag
diff --git a/.drone/manifest.tmpl b/.drone/manifest.tmpl
deleted file mode 100644
index 4ee77d8..0000000
--- a/.drone/manifest.tmpl
+++ /dev/null
@@ -1,18 +0,0 @@
-image: docker.io/drycc/redis:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}}
-{{#if build.tags}}
-tags:
-{{#each build.tags}}
-  - {{this}}
-{{/each}}
-{{/if}}
-manifests:
-  -
-    image: docker.io/drycc/redis:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64
-    platform:
-      architecture: amd64
-      os: linux
-  -
-    image: docker.io/drycc/redis:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64
-    platform:
-      architecture: arm64
-      os: linux
diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml
new file mode 100644
index 0000000..ef4ecfd
--- /dev/null
+++ b/.woodpecker/build-linux.yml
@@ -0,0 +1,35 @@
+matrix:
+  platform:
+    - linux/amd64
+    - linux/arm64
+
+labels:
+  type: exec
+  platform: ${platform}
+
+steps:
+- name: publish-linux
+  image: bash
+  commands:
+  - export VERSION=$([ -z $CI_COMMIT_TAG ] && echo latest || echo $CI_COMMIT_TAG)-$(sed 's#/#-#g' <<< $CI_SYSTEM_PLATFORM)
+  - echo $CONTAINER_PASSWORD | podman login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1
+  - make podman-build podman-immutable-push
+  environment:
+    CODENAME:
+      from_secret: codename
+    DEV_REGISTRY:
+      from_secret: dev_registry
+    DRYCC_REGISTRY:
+      from_secret: drycc_registry
+    CONTAINER_USERNAME:
+      from_secret: container_username
+    CONTAINER_PASSWORD:
+      from_secret: container_password
+  when:
+    event:
+    - push
+    - tag
+    - cron
+
+depends_on:
+- test-linux
\ No newline at end of file
diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml
new file mode 100644
index 0000000..cfcd3ec
--- /dev/null
+++ b/.woodpecker/chart.yaml
@@ -0,0 +1,33 @@
+labels:
+  type: exec
+  platform: linux/amd64
+
+steps:
+- name: generate-chart
+  image: bash
+  commands:
+  - export VERSION=$(sed 's#v##' <<< $CI_COMMIT_TAG)
+  - export IMAGE_TAG=$([ ! -z $CI_COMMIT_TAG ] && echo \"$VERSION\" || echo \"canary\")
+  - export APP_VERSION=$([ -z $CI_COMMIT_TAG ] && echo $CI_COMMIT_SHA || echo $VERSION)
+  - export CHART_VERSION=$([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION)
+  - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/$${CI_REPO_NAME}/values.yaml
+  - helm package -u charts/$${CI_REPO_NAME} --version $CHART_VERSION --app-version $APP_VERSION
+  - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin
+  - helm push $${CI_REPO_NAME}-$CHART_VERSION.tgz oci://$DRYCC_REGISTRY/$([ -z $CI_COMMIT_TAG ] && echo charts-testing || echo charts)
+  environment:
+    DEV_REGISTRY:
+      from_secret: dev_registry
+    DRYCC_REGISTRY:
+      from_secret: drycc_registry
+    CONTAINER_USERNAME:
+      from_secret: container_username
+    CONTAINER_PASSWORD:
+      from_secret: container_password
+  when:
+    event:
+    - push
+    - tag
+    - cron
+
+depends_on:
+- manifest
diff --git a/.woodpecker/manifest.tmpl b/.woodpecker/manifest.tmpl
new file mode 100644
index 0000000..8739535
--- /dev/null
+++ b/.woodpecker/manifest.tmpl
@@ -0,0 +1,18 @@
+image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}}
+{{#if build.tags}}
+tags:
+{{#each build.tags}}
+  - {{this}}
+{{/each}}
+{{/if}}
+manifests:
+  -
+    image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64
+    platform:
+      architecture: amd64
+      os: linux
+  -
+    image: registry.drycc.cc/drycc/{{project}}:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64
+    platform:
+      architecture: arm64
+      os: linux
diff --git a/.woodpecker/manifest.yml b/.woodpecker/manifest.yml
new file mode 100644
index 0000000..19fc70c
--- /dev/null
+++ b/.woodpecker/manifest.yml
@@ -0,0 +1,43 @@
+labels:
+  type: exec
+  platform: linux/amd64
+
+steps:
+- name: generate-manifest
+  image: bash
+  commands:
+  - sed -i "s/{{project}}/$${CI_REPO_NAME}/g" .woodpecker/manifest.tmpl
+  - sed -i "s/registry.drycc.cc/$${DRYCC_REGISTRY}/g" .woodpecker/manifest.tmpl
+  environment:
+    DRYCC_REGISTRY:
+      from_secret: drycc_registry
+  when:
+    event:
+    - tag
+    - push
+    - cron
+
+- name: publish-manifest
+  image: bash
+  commands:
+  - podman run --rm
+    -e PLUGIN_SPEC=.woodpecker/manifest.tmpl
+    -e PLUGIN_USERNAME=$CONTAINER_USERNAME
+    -e PLUGIN_PASSWORD=$CONTAINER_PASSWORD
+    -e DRONE_TAG=$CI_COMMIT_TAG
+    -v $(pwd):$(pwd)
+    -w $(pwd)
+    docker.io/plugins/manifest
+  environment:
+    CONTAINER_USERNAME:
+      from_secret: container_username
+    CONTAINER_PASSWORD:
+      from_secret: container_password
+  when:
+    event:
+    - tag
+    - push
+    - cron
+
+depends_on:
+- build-linux
diff --git a/.woodpecker/test-linux.yml b/.woodpecker/test-linux.yml
new file mode 100644
index 0000000..194832f
--- /dev/null
+++ b/.woodpecker/test-linux.yml
@@ -0,0 +1,24 @@
+matrix:
+  platform:
+  - linux/amd64
+  - linux/arm64
+
+labels:
+  type: exec
+  platform: ${platform}
+
+steps:
+- name: test-linux
+  image: bash
+  commands:
+  - make test
+  environment:
+    CODENAME:
+      from_secret: codename
+    DEV_REGISTRY:
+      from_secret: dev_registry
+  when:
+    event:
+    - push
+    - tag
+    - cron
diff --git a/Makefile b/Makefile
index 03ff086..6c8d6c9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,38 +1,36 @@
 # Short name: Short name, following [a-zA-Z_], used all over the place.
 # Some uses for short name:
-# - Docker image name
+# - Container image name
 # - Kubernetes service, rc, pod, secret, volume names
-SHORT_NAME := redis
-DRYCC_REGISTY ?= ${DEV_REGISTRY}
+SHORT_NAME := valkey
+DRYCC_REGISTRY ?= ${DEV_REGISTRY}
 IMAGE_PREFIX ?= drycc
 PLATFORM ?= linux/amd64,linux/arm64
 
 include versioning.mk
 
-SHELL_SCRIPTS = $(wildcard _scripts/*.sh) rootfs/bin/boot
+SHELL_SCRIPTS = $(wildcard _test/*.sh) rootfs/bin/valkey-start rootfs/scripts/*
 
 # The following variables describe the containerized development environment
 # and other build options
 DEV_ENV_IMAGE := ${DEV_REGISTRY}/drycc/go-dev
-DEV_ENV_WORK_DIR := /go/src/${REPO_PATH}
-DEV_ENV_CMD := docker run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${DEV_ENV_IMAGE}
-DEV_ENV_CMD_INT := docker run -it --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${DEV_ENV_IMAGE}
+DEV_ENV_WORK_DIR := /opt/drycc/go/src/${REPO_PATH}
+DEV_ENV_CMD := podman run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${DEV_ENV_IMAGE}
+DEV_ENV_CMD_INT := podman run -it --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${DEV_ENV_IMAGE}
 
-all: docker-build docker-push
+all: podman-build podman-push
 
 dev:
 	${DEV_ENV_CMD_INT} bash
 
 # For cases where we're building from local
 # We also alter the RC file to set the image name.
-docker-build:
-	docker build ${DOCKER_BUILD_FLAGS} -t ${IMAGE} rootfs
-	docker tag ${IMAGE} ${MUTABLE_IMAGE}
+podman-build:
+	podman build --build-arg CODENAME=${CODENAME} -t ${IMAGE} rootfs
+	podman tag ${IMAGE} ${MUTABLE_IMAGE}
 
-docker-buildx:
-	docker buildx build --platform ${PLATFORM} ${DOCKER_BUILD_FLAGS} -t ${IMAGE} rootfs --push
-
-test: test-style
+test: podman-build test-style
+	./_test/test.sh ${VERSION}
 
 test-style:
 	${DEV_ENV_CMD} shellcheck $(SHELL_SCRIPTS)
diff --git a/README.md b/README.md
index a73cc65..ea5a0de 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,21 @@
-# Drycc Redis
-[![Build Status](https://drone.drycc.cc/api/badges/drycc/redis/status.svg)](https://drone.drycc.cc/drycc/redis)
+# Drycc Valkey
+[![Build Status](https://woodpecker.drycc.cc/api/badges/drycc/valkey/status.svg)](https://woodpecker.drycc.cc/drycc/valkey)
 
 Drycc (pronounced DAY-iss) Workflow is an open source Platform as a Service (PaaS) that adds a developer-friendly layer to any [Kubernetes](http://kubernetes.io) cluster, making it easy to deploy and manage applications on your own servers.
 
+## Usage
+
+Different components use different db, as follows:
+
+* `controller` use db 0
+* `passport` use db 1
+* `grafana` use db 2
+* `manager` use db 10
+* `helmbroker` use db 11
+
+The above are the default configurations for each component.
+
 ## Description
-A Docker image for running standalone (not clustered) Redis on a Kubernetes cluster.
+A Container image for running standalone (not clustered) Valkey on a Kubernetes cluster.
 
 [v2.18]: https://github.com/drycc/workflow/releases/tag/v2.18.0
diff --git a/_test/test.sh b/_test/test.sh
new file mode 100755
index 0000000..574b989
--- /dev/null
+++ b/_test/test.sh
@@ -0,0 +1,140 @@
+#!/usr/bin/env bash
+
+VERSION="$1"
+
+CONTAINER_PROXY_NAME="valkey-benchmark-proxy"
+CONTAINER_MASTER_NAME="valkey-benchmark-master"
+CONTAINER_SLAVE1_NAME="valkey-benchmark-slave1"
+CONTAINER_SLAVE2_NAME="valkey-benchmark-slave2"
+DRYCC_VALKEY_PASSWORD=123456
+
+function clean_before_exit {
+    # delay before exiting, so stdout/stderr flushes through the logging system
+    clean-valkey
+}
+trap clean_before_exit EXIT
+
+start-valkey-master() {
+    podman run -d \
+      --rm \
+      --env "REDISCLI_AUTH=$DRYCC_VALKEY_PASSWORD" \
+      --env "DRYCC_VALKEY_PASSWORD=$DRYCC_VALKEY_PASSWORD" \
+      --name "$CONTAINER_MASTER_NAME" \
+      "registry.drycc.cc/drycc/valkey:$VERSION" \
+      sleep infinity
+
+    master_ip=$(podman exec "$CONTAINER_MASTER_NAME" hostname -i | tr -d '\r\n')
+    podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_MASTER_NAME" init-stack valkey-start server "$master_ip"  &
+    podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_MASTER_NAME" init-stack valkey-start sentinel "$master_ip" &
+}
+
+start-valkey-slave1() {
+    podman run -d \
+      --rm \
+      --env "REDISCLI_AUTH=$DRYCC_VALKEY_PASSWORD" \
+      --env "DRYCC_VALKEY_PASSWORD=$DRYCC_VALKEY_PASSWORD" \
+      --name "$CONTAINER_SLAVE1_NAME" \
+      "registry.drycc.cc/drycc/valkey:$VERSION" \
+      sleep infinity
+
+    slave1_ip=$(podman exec -it "$CONTAINER_SLAVE1_NAME" hostname -i | tr -d '\r\n')
+    podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_SLAVE1_NAME" init-stack valkey-start server "$slave1_ip" &
+    podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_SLAVE1_NAME" init-stack valkey-start sentinel "$slave1_ip" &
+}
+
+start-valkey-slave2() {
+    podman run -d \
+      --rm \
+      --env "REDISCLI_AUTH=$DRYCC_VALKEY_PASSWORD" \
+      --env "DRYCC_VALKEY_PASSWORD=$DRYCC_VALKEY_PASSWORD" \
+      --name "$CONTAINER_SLAVE2_NAME" \
+      "registry.drycc.cc/drycc/valkey:$VERSION" \
+      sleep infinity
+
+    slave2_ip=$(podman exec -it "$CONTAINER_SLAVE2_NAME" hostname -i | tr -d '\r\n')
+    podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_SLAVE2_NAME" init-stack valkey-start server "$slave2_ip" &
+    podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_SLAVE2_NAME" init-stack valkey-start sentinel "$slave2_ip" &
+}
+
+start-valkey-proxy() {
+    podman run -d \
+      --rm \
+      --env "REDISCLI_AUTH=$DRYCC_VALKEY_PASSWORD" \
+      --env "DRYCC_VALKEY_PASSWORD=$DRYCC_VALKEY_PASSWORD" \
+      --name "$CONTAINER_PROXY_NAME" \
+      "registry.drycc.cc/drycc/valkey:$VERSION" \
+      sleep infinity
+
+    podman exec --env "DRYCC_VALKEY_SENTINEL=$master_ip" "$CONTAINER_PROXY_NAME" init-stack valkey-start proxy &
+}
+
+clean-valkey() {
+    {
+        podman kill "$CONTAINER_PROXY_NAME"
+        podman kill "$CONTAINER_SLAVE1_NAME"
+        podman kill "$CONTAINER_SLAVE2_NAME"
+        podman kill "$CONTAINER_MASTER_NAME"
+    } >>/dev/null 2>&1
+}
+
+clean-valkey
+start-valkey-master
+start-valkey-slave1
+start-valkey-slave2
+start-valkey-proxy
+echo "run valkey proxy benchmark..."
+podman exec "$CONTAINER_PROXY_NAME" init-stack valkey-benchmark -p 16379 -a $DRYCC_VALKEY_PASSWORD
+
+echo "run valkey master benchmark..."
+podman exec "$CONTAINER_MASTER_NAME" init-stack valkey-benchmark -a $DRYCC_VALKEY_PASSWORD
+
+echo "check slave all keys..."
+KEYS=$(podman exec "$CONTAINER_MASTER_NAME" bash -c 'init-stack valkey-cli KEYS "*"')
+if [[ "${KEYS}" == "" ]]; then
+    echo "error: there is no data from the database"
+    exit 1
+fi
+
+echo "check sentinel $CONTAINER_MASTER_NAME get master..."
+MASTER=$(podman exec "$CONTAINER_MASTER_NAME" init-stack valkey-cli -p 26379 sentinel get-master-addr-by-name drycc)
+if [[ "${MASTER}" == "" ]]; then
+    echo "error: unable to obtain master information"
+    exit 1
+fi
+
+echo "check sentinel $CONTAINER_SLAVE1_NAME get master..."
+MASTER=$(podman exec "$CONTAINER_SLAVE1_NAME" init-stack valkey-cli -p 26379 sentinel get-master-addr-by-name drycc)
+if [[ "${MASTER}" == "" ]]; then
+    echo "error: unable to obtain master information"
+    exit 1
+fi
+
+echo "check sentinel $CONTAINER_SLAVE2_NAME get master..."
+MASTER=$(podman exec "$CONTAINER_SLAVE2_NAME" init-stack valkey-cli -p 26379 sentinel get-master-addr-by-name drycc)
+if [[ "${MASTER}" == "" ]]; then
+    echo "error: unable to obtain master information"
+    exit 1
+fi
+
+echo "check sentinel $CONTAINER_MASTER_NAME get slaves..."
+SLAVES=$(podman exec "$CONTAINER_MASTER_NAME" init-stack valkey-cli -p 26379 sentinel replicas drycc)
+if [[ "${SLAVES}" == "" ]]; then
+    echo "error: unable to obtain slaves information"
+    exit 1
+fi
+
+echo "check sentinel $CONTAINER_SLAVE1_NAME get slaves..."
+SLAVES=$(podman exec "$CONTAINER_SLAVE1_NAME" init-stack valkey-cli -p 26379 sentinel replicas drycc)
+if [[ "${SLAVES}" == "" ]]; then
+    echo "error: unable to obtain slaves information"
+    exit 1
+fi
+
+echo "check sentinel $CONTAINER_SLAVE2_NAME get slaves..."
+SLAVES=$(podman exec "$CONTAINER_SLAVE2_NAME" init-stack valkey-cli -p 26379 sentinel replicas drycc)
+if [[ "${SLAVES}" == "" ]]; then
+    echo "error: unable to obtain slaves information"
+    exit 1
+fi
+
+echo "all test ok..."
diff --git a/charts/redis/Chart.yaml b/charts/redis/Chart.yaml
deleted file mode 100644
index ae744a4..0000000
--- a/charts/redis/Chart.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: redis
-home: https://github.com/drycc/redis
-version: v1.0.0
-description: A Redis database for use inside a Kubernetes cluster.
-maintainers:
-  - name: Drycc Team
-    email: engineering@drycc.com
diff --git a/charts/redis/templates/redis-secret-creds.yaml b/charts/redis/templates/redis-secret-creds.yaml
deleted file mode 100644
index eb4dd1f..0000000
--- a/charts/redis/templates/redis-secret-creds.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-apiVersion: v1
-kind: Secret
-metadata:
-  name: redis-creds
-  labels:
-    app: drycc-redis
-    heritage: drycc
-  annotations:
-    "helm.sh/hook": pre-install
-data: 
-  {{ if eq .Values.global.redis_location "on-cluster"}}
-  password: {{ randAlphaNum 32 | b64enc }}
-  {{ else if eq .Values.global.redis_location "off-cluster"}}
-  addrs: {{ .Values.addrs | b64enc }}
-  password: {{ .Values.password | b64enc }}
-  {{ end }}
diff --git a/charts/redis/templates/redis-statefulset.yaml b/charts/redis/templates/redis-statefulset.yaml
deleted file mode 100644
index 8d69964..0000000
--- a/charts/redis/templates/redis-statefulset.yaml
+++ /dev/null
@@ -1,45 +0,0 @@
-{{- if eq .Values.global.redis_location "on-cluster" }}
-apiVersion: apps/v1
-kind: StatefulSet
-metadata:
-  name: drycc-redis
-  labels:
-    heritage: drycc
-  annotations:
-    component.drycc.cc/version: {{ .Values.image_tag }}
-spec:
-  serviceName: drycc-redis
-  replicas: {{ .Values.replicas }}
-  selector:
-    matchLabels:
-      app: drycc-redis
-  template:
-    metadata:
-      labels:
-        app: drycc-redis
-    spec:
-      containers:
-      - name: drycc-redis
-        image: {{.Values.image_registry}}/{{.Values.image_org}}/redis:{{ .Values.image_tag }}
-        imagePullPolicy: {{ .Values.image_pull_policy }}
-{{- if or (.Values.limits_cpu) (.Values.limits_memory)}}
-        resources:
-          limits:
-{{- if (.Values.limits_cpu) }}
-            cpu: {{.Values.limits_cpu}}
-{{- end}}
-{{- if (.Values.limits_memory) }}
-            memory: {{.Values.limits_memory}}
-{{- end}}
-{{- end}}
-        command: ["/bin/boot", "--port", "6379"]
-        ports:
-        - containerPort: 6379
-        volumeMounts:
-        - name: redis-creds
-          mountPath: /var/run/secrets/drycc/redis/creds
-      volumes:
-      - name: redis-creds
-        secret:
-          secretName: redis-creds
-{{- end }}
diff --git a/charts/redis/templates/redis-svc.yaml b/charts/redis/templates/redis-svc.yaml
deleted file mode 100644
index 6d32a80..0000000
--- a/charts/redis/templates/redis-svc.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-{{- if eq .Values.global.redis_location "on-cluster" }}
-apiVersion: v1
-kind: Service
-metadata:
-  name: drycc-redis
-  labels:
-    heritage: drycc
-spec:
-  clusterIP: None
-  selector:
-    app: drycc-redis
-  ports:
-  - port: 6379
-{{- end }}
diff --git a/charts/redis/values.yaml b/charts/redis/values.yaml
deleted file mode 100644
index a472306..0000000
--- a/charts/redis/values.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-image_org: "drycc"
-image_pull_policy: "Always"
-image_tag: "canary"
-image_registry: "docker.io"
-# limits_cpu: "100m"
-# limits_memory: "50Mi"
-
-global:
-  # Set the location of Workflow's redis instance
-  #
-  # Valid values are:
-  # - on-cluster: Run Redis within the Kubernetes cluster
-  # - off-cluster: Run Redis outside the Kubernetes cluster (configure in redis section)
-  redis_location: "on-cluster"
-
-# The following parameters are configured only when using an on-cluster Redis instance
-replicas: 1
-
-# The following parameters are configured only when using an off-cluster Redis instance
-addrs: "" # A list of clusters: "127.0.0.1:7001/1,127.0.0.2:7002/1"
-password: "redis password" # "" == no password
diff --git a/charts/valkey/Chart.yaml b/charts/valkey/Chart.yaml
new file mode 100644
index 0000000..5e7e12d
--- /dev/null
+++ b/charts/valkey/Chart.yaml
@@ -0,0 +1,13 @@
+name: valkey
+home: https://github.com/drycc/valkey
+apiVersion: v2
+appVersion: 1.0.0
+dependencies:
+  - name: common
+    repository: oci://registry.drycc.cc/charts
+    version: ~1.1.2
+description: A Valkey database for use inside a Kubernetes cluster.
+maintainers:
+  - name: Drycc Team
+    email: engineering@drycc.com
+version: v1.0.0
diff --git a/charts/valkey/templates/valkey-secret-creds.yaml b/charts/valkey/templates/valkey-secret-creds.yaml
new file mode 100644
index 0000000..33c81d2
--- /dev/null
+++ b/charts/valkey/templates/valkey-secret-creds.yaml
@@ -0,0 +1,9 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: valkey-creds
+  labels:
+    app: drycc-valkey
+    heritage: drycc
+data:
+  password: {{ include "common.secrets.lookup" (dict "secret" "valkey-creds" "key" "password" "defaultValue" (randAlphaNum 32) "context" $) }}
diff --git a/charts/valkey/templates/valkey-statefulset.yaml b/charts/valkey/templates/valkey-statefulset.yaml
new file mode 100644
index 0000000..be223df
--- /dev/null
+++ b/charts/valkey/templates/valkey-statefulset.yaml
@@ -0,0 +1,290 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: drycc-valkey
+  labels:
+    heritage: drycc
+  annotations:
+    component.drycc.cc/version: {{ .Values.imageTag }}
+spec:
+  serviceName: drycc-valkey
+  replicas: {{ .Values.replicas }}
+  selector:
+    matchLabels:
+      app: drycc-valkey
+  template:
+    metadata:
+      labels: {{- include "common.labels.standard" . | nindent 8 }}
+        app: drycc-valkey
+    spec:
+      affinity:
+        podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset.type "component" "" "extraMatchLabels" .Values.podAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }}
+        podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset.type "component" "" "extraMatchLabels" .Values.podAntiAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }}
+        nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values ) | nindent 10 }}
+      containers:
+      - name: drycc-valkey-proxy
+        image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/valkey:{{ .Values.imageTag }}
+        imagePullPolicy: {{ .Values.imagePullPolicy }}
+        {{- if .Values.diagnosticMode.enabled }}
+        command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }}
+        args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }}
+        {{- else }}
+        args:
+        - valkey-start
+        - proxy
+        {{- end }}
+        env:
+        - name: DRYCC_VALKEY_PASSWORD
+          valueFrom:
+            secretKeyRef:
+              name: valkey-creds
+              key: password
+        - name: DRYCC_VALKEY_SENTINEL
+          value: drycc-valkey
+        ports:
+        - containerPort: 16379
+        {{- with index .Values "proxy" "resources" }}
+        resources:
+          {{- toYaml . | nindent 10 }}
+        {{- end }}
+        startupProbe:
+          initialDelaySeconds: 10
+          periodSeconds: 10
+          timeoutSeconds: 5
+          successThreshold: 1
+          failureThreshold: 22
+          tcpSocket:
+            port: 16379
+        livenessProbe:
+          initialDelaySeconds: 20
+          periodSeconds: 5
+          timeoutSeconds: 5
+          successThreshold: 1
+          failureThreshold: 5
+          tcpSocket:
+            port: 16379
+        readinessProbe:
+          initialDelaySeconds: 20
+          periodSeconds: 5
+          timeoutSeconds: 1
+          successThreshold: 1
+          failureThreshold: 5
+          tcpSocket:
+            port: 16379
+      - name: drycc-valkey-server
+        image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/valkey:{{ .Values.imageTag }}
+        imagePullPolicy: {{ .Values.imagePullPolicy }}
+        {{- if .Values.diagnosticMode.enabled }}
+        command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }}
+        args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }}
+        {{- else }}
+        args:
+        - valkey-start
+        - server
+        - $(POD_NAME).drycc-valkey
+        {{- end }}
+        ports:
+        - containerPort: 6379
+        {{- with index .Values "server" "resources" }}
+        resources:
+          {{- toYaml . | nindent 10 }}
+        {{- end }}
+        env:
+        - name: POD_NAME
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.name
+        - name: DRYCC_VALKEY_SENTINEL
+          value: drycc-valkey
+        - name: DRYCC_VALKEY_PASSWORD
+          valueFrom:
+            secretKeyRef:
+              name: valkey-creds
+              key: password
+        startupProbe:
+          initialDelaySeconds: 10
+          periodSeconds: 10
+          timeoutSeconds: 5
+          successThreshold: 1
+          failureThreshold: 22
+          tcpSocket:
+            port: 6379
+        livenessProbe:
+          initialDelaySeconds: 20
+          periodSeconds: 5
+          timeoutSeconds: 5
+          successThreshold: 1
+          failureThreshold: 5
+          exec:
+            command:
+              - init-stack
+              - sh
+              - -c
+              - /scripts/ping_liveness_local.sh 5
+        readinessProbe:
+          initialDelaySeconds: 20
+          periodSeconds: 5
+          timeoutSeconds: 1
+          successThreshold: 1
+          failureThreshold: 5
+          exec:
+            command:
+              - init-stack
+              - sh
+              - -c
+              - /scripts/ping_readiness_local.sh 1
+        {{- if .Values.persistence.enabled }}
+        volumeMounts:
+        - name: valkey-data
+          mountPath: /data
+        {{- end }}
+        lifecycle:
+          preStop:
+            exec:
+              command:
+                - init-stack
+                - /bin/bash
+                - -c
+                - /scripts/prestop-valkey.sh
+      - name: drycc-valkey-sentinel
+        image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/valkey:{{ .Values.imageTag }}
+        imagePullPolicy: {{ .Values.imagePullPolicy }}
+        {{- if .Values.diagnosticMode.enabled }}
+        command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }}
+        args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }}
+        {{- else }}
+        args:
+        - valkey-start
+        - sentinel
+        - $(POD_NAME).drycc-valkey
+        {{- end }}
+        ports:
+        - containerPort: 26379
+        {{- with index .Values "sentinel" "resources" }}
+        resources:
+          {{- toYaml . | nindent 10 }}
+        {{- end }}
+        env:
+        - name: POD_NAME
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.name
+        - name: DRYCC_VALKEY_SENTINEL
+          value: drycc-valkey
+        - name: DRYCC_VALKEY_PASSWORD
+          valueFrom:
+            secretKeyRef:
+              name: valkey-creds
+              key: password
+        startupProbe:
+          initialDelaySeconds: 10
+          periodSeconds: 10
+          timeoutSeconds: 5
+          successThreshold: 1
+          failureThreshold: 22
+          tcpSocket:
+            port: 26379
+        livenessProbe:
+          initialDelaySeconds: 20
+          periodSeconds: 5
+          timeoutSeconds: 5
+          successThreshold: 1
+          failureThreshold: 5
+          exec:
+            command:
+            - init-stack
+            - sh
+            - -c
+            - /scripts/ping_sentinel.sh 5
+        readinessProbe:
+          initialDelaySeconds: 20
+          periodSeconds: 5
+          timeoutSeconds: 1
+          successThreshold: 1
+          failureThreshold: 5
+          exec:
+            command:
+            - init-stack
+            - sh
+            - -c
+            - /scripts/ping_sentinel.sh 1
+        {{- if .Values.persistence.enabled }}
+        volumeMounts:
+        - name: valkey-data
+          mountPath: /data
+        {{- end }}
+        lifecycle:
+          preStop:
+            exec:
+              command:
+                - init-stack
+                - /bin/bash
+                - -c
+                - /scripts/prestop-sentinel.sh
+      - name: drycc-valkey-metrics
+        image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/valkey:{{ .Values.imageTag }}
+        imagePullPolicy: {{ .Values.imagePullPolicy }}
+        {{- if .Values.diagnosticMode.enabled }}
+        command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 10 }}
+        args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 10 }}
+        {{- else }}
+        command:
+        - init-stack
+        args:
+        - redis_exporter
+        {{- end }}
+        ports:
+        - containerPort: 9121
+        env:
+        - name: REDIS_PASSWORD
+          valueFrom:
+            secretKeyRef:
+              name: valkey-creds
+              key: password
+        startupProbe:
+          initialDelaySeconds: 10
+          tcpSocket:
+              port: 9121
+          periodSeconds: 10
+          timeoutSeconds: 1
+          failureThreshold: 15
+          successThreshold: 1
+        livenessProbe:
+          initialDelaySeconds: 5
+          httpGet:
+            path: /
+            port: 9121
+          periodSeconds: 10
+          timeoutSeconds: 5
+          failureThreshold: 6
+          successThreshold: 1
+        readinessProbe:
+          initialDelaySeconds: 5
+          httpGet:
+            path: /
+            port: 9121
+          periodSeconds: 10
+          timeoutSeconds: 5
+          failureThreshold: 6
+          successThreshold: 1
+      securityContext:
+        fsGroup: 1001
+        runAsGroup: 1001
+        runAsUser: 1001
+  {{- if .Values.persistence.enabled }}
+  volumeClaimTemplates:
+  - metadata:
+      name: valkey-data
+    spec:
+      accessModes: [ "ReadWriteOnce" ]
+      {{- if .Values.persistence.storageClass }}
+      {{- if (eq "-" .Values.persistence.storageClass) }}
+      storageClassName: ""
+      {{- else }}
+      storageClassName: "{{ .Values.persistence.storageClass }}"
+      {{- end }}
+      {{- end }}
+      resources:
+        requests:
+          storage: {{ .Values.persistence.size | quote }}
+  {{- end }}
diff --git a/charts/valkey/templates/valkey-svc.yaml b/charts/valkey/templates/valkey-svc.yaml
new file mode 100644
index 0000000..c38af8c
--- /dev/null
+++ b/charts/valkey/templates/valkey-svc.yaml
@@ -0,0 +1,35 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: drycc-valkey
+  annotations:
+    prometheus.io/path: /metrics
+    prometheus.io/port: "9121"
+    prometheus.io/scrape: "true"
+    {{- with .Values.service.annotations }}
+    {{- toYaml . | nindent 4 }}
+    {{- end }}
+  labels:
+    heritage: drycc
+spec:
+  clusterIP: None
+  publishNotReadyAddresses: true
+  ports:
+  - name: proxy
+    port: 16379
+    targetPort: 16379
+    protocol: TCP
+  - name: server
+    port: 6379
+    targetPort: 6379
+    protocol: TCP
+  - name: metrics
+    port: 9121
+    targetPort: 9121
+    protocol: TCP
+  - name: sentinel
+    port: 26379
+    targetPort: 26379
+    protocol: TCP
+  selector:
+    app: drycc-valkey
diff --git a/charts/valkey/values.yaml b/charts/valkey/values.yaml
new file mode 100644
index 0000000..bf19ca1
--- /dev/null
+++ b/charts/valkey/values.yaml
@@ -0,0 +1,76 @@
+imageOrg: "drycc"
+imagePullPolicy: "Always"
+imageTag: "canary"
+imageRegistry: "registry.drycc.cc"
+
+## Enable diagnostic mode
+##
+diagnosticMode:
+  ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden)
+  ##
+  enabled: false
+  ## @param diagnosticMode.command Command to override all containers
+  ##
+  command:
+    - sleep
+  ## @param diagnosticMode.args Args to override all containers
+  ##
+  args:
+    - infinity
+
+nodeAffinityPreset:
+  key: "drycc.cc/node"
+  type: "soft"
+  values:
+  - "true"
+
+podAffinityPreset:
+  type: ""
+  extraMatchLabels:
+    security: "drycc-security"
+
+podAntiAffinityPreset:
+  type: "soft"
+  extraMatchLabels:
+    app: "drycc-valkey"
+
+# The following parameters are configured only when using an on-cluster Valkey instance
+replicas: 3
+
+# Service
+service:
+  # Provide any additional service annotations
+  annotations: {}
+
+proxy:
+  resources: {}
+    # limits:
+    #   cpu: 200m
+    #   memory: 50Mi
+    # requests:
+    #   cpu: 100m
+    #   memory: 30Mi
+
+server:
+  resources: {}
+    # limits:
+    #   cpu: 200m
+    #   memory: 50Mi
+    # requests:
+    #   cpu: 100m
+    #   memory: 30Mi
+
+sentinel:
+  resources: {}
+    # limits:
+    #   cpu: 200m
+    #   memory: 50Mi
+    # requests:
+    #   cpu: 100m
+    #   memory: 30Mi
+
+# GCP PDs and EBS volumes are supported only
+persistence:
+  enabled: false # Set to true to enable persistence
+  size: 5Gi
+  storageClass: ""
diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile
index 723858d..6068bee 100644
--- a/rootfs/Dockerfile
+++ b/rootfs/Dockerfile
@@ -1,10 +1,39 @@
-FROM docker.io/library/redis:6-alpine
+ARG CODENAME
+FROM registry.drycc.cc/drycc/base:${CODENAME}
 
-COPY . /
+ARG DRYCC_UID=1001 \
+  DRYCC_GID=1001 \
+  DRYCC_HOME_DIR=/data \
+  VALKEY_VERSION="9.0.0" \
+  VALKEY_SENTINEL_PROXY_VERSION="2.0.1" \
+  REDIS_EXPORTER_VERSION="1.80.1"
 
-RUN chown -R redis:redis /etc/redis
+RUN groupadd drycc --gid ${DRYCC_GID} \
+  && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR}
 
-USER redis
+COPY scripts /scripts
+COPY etc/valkey /etc/valkey
+COPY bin/valkey-start /bin/valkey-start
 
-CMD ["/bin/boot"]
-EXPOSE 6379
+RUN install-stack valkey ${VALKEY_VERSION} \
+  && install-stack valkey-sentinel-proxy ${VALKEY_SENTINEL_PROXY_VERSION} \
+  && install-stack redis_exporter $REDIS_EXPORTER_VERSION \
+  && rm -rf \
+      /usr/share/doc \
+      /usr/share/man \
+      /usr/share/info \
+      /usr/share/locale \
+      /var/lib/apt/lists/* \
+      /var/log/* \
+      /var/cache/debconf/* \
+      /etc/systemd \
+      /lib/lsb \
+      /lib/udev \
+      /usr/lib/`echo $(uname -m)`-linux-gnu/gconv/IBM* \
+      /usr/lib/`echo $(uname -m)`-linux-gnu/gconv/EBC* \
+  && mkdir -p /usr/share/man/man{1..8} \
+  && chown -R ${DRYCC_UID}:${DRYCC_GID} /etc/valkey ${DRYCC_HOME_DIR}
+
+USER ${DRYCC_UID}
+WORKDIR ${DRYCC_HOME_DIR}
+EXPOSE 6379 9121 26379
diff --git a/rootfs/bin/boot b/rootfs/bin/boot
deleted file mode 100755
index f0b4ab6..0000000
--- a/rootfs/bin/boot
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env sh
-
-set -eof pipefail
-
-REDIS_CONFIG_FILE=/etc/redis/redis.conf
-
-# Set password
-REDIS_PASSWORD_FILE=/var/run/secrets/drycc/redis/creds/password
-if [ -e $REDIS_PASSWORD_FILE ]; then
-    REDIS_PASSWORD="$(cat /var/run/secrets/drycc/redis/creds/password)"
-    if [ -n "$REDIS_PASSWORD" ]; then
-        echo "requirepass $REDIS_PASSWORD" >> $REDIS_CONFIG_FILE
-    fi
-fi
-
-exec redis-server $REDIS_CONFIG_FILE "$@"
diff --git a/rootfs/bin/valkey-start b/rootfs/bin/valkey-start
new file mode 100755
index 0000000..d8204d8
--- /dev/null
+++ b/rootfs/bin/valkey-start
@@ -0,0 +1,126 @@
+#!/usr/bin/env bash
+
+# shellcheck disable=SC1091
+. /scripts/init.sh
+
+print_usage() {
+    echo "Valid commands for valkey-start:"
+    echo ""
+    echo "proxy <announce-host>               start valkey proxy"
+    echo "server <announce-host>              start valkey server"
+    echo "sentinel <announce-host>            start valkey sentinel"
+    echo ""
+    echo "Such as 'valkey-start server valkey.valkey.svc' to start valkey server."
+}
+
+remove_in_file() {
+    local filename="${1:?filename is required}"
+    local match_regex="^\s*${2:?match regex is required} .*"
+    sed -i "/$match_regex/d" "$filename"
+}
+
+get_master_info() {
+    export REDISCLI_AUTH=${DRYCC_VALKEY_PASSWORD}
+    command="valkey-cli -h ${DRYCC_VALKEY_SENTINEL} -p ${SENTINEL_PORT} sentinel get-master-addr-by-name drycc 2>>/dev/null"
+    eval "$command"
+}
+
+wait_for_valkey() {
+    local host="${1:?host is required}"
+    local port="${2:?port is required}"
+    local retries="${3:-30}"
+    log "Waiting for valkey (${host}:${port}) to be ready..."
+    for ((i = 1; i <= retries; i += 1)); do
+        if valkey-cli -h "$host" -p "$port" ping 2>/dev/null | grep -q PONG; then
+            log "Valkey (${host}:${port}) is ready."
+            return 0
+        fi
+        sleep 2s
+    done
+    error "Valkey (${host}:${port}) did not become ready in time"
+}
+
+start_valkey_proxy() {
+    wait_for_valkey "${DRYCC_VALKEY_SENTINEL}" "${SENTINEL_PORT}"
+    exec valkey-sentinel-proxy \
+        --listen=:16379 \
+        --master=drycc \
+        --max-procs=4 \
+        --sentinel-addr="${DRYCC_VALKEY_SENTINEL}":26379 \
+        --sentinel-pass="${DRYCC_VALKEY_PASSWORD}"
+}
+
+start_valkey_server() {
+    announce_ip="$1"
+    VALKEY_CONFIG_FILE=/data/server/valkey.conf
+    if [ ! -f ${VALKEY_CONFIG_FILE} ]; then
+        cp -rf /etc/valkey/valkey-default.conf ${VALKEY_CONFIG_FILE}
+    fi
+
+    # Clean old
+    remove_in_file ${VALKEY_CONFIG_FILE} masterauth
+    remove_in_file ${VALKEY_CONFIG_FILE} requirepass
+    remove_in_file ${VALKEY_CONFIG_FILE} replicaof
+    remove_in_file ${VALKEY_CONFIG_FILE} replica-announce-ip
+    remove_in_file ${VALKEY_CONFIG_FILE} replica-announce-port
+    {
+        printf "\nmasterauth %s" "${DRYCC_VALKEY_PASSWORD}"
+        printf "\nrequirepass %s" "${DRYCC_VALKEY_PASSWORD}"
+        printf "\nreplica-announce-ip %s" "${announce_ip}"
+        printf "\nreplica-announce-port %s" "${SERVER_PORT}"
+    } >> ${VALKEY_CONFIG_FILE}
+    # Set server slaveof
+    if get_master_info; then
+        # shellcheck disable=SC2207
+        master_info=($(get_master_info))
+        if [ "${master_info[0]}" != "${announce_ip}" ]; then
+            printf "\nreplicaof %s %s" "${master_info[0]}" "${master_info[1]}" >> "${VALKEY_CONFIG_FILE}"
+        fi
+    fi
+    exec valkey-server "${VALKEY_CONFIG_FILE}"
+}
+
+start_valkey_sentinel() {
+    announce_ip="$1"
+    VALKEY_SENTINEL_CONFIG_FILE=/data/sentinel/valkey-sentinel.conf
+    if [ ! -f ${VALKEY_SENTINEL_CONFIG_FILE} ]; then
+        cp -rf /etc/valkey/valkey-sentinel-default.conf ${VALKEY_SENTINEL_CONFIG_FILE}
+    fi
+
+    # Clean old
+    remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} masterauth
+    remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} requirepass
+    remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} primaryauth
+    remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} "sentinel auth-pass"
+    remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} "sentinel announce-ip"
+    remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} "sentinel announce-port"
+    remove_in_file ${VALKEY_SENTINEL_CONFIG_FILE} "sentinel monitor"
+
+    # Set sentinel config
+    {
+        printf "\nmasterauth %s" "${DRYCC_VALKEY_PASSWORD}"
+        printf "\nrequirepass %s" "${DRYCC_VALKEY_PASSWORD}"
+        printf "\nprimaryauth %s" "${DRYCC_VALKEY_PASSWORD}"
+        printf "\nsentinel auth-pass drycc %s" "${DRYCC_VALKEY_PASSWORD}"
+        printf "\nsentinel announce-ip %s" "${announce_ip}"
+        printf "\nsentinel announce-port %s" "${SENTINEL_PORT}"
+    } >> ${VALKEY_SENTINEL_CONFIG_FILE}
+
+    # Set monitor
+    if get_master_info; then
+        # shellcheck disable=SC2207
+        master_info=($(get_master_info))
+        printf "\nsentinel monitor drycc %s %s 2" "${master_info[0]}" "${master_info[1]}" >> ${VALKEY_SENTINEL_CONFIG_FILE}
+    else
+        printf "\nsentinel monitor drycc %s %s 2" "${announce_ip}" "${SERVER_PORT}" >> ${VALKEY_SENTINEL_CONFIG_FILE}
+    fi
+    exec valkey-server $VALKEY_SENTINEL_CONFIG_FILE --sentinel
+}
+
+command="$1"
+if [[ ${command} == "proxy" || ${command} == "server" || ${command} == "sentinel" ]]; then
+    "start_valkey_$command" "$2"
+else
+    print_usage
+    exit 1
+fi
diff --git a/rootfs/etc/redis/redis.conf b/rootfs/etc/redis/redis.conf
deleted file mode 100644
index 5b7a8d9..0000000
--- a/rootfs/etc/redis/redis.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-daemonize no
-bind 0.0.0.0
-logfile ""
diff --git a/rootfs/etc/valkey/valkey-default.conf b/rootfs/etc/valkey/valkey-default.conf
new file mode 100644
index 0000000..0b51917
--- /dev/null
+++ b/rootfs/etc/valkey/valkey-default.conf
@@ -0,0 +1,13 @@
+daemonize no
+bind 0.0.0.0
+logfile ""
+dir "/data/server"
+
+save 900 1
+save 300 10
+save 60 1000
+
+appendonly yes
+auto-aof-rewrite-percentage 100
+auto-aof-rewrite-min-size 64mb
+appendfsync everysec
diff --git a/rootfs/etc/valkey/valkey-sentinel-default.conf b/rootfs/etc/valkey/valkey-sentinel-default.conf
new file mode 100644
index 0000000..429f3b6
--- /dev/null
+++ b/rootfs/etc/valkey/valkey-sentinel-default.conf
@@ -0,0 +1,10 @@
+daemonize no
+bind 0.0.0.0
+logfile ""
+dir "/data/sentinel"
+
+sentinel resolve-hostnames yes
+sentinel announce-hostnames yes
+sentinel down-after-milliseconds drycc 60000
+sentinel failover-timeout drycc 180000
+sentinel parallel-syncs drycc 1
diff --git a/rootfs/scripts/init.sh b/rootfs/scripts/init.sh
new file mode 100755
index 0000000..e9ee373
--- /dev/null
+++ b/rootfs/scripts/init.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# Constants
+RESET='\033[0m'
+RED='\033[38;5;1m'
+MAGENTA='\033[38;5;5m'
+CYAN='\033[38;5;6m'
+
+log() {
+    # 'is_boolean_yes' is defined in libvalidations.sh, but depends on this file so we cannot source it
+    local bool="${DRYCC_QUIET:-false}"
+    # comparison is performed without regard to the case of alphabetic characters
+    shopt -s nocasematch
+    if ! [[ "$bool" = 1 || "$bool" =~ ^(yes|true)$ ]]; then
+        printf "%b\\n" "${CYAN}${MODULE:-} ${MAGENTA}$(date "+%T.%2N ")${RESET}${*}" >&2
+    fi
+}
+
+error() {
+    log "${RED}ERROR${RESET} ==> ${*}"
+    exit 1
+}
+
+########################
+# Retries a command a given number of times
+# Arguments:
+#   $1 - cmd (as a string)
+#   $2 - max retries. Default: 12
+#   $3 - sleep between retries (in seconds). Default: 5
+# Returns:
+#   Boolean
+#########################
+retry_while() {
+    local cmd="${1:?cmd is missing}"
+    local retries="${2:-12}"
+    local sleep_time="${3:-5}"
+    local return_value=1
+
+    read -r -a command <<<"$cmd"
+    for ((i = 1; i <= retries; i += 1)); do
+        "${command[@]}" && return_value=0 && break
+        sleep "$sleep_time"
+    done
+    return $return_value
+}
+
+init_default_env() {
+    if [[ -z "$DRYCC_VALKEY_SENTINEL" ]]; then
+        error "DRYCC_VALKEY_SENTINEL cannot be empty"
+    fi
+    if [[ -z "$DRYCC_VALKEY_PASSWORD" ]]; then
+        error "DRYCC_VALKEY_PASSWORD cannot be empty"
+    fi
+    mkdir -p /data/{server,sentinel}
+    SERVER_PORT=${SERVER_PORT:-6379}
+    SENTINEL_PORT=${SENTINEL_PORT:-26379}
+    REDISCLI_AUTH="$DRYCC_VALKEY_PASSWORD"
+    export SERVER_PORT SENTINEL_PORT REDISCLI_AUTH
+}
+
+init_default_env
diff --git a/rootfs/scripts/ping_liveness_local.sh b/rootfs/scripts/ping_liveness_local.sh
new file mode 100755
index 0000000..baf72d2
--- /dev/null
+++ b/rootfs/scripts/ping_liveness_local.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# shellcheck disable=SC1091
+. /scripts/init.sh
+
+response=$(
+    timeout -s 3 "$1" \
+    valkey-cli \
+    -h localhost \
+    -p "${SERVER_PORT}" \
+    ping
+)
+if [ "$?" -eq "124" ]; then
+    echo "Timed out"
+    exit 1
+fi
+responseFirstWord=$(echo "$response" | head -n1 | awk '{print $1;}')
+if [ "$response" != "PONG" ] && [ "$responseFirstWord" != "LOADING" ] && [ "$responseFirstWord" != "MASTERDOWN" ]; then
+    echo "$response"
+    exit 1
+fi
\ No newline at end of file
diff --git a/rootfs/scripts/ping_readiness_local.sh b/rootfs/scripts/ping_readiness_local.sh
new file mode 100755
index 0000000..df10bac
--- /dev/null
+++ b/rootfs/scripts/ping_readiness_local.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# shellcheck disable=SC1091
+. /scripts/init.sh
+
+response=$(
+    timeout -s 3 "$1" \
+    valkey-cli \
+    -h localhost \
+    -p "${SERVER_PORT}" \
+    ping
+)
+if [ "$?" -eq "124" ]; then
+    echo "Timed out"
+    exit 1
+fi
+if [ "$response" != "PONG" ]; then
+    echo "$response"
+    exit 1
+fi
\ No newline at end of file
diff --git a/rootfs/scripts/ping_sentinel.sh b/rootfs/scripts/ping_sentinel.sh
new file mode 100755
index 0000000..159f4c8
--- /dev/null
+++ b/rootfs/scripts/ping_sentinel.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# shellcheck disable=SC1091
+. /scripts/init.sh
+
+response=$(
+    timeout -s 3 "$1" \
+    valkey-cli \
+    -h localhost \
+    -p "${SENTINEL_PORT}" \
+    ping
+)
+if [ "$?" -eq "124" ]; then
+    echo "Timed out"
+    exit 1
+fi
+if [ "$response" != "PONG" ]; then
+    echo "$response"
+    exit 1
+fi
diff --git a/rootfs/scripts/prestop-sentinel.sh b/rootfs/scripts/prestop-sentinel.sh
new file mode 100755
index 0000000..0bb158b
--- /dev/null
+++ b/rootfs/scripts/prestop-sentinel.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# shellcheck disable=SC1091
+. /scripts/init.sh
+
+run_sentinel_command() {
+    valkey-cli -h "$DRYCC_VALKEY_SENTINEL" -p "${SENTINEL_PORT}" sentinel "$@"
+}
+
+failover_finished() {
+    # shellcheck disable=SC2207
+    SENTINEL_INFO=($(run_sentinel_command get-master-addr-by-name drycc))
+    VALKEY_MASTER_HOST="${SENTINEL_INFO[0]}"
+    [[ "$VALKEY_MASTER_HOST" != "$(hostname -I | xargs)" ]]
+}
+
+if ! failover_finished; then
+    echo "I am the master pod and you are stopping me. Starting sentinel failover"
+    # if I am the master, issue a command to failover once and then wait for the failover to finish
+    run_sentinel_command failover drycc
+    if retry_while "failover_finished" 30 1; then
+        echo "Master has been successfuly failed over to a different pod."
+        exit 0
+    else
+        echo "Master failover failed"
+        exit 1
+    fi
+else
+    exit 0
+fi
\ No newline at end of file
diff --git a/rootfs/scripts/prestop-valkey.sh b/rootfs/scripts/prestop-valkey.sh
new file mode 100755
index 0000000..275cee6
--- /dev/null
+++ b/rootfs/scripts/prestop-valkey.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# shellcheck disable=SC1091
+. /scripts/init.sh
+
+run_valkey_command() {
+    valkey-cli -h localhost -p "${SERVER_PORT}" "$@"
+}
+
+failover_finished() {
+    VALKEY_ROLE=$(run_valkey_command role | head -1)
+    [[ "$VALKEY_ROLE" != "master" ]]
+}
+
+if ! failover_finished; then
+    echo "Waiting for sentinel to run failover for up to {{ sub .Values.sentinel.terminationGracePeriodSeconds 10 }}s"
+    retry_while "failover_finished" 30 1
+else
+    exit 0
+fi
diff --git a/versioning.mk b/versioning.mk
index fd4a488..10e3d7b 100644
--- a/versioning.mk
+++ b/versioning.mk
@@ -10,13 +10,13 @@ info:
 	@echo "Immutable tag:   ${IMAGE}"
 	@echo "Mutable tag:     ${MUTABLE_IMAGE}"
 
-.PHONY: docker-push
-docker-push: docker-immutable-push docker-mutable-push
+.PHONY: podman-push
+podman-push: podman-immutable-push podman-mutable-push
 
-.PHONY: docker-immutable-push
-docker-immutable-push:
-	docker push ${IMAGE}
+.PHONY: podman-immutable-push
+podman-immutable-push:
+	podman push ${IMAGE}
 
-.PHONY: docker-mutable-push
-docker-mutable-push:
-	docker push ${MUTABLE_IMAGE}
+.PHONY: podman-mutable-push
+podman-mutable-push:
+	podman push ${MUTABLE_IMAGE}
