Scaling Redis in Ambient Mesh with Envoy Gateway
Learn how Houzz and Tetrate enabled Redis cluster traffic in Istio Ambient Mesh using Envoy Gateway as a centralized waypoint proxy.
In a previous blog post—Use Envoy Gateway as the Unified Ingress Gateway and Waypoint Proxy for Ambient Mesh—we demonstrated how Envoy Gateway fills key Layer-7 gaps in Istio Ambient Mesh, such as global rate limiting, circuit breaking, OIDC authentication, and xDS patching.
Building on that foundation, we now explore a more advanced and stateful use case: managing Redis cluster traffic in Ambient Mesh.
As service mesh adoption expands, supporting stateful protocols like Redis—beyond traditional HTTP workloads—has become increasingly critical. In this joint post by Houzz and Tetrate, we share how we collaborated to enable Redis cluster communication within Istio Ambient Mesh using Envoy Gateway as a centralized Waypoint Proxy.
This effort originated when Houzz, an early adopter of Redis on Kubernetes, began migrating workloads from the sidecar-based Istio model to the lightweight, sidecarless Ambient mode. While Ambient greatly simplifies operations and reduces resource overhead, it lacked built-in support for Redis cluster behavior—particularly the need for topology discovery and slot-based request routing.
Together, we designed an architectural solution that uses Envoy Gateway as the Waypoint Proxy to offload Redis cluster topology awareness and request routing from applications. This allows in-mesh clients to access external Redis shards seamlessly, without requiring custom client logic or sidecar injection.
In this post, we’ll walk through the problem, the solution architecture, a step-by-step deployment guide, and the practical impact this approach has had on Redis workloads running in Houzz’s infrastructure.
Background: Redis at Houzz
Redis plays a critical role in Houzz’s infrastructure, supporting applications across multiple languages, including PHP and Node.js. Houzz operates a large-scale Redis cluster in cluster mode, running across cloud-hosted virtual machines. The cluster comprises hundreds of nodes and stores several hundred terabytes of data, much of which is persistent and must remain accessible across service updates.
This Redis cluster is deployed in a sharded configuration, where each master node owns a subset of the keyspace, defined by a range of hash slots. When a request is sent to a node that does not own the requested key’s slot, the node responds with a MOVED redirection pointing to the correct shard. Clients are expected to fetch cluster topology (via the CLUSTER SLOTS command), maintain awareness of slot ownership, and route requests accordingly.
Traditionally, Houzz applications handled this routing logic within client libraries. However, this created a high degree of coupling between application logic and cluster topology, and imposed operational burdens on teams writing and maintaining Redis clients in multiple languages.
Building Redis Cluster Support with Istio Sidecar and EnvoyFilter
Before moving to Ambient Mesh, Houzz adopted a sidecar-based pattern using Istio’s EnvoyFilter API to support Redis cluster traffic.
This approach offloaded cluster awareness and key-slot routing logic from application clients to the Envoy proxy running inside the Istio sidecar (istio-proxy). Specifically:
- A “backend” cluster was defined in Envoy to describe all known Redis nodes and allow slot-aware routing.
- A local listener exposed a loopback address (e.g.,
127.0.10.1:6379) so clients could communicate as if Redis were a single-node instance. - The Redis proxy filter inside Envoy handled
CLUSTER SLOTSlookups, computed key slot assignments, and routed traffic to the correct Redis backend.
With this setup, application developers no longer needed to account for Redis cluster topology in their code. It also enabled protocol-level optimizations, such as TCP connection reuse—particularly helpful for runtimes like PHP-FPM that manage Redis connections in a fork-per-request model.
The Challenge: Redis in Ambient Mesh Without Sidecars
Istio’s Ambient Mesh mode removes sidecar proxies from application pods, significantly reducing resource usage and simplifying operational complexity. However, when Houzz first began exploring Ambient mode, a critical limitation became apparent: Redis cluster support via EnvoyFilter was not available.
In traditional (sidecar-based) mode, custom EnvoyFilter configurations allowed deep integration with Redis cluster behavior, including topology lookups and slot-based routing. Ambient Mesh, in contrast, introduced a different architecture:
- A ztunnel process transparently captures application traffic.
- An optional Waypoint proxy handles Layer-7 logic.
- Custom Layer-7 filters like
envoy.filters.network.redis_proxywere not initially supported.
For Houzz, this meant their applications could not transparently connect to the Redis cluster in Ambient mode. Redis clients would receive MOVED responses and fail without topology awareness. This blocked broader Ambient adoption—despite its clear benefits in reducing sidecar overhead—because Redis access was foundational to many services.
The Solution: Envoy Gateway as Waypoint Proxy for Redis
To address this limitation, the joint team from Houzz and Tetrate designed an approach that leverages Envoy Gateway as a programmable Waypoint Proxy within Ambient Mesh. Envoy Gateway, which supports Redis filters and extensibility via EnvoyPatchPolicy, provides a flexible way to manage Layer-7 logic even without sidecars.
In this design:
- Ambient Mesh’s ztunnel captures outbound Redis traffic from application pods.
- The traffic is forwarded to an Envoy Gateway Waypoint, deployed in the mesh.
- The Waypoint is configured with Envoy’s Redis proxy filter, which issues
CLUSTER SLOTS, calculates key hash slots, and routes commands to the correct Redis backend. - Clients remain unaware of Redis cluster topology—they simply connect to a virtual IP and port (e.g.,
redis:7000) as if talking to a single node.
This preserves the Redis client experience across languages and frameworks, without embedding any cluster-specific logic in the application code. At the same time, it maintains performance and scales efficiently thanks to Ambient Mesh’s sidecarless model.
Deployment Walkthrough: Redis in Ambient Mesh with Envoy Gateway
This section provides a practical guide for replicating the Redis-in-Ambient setup described earlier. You’ll deploy a Redis cluster externally, enable Ambient Mesh, install Envoy Gateway as the Waypoint proxy, and verify that Redis traffic is routed correctly through the mesh.
We assume you’re starting with a fresh Kubernetes cluster. For demonstration purposes, the following steps use a local Kind cluster, but this setup is applicable to any Kubernetes environment.
Step 1: Install Istio Ambient Mesh
istioctl install --set profile=ambient
Ambient mode installs the ztunnel component, which captures and redirects traffic without sidecar injection. This enables lower resource usage per workload.
Step 2: Install Envoy Gateway
Envoy Gateway will be used as the Waypoint Proxy to route Redis cluster traffic:
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.5.4 \
--set config.envoyGateway.provider.kubernetes.deploy.type=GatewayNamespace \
--set config.envoyGateway.extensionApis.enableEnvoyPatchPolicy=true \
-n envoy-gateway-system \
--create-namespace
This installation enables support for EnvoyPatchPolicy, which is required to configure Redis proxy filters.
Step 3: Deploy a Redis Cluster
We deploy a 6-node Redis cluster in the external-redis namespace. This setup will run Redis in cluster mode and expose port 7000 for client traffic.
apiVersion: v1
kind: Namespace
metadata:
name: external-redis
---
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-cluster
namespace: external-redis
data:
update-node.sh: |
#!/bin/sh
REDIS_NODES="/data/nodes.conf"
sed -i -e "/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/${POD_IP}/" ${REDIS_NODES}
exec "$@"
redis.conf: |+
cluster-enabled yes
cluster-require-full-coverage no
cluster-node-timeout 15000
cluster-config-file /data/nodes.conf
cluster-migration-barrier 1
appendonly yes
protected-mode no
port 7000
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster
namespace: external-redis
spec:
serviceName: redis-cluster
replicas: 6
selector:
matchLabels:
app: redis-cluster
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
labels:
app: redis-cluster
spec:
containers:
- name: redis
image: redis
ports:
- containerPort: 7000
name: tcp-redis
- containerPort: 17000
name: tcp-gossip
command: ["/conf/update-node.sh", "redis-server", "/conf/redis.conf", "--cluster-announce-ip $(POD_IP)"]
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
volumeMounts:
- name: conf
mountPath: /conf
readOnly: false
volumes:
- name: conf
configMap:
name: redis-cluster
defaultMode: 0755
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: external-redis
spec:
type: ClusterIP
ports:
- port: 7000
targetPort: 7000
name: tcp-redis
selector:
app: redis-cluster
Wait for the StatefulSet to be ready:
kubectl -n external-redis rollout status --watch statefulset/redis-cluster --timeout=600s
kubectl -n external-redis wait pod --selector=app=redis-cluster --for=condition=ContainersReady=True --timeout=600s -o jsonpath='{.status.podIP}'
Initialize the Redis cluster:
kubectl exec -it redis-cluster-0 -c redis -n external-redis -- redis-cli --cluster create --cluster-yes --cluster-replicas 1 $(kubectl get pod -n external-redis -l=app=redis-cluster -o json | jq -r '.items[] | .status.podIP + ":7000"')
You should see output confirming that all 16,384 slots have been assigned and the nodes are replicating properly.
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 10.244.0.16:7000 to 10.244.0.12:7000
Adding replica 10.244.0.17:7000 to 10.244.0.13:7000
Adding replica 10.244.0.15:7000 to 10.244.0.14:7000
... omitted for brevity
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
Step 4: Deploy Redis Client Pod
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: redis-client
name: redis-client
namespace: default
spec:
selector:
matchLabels:
app: redis-client
template:
metadata:
annotations:
sidecar.istio.io/logLevel: debug
labels:
app: redis-client
spec:
containers:
- image: redis
imagePullPolicy: Always
name: redis-client
This pod will be used to validate Redis access through the mesh.
Step 5: Configure Envoy Gateway as Redis Waypoint
Create the required Gateway resources and apply the Redis proxy filter via EnvoyPatchPolicy.
Note: Replace
redis.external-rediswith your actual Redis service address if needed.
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: waypoint
namespace: envoy-gateway-system
spec:
logging:
level:
default: debug
provider:
kubernetes:
envoyDeployment:
container:
image: envoyproxy/envoy:v1.34.4
envoyService:
patch:
type: StrategicMerge
value:
spec:
ports:
- name: fake-hbone-port
port: 15008
protocol: TCP
targetPort: 15008
type: ClusterIP
type: Kubernetes
telemetry:
accessLog: {}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
labels:
istio.io/dataplane-mode: ambient
name: redis-waypoint
namespace: default
spec:
gatewayClassName: eg-waypoint
listeners:
- allowedRoutes:
namespaces:
from: All
name: redis
port: 7000
protocol: TCP
---
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg-waypoint
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parametersRef:
group: gateway.envoyproxy.io
kind: EnvoyProxy
name: waypoint
namespace: envoy-gateway-system
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
name: redis
namespace: default
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: redis-waypoint
rules:
- backendRefs:
- group: ""
kind: Service
name: redis
port: 7000
weight: 1
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
name: redis-envoy-patch-policy
namespace: default
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: redis-waypoint
type: JSONPatch
jsonPatches:
- name: default/redis-waypoint/redis
type: type.googleapis.com/envoy.config.listener.v3.Listener
operation:
op: replace
path: /filter_chains/0/filters/0
value:
name: envoy.filters.network.redis_proxy
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy
prefix_routes:
catch_all_route:
cluster: redis_cluster
settings:
enable_redirection: true
op_timeout: 5s
dns_cache_config:
name: dns_cache_for_redis
dns_lookup_family: V4_ONLY
max_hosts: 100
stat_prefix: redis_stats
- name: redis_cluster
type: type.googleapis.com/envoy.config.cluster.v3.Cluster
operation:
op: add
path: ""
value:
name: redis_cluster
connect_timeout: 10s
cluster_type:
name: envoy.clusters.redis
load_assignment:
cluster_name: redis-cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: redis.external-redis # please replace with your redis service address
port_value: 7000
Step 6: Expose Redis Service for Waypoint Interception
Create a selector-less service marked for waypoint interception. We don’t need to specify the endpoints as the request will be intercepted by ztunnel and redirected to the Envoy Gateway waypoint, then the waypoint will route the request to the correct endpoint.
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: default
labels:
istio.io/use-waypoint: redis-waypoint
spec:
ports:
- port: 7000
targetPort: 7000
name: redis-port
protocol: TCP
Step 7: Verify Redis Access from Client Pod
Open a Redis CLI session in the client pod:
kubectl exec -it `kubectl get pod -l app=redis-client -o jsonpath="{.items[0].metadata.name}"` -c redis-client -- redis-cli -h redis -p 7000
You should now be able to run standard Redis commands:
redis:7000> set foo bar
OK
redis:7000> get foo
"bar"
Check the Envoy logs in the waypoint deployment for Redis proxy activity:
kubectl logs deployments/redis-waypoint |grep redis_proxy
You should see command-level logs, such as:
[2025-08-13 09:29:25.636][43][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:886] splitting '["set", "foo", "bar"]'
[2025-08-13 09:29:28.100][43][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:886] splitting '["get", "foo"]'
Conclusion
This implementation demonstrates that Redis Cluster can be effectively integrated into Istio Ambient Mesh by leveraging Envoy Gateway as the Waypoint Proxy. Offloading cluster topology discovery and slot-based request routing to Envoy eliminates the need for Redis-aware client libraries, simplifies application code across languages, and avoids per-pod sidecar overhead. Centralizing Redis logic in the mesh layer also enables efficient connection reuse, particularly for runtimes with short-lived processes like PHP-FPM. The result is a scalable, maintainable architecture for running stateful, non-HTTP protocols in Ambient Mesh with minimal operational complexity.
Learn more about Tetrate Enterprise Gateway to see how it can help you implement Envoy Gateway in your enterprise environment.
Contact us to learn how Tetrate can help your journey. Follow us on LinkedIn for latest updates and best practices.