Redis proxy
Redis architecture overview
This filter should be configured with the type URL
type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy.
Statistics
Every configured Redis proxy filter has statistics rooted at redis.<stat_prefix>. with the following statistics:
Name |
Type |
Description |
|---|---|---|
downstream_cx_active |
Gauge |
Total active connections |
downstream_cx_protocol_error |
Counter |
Total protocol errors |
downstream_cx_rx_bytes_buffered |
Gauge |
Total received bytes currently buffered |
downstream_cx_rx_bytes_total |
Counter |
Total bytes received |
downstream_cx_total |
Counter |
Total connections |
downstream_cx_tx_bytes_buffered |
Gauge |
Total sent bytes currently buffered |
downstream_cx_tx_bytes_total |
Counter |
Total bytes sent |
downstream_cx_drain_close |
Counter |
Number of connections closed due to draining |
downstream_rq_active |
Gauge |
Total active requests |
downstream_rq_noproto |
Counter |
Data commands rejected |
downstream_rq_total |
Counter |
Total requests |
Splitter statistics
The Redis filter will gather statistics for the command splitter in the redis.<stat_prefix>.splitter. with the following statistics:
Name |
Type |
Description |
|---|---|---|
invalid_request |
Counter |
Number of requests with an incorrect number of arguments |
unsupported_command |
Counter |
Number of commands issued which are not recognized by the command splitter |
Per command statistics
The Redis filter will gather statistics for commands in the redis.<stat_prefix>.command.<command>. namespace. By default latency stats are in milliseconds and can be changed to microseconds by setting the configuration parameter latency_in_micros to true.
Name |
Type |
Description |
|---|---|---|
total |
Counter |
Number of commands |
success |
Counter |
Number of commands that were successful |
error |
Counter |
Number of commands that returned a partial or complete error response |
latency |
Histogram |
Command execution time in milliseconds (including delay faults) |
error_fault |
Counter |
Number of commands that had an error fault injected |
delay_fault |
Counter |
Number of commands that had a delay fault injected |
Runtime
The Redis proxy filter supports the following runtime settings:
- redis.drain_close_enabled
% of connections that will be drain closed if the server is draining and would otherwise attempt a drain close. Defaults to 100.
Fault Injection
The Redis filter can perform fault injection. Currently, Delay and Error faults are supported. Delay faults delay a request, and Error faults respond with an error. Moreover, errors can be delayed.
Note that the Redis filter does not check for correctness in your configuration - it is the user’s responsibility to make sure both the default and runtime percentages are correct! This is because percentages can be changed during runtime, and validating correctness at request time is expensive. If multiple faults are specified, the fault injection percentage should not exceed 100% for a given fault and Redis command combination. For example, if two faults are specified; one applying to GET at 60 %, and one applying to all commands at 50%, that is a bad configuration as GET now has 110% chance of applying a fault. This means that every request will have a fault.
If a delay is injected, the delay is additive - if the request took 400ms and a delay of 100ms is injected, then the total request latency is 500ms. Also, due to implementation of the redis protocol, a delayed request will delay everything that comes in after it, due to the proxy’s need to respect the order of commands it receives.
Note that faults must have a fault_enabled field, and are not enabled by default (if no default value
or runtime key are set).
Example configuration:
19 faults:
20 - fault_type: ERROR
21 fault_enabled:
22 default_value:
23 numerator: 10
24 denominator: HUNDRED
25 runtime_key: "bogus_key"
26 commands:
27 - GET
28 - fault_type: DELAY
29 fault_enabled:
30 default_value:
31 numerator: 10
32 denominator: HUNDRED
33 runtime_key: "bogus_key"
34 delay: 2s
This creates two faults- an error, applying only to GET commands at 10%, and a delay, applying to all commands at 10%. This means that 20% of GET commands will have a fault applied, as discussed earlier.
RESP protocol version
The Redis proxy filter speaks one RESP protocol version on the listener, configured via the protocol_version field on RedisProxy. The same value governs both downstream client connections and every routed upstream connection pool — there is no separate per-cluster RESP knob, and no implicit floor across clusters. On the upstream-routed data path, downstream and upstream speak the same RESP version (locally emitted replies — AUTH/QUIT/NOPROTO — are encoded in the downstream-negotiated version).
When protocol_version is unset or RESP2 (the default), the negotiated wire version is
RESP2: no HELLO 3 is sent upstream, and a downstream HELLO 3 is rejected with
-NOPROTO. (RESP3-aware handling — the local HELLO reply, CLIENT SETINFO /
SETNAME acceptance, and the RESP3 decoder — is always present regardless of this value.)
When protocol_version is RESP3:
Every routed upstream Redis-compatible backend must support
HELLO 3/ RESP3 (Redis 6.0+, where RESP3 was introduced). Misconfigured upstreams fail every connection’s HELLO 3 negotiation, surfaced asupstream_resp3_hello_failurecounter increments on the per-cluster scope.The upstream client sends
HELLO 3(combined withAUTHwhen static credentials or AWS IAM authentication are configured) on every new upstream connection. User requests submitted before negotiation completes are buffered and replayed in order once bothHELLOand any requiredREADONLY(for non-Primary read policies) succeed. If the upstream rejects RESP3, the connection is closed and the buffered requests fail upstream so the caller can retry on a fresh connection that will re-attempt negotiation.Downstream clients must perform an explicit
HELLO 3handshake before any data command. Any command other thanHELLO,AUTH, orQUITarriving on a connection that has not yet negotiated RESP3 is rejected with-NOPROTO— including unknown commands, which surface as-NOPROTOrather than the splitter’s usualERR unknown commandso the operator-facing error always points to “client failed to handshake” rather than masking the missing handshake.Both explicit
HELLO Nand bareHELLOare exact-matched against the listener’sprotocol_version: bareHELLOon a freshRESP3listener is rejected because the connection’s current version (default2) does not match the required3. After a successfulHELLO 3, bareHELLOreaffirms the negotiated version.
Downstream HELLO N AUTH <user> <pass> is supported with both locally configured
credentials (downstream_auth_passwords / downstream_auth_username) and an external
auth provider; the latter defers the round trip and emits the deferred HELLO Map (or
error) when the provider responds.
When a HELLO is resolved by an external auth provider, its outcome is emitted from the
filter after the deferred round trip, so only command.hello.total is incremented —
command.hello.success and command.hello.error are not. For HELLO alone, therefore,
total may exceed success + error; the authentication result stays observable through
the external auth provider’s own metrics and the downstream reply.
The HELLO reply returned to the downstream client is synthesized locally by the proxy;
it is not proxied from, and does not reflect, any upstream Redis server. Several fields therefore
carry fixed proxy-specific values rather than a backend’s: server is envoy-redis-proxy,
version is a fixed Redis-compatibility version (6.0.0) advertised for client-library
compatibility rather than the Envoy build version, id is 0, mode is standalone,
role is master, and modules is empty. Only proto is dynamic — it reflects the
negotiated version (2 or 3). Clients that key behavior off these fields (for example a
server version gate or the connection id) must not expect them to match the upstream
Redis the data commands are routed to.
The proxy does not cross-encode upstream responses between RESP2 and RESP3. Because the
listener forces the upstream-routed data path to a single RESP version, an upstream reply
is always emitted downstream in the same RESP version it arrived; no transparent reshaping
(e.g. RESP3 Map → flat RESP2 array) is attempted, which avoids structural divergence such
as ZRANGE WITHSCORES returning nested pair arrays under RESP3 vs flat arrays under
RESP2.
The one exception is cluster-scoped commands whose replies are aggregated across shards
(for example CONFIG GET and KEYS on a Redis Cluster): these are always emitted as a
flat array, even when a RESP3 upstream shard returns a Map. Aggregating shard responses into
a Map would force clients into duplicate-key handling across shards, so a stable flat array
is emitted for both RESP2 and RESP3 downstreams.
DNS lookups on redirections
As noted in the architecture overview, when Envoy sees a MOVED or ASK response containing a hostname it will not perform a DNS lookup and instead bubble up the error to the client. The following configuration example enables DNS lookups on such responses to avoid the client error and have Envoy itself perform the redirection:
11 typed_config:
12 "@type": type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy
13 stat_prefix: redis_stats
14 prefix_routes:
15 catch_all_route:
16 cluster: redis_cluster
17 settings:
18 op_timeout: 5s
19 enable_redirection: true
20 dns_cache_config:
21 name: dns_cache_for_redis
22 dns_lookup_family: V4_ONLY
23 max_hosts: 100
Upstream Redis Authentication
The Redis proxy filter supports authenticating to upstream Redis clusters. If there are multiple upstream clusters configured, they can use either the same username and password or separate ones per cluster if each credential can be linked to the relevant cluster, and the proxy filter will authenticate appropriately to them.
To use the same username and password for all upstream clusters, the top-level auth_username and auth_password in RedisProtocolOptions should be used.
To use separate credentials for each upstream cluster, then the top-level credentials field in RedisProtocolOptions should be used. The address field is used to link this credential to individual upstream endpoint in load_assignment.endpoints.lb_endpoints.endpoint. The values for the address in both locations should be the same. Only socket addresses are supported in this mode.
19 clusters:
20 - name: redis_cluster
21 connect_timeout: 1s
22 type: STRICT_DNS
23 load_assignment:
24 cluster_name: redis_cluster
25 endpoints:
26 - lb_endpoints:
27 - endpoint:
28 address:
29 socket_address:
30 address: endpoint_1
31 port_value: 6380
32 - endpoint:
33 address:
34 socket_address:
35 address: endpoint_2
36 port_value: 6381
37 - endpoint:
38 address:
39 socket_address:
40 address: endpoint_3
41 port_value: 6382
42 typed_extension_protocol_options:
43 envoy.filters.network.redis_proxy:
44 "@type": type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProtocolOptions
45 auth_username:
46 inline_string: default_username
47 auth_password:
48 inline_string: default_password
49 credentials:
50 - address:
51 socket_address:
52 address: endpoint_1
53 port_value: 6380
54 auth_username:
55 inline_string: endpoint_1_username
56 auth_password:
57 inline_string: endpoint_1_password
58 - address:
59 socket_address:
60 address: endpoint_2
61 port_value: 6381
62 auth_username:
63 inline_string: endpoint_2_username
64 auth_password:
65 inline_string: endpoint_2_password
66 - address:
67 socket_address:
68 address: endpoint_3
69 port_value: 6382
70 auth_username:
71 inline_string: endpoint_3_username
72 auth_password:
73 inline_string: endpoint_3_password
AWS IAM Authentication
The redis proxy filter supports authentication with AWS IAM credentials, to ElastiCache and MemoryDB instances. To configure AWS IAM Authentication, additional fields are provided in the cluster Redis settings. If region is not specified, the region will be deduced using the region provider chain as described in Regions. cache_name is required and is set to the name of your cache. Both auth_username and cache_name are used when calculating the IAM authentication token. auth_password is not used in AWS IAM configuration and the password value is automatically calculated by Envoy. In your upstream cluster, the auth_username field must be configured with the user that has been added to your cache, as per Setup. Different upstreams may use different usernames and different cache names, credentials will be generated correctly based on the cluster the traffic is destined to. The service_name should be elasticache for an Amazon ElastiCache cache in valkey or Redis OSS mode, or memorydb for an Amazon MemoryDB cluster. The service_name matches the service which is added to the IAM Policy for the associated IAM principal being used to make the connection. For example, service_name: memorydb matches an AWS IAM Policy containing the Action memorydb:Connect, and that policy must be attached to the IAM principal being used by Envoy.
8 filter_chains:
9 - filters:
10 - name: envoy.filters.network.redis_proxy
11 typed_config:
12 "@type": type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy
13 stat_prefix: egress_redis
14 settings:
15 op_timeout: 5s
16 prefix_routes:
17 catch_all_route:
18 cluster: redis_cluster
19 clusters:
20 - name: redis_cluster
21 connect_timeout: 1s
22 type: STRICT_DNS
23 load_assignment:
24 cluster_name: redis_cluster
25 endpoints:
26 - lb_endpoints:
27 - endpoint:
28 address:
29 socket_address:
30 address: testcache-7dh4z9.serverless.apse2.cache.amazonaws.com
31 port_value: 6379
32 typed_extension_protocol_options:
33 envoy.filters.network.redis_proxy:
34 "@type": type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProtocolOptions
35 auth_username:
36 inline_string: test
37 aws_iam:
38 region: ap-southeast-2
39 service_name: elasticache
40 cache_name: testcache
41 expiration_time: 900s