A rolling deploy starts, every new pod opens its connection pool at once, and within seconds the application logs fill with ERR max number of clients reached. In cluster mode the string is ERR max number of clients + cluster connections reached, and TLS clients often see only a dropped connection. CPU and memory on the Valkey node look normal, yet nothing new can connect, including your own valkey-cli. This is a connection storm. It needs a different runbook from a latency spike or a memory incident. This guide covers Valkey 9.1 and 9.0: reading INFO clients and CLIENT LIST, maxclients and the file-descriptor limit, the 9.0 CLIENT KILL filters, client eviction and output buffer limits, and the reserved admin connections in the 9.2 release candidate.
- Confirm the storm with
rejected_connectionsinINFO statsandconnected_clientsagainstmaxclients(default 10000) inINFO clients. - Find the source with
CLIENT LIST TYPE normalgrouped by address andlib-name, then stop the source before you kill connections, or the killed clients reconnect straight away. - Valkey needs
maxclients+ 32 file descriptors. Set the service's open-files limit above that, soCONFIG SET maxclientscan be raised during an incident. - Client eviction (
maxmemory-clients) is off by default. Turn it on, and mark monitoring connections withCLIENT NO-EVICT on. - Valkey 9.2 release candidates add
priority-subnetsandmaxclients-reserved, which keep slots free for operators. They are not in any GA release yet.
Scope and review date: November 2026. This post covers Valkey 9.1.3 and 9.0.7 (the current GA patches), with 9.2.0-rc2 features labelled as release candidate. Defaults, error strings and command filters were checked against the valkey.io documentation, valkey.conf and the server source at those tags on 10 November 2026. The commands were not run in a lab, so rehearse them on staging before you use them on production.
What does a Valkey connection storm look like?
A connection storm is a burst of new connections that arrives faster than old ones close. The server stays healthy but hits maxclients and refuses everything new. Three patterns cause most storms:
- Deploy-time reconnect stampedes. During a rolling deploy, old pods still hold their pools while new pods open theirs, so the connection count can roughly double until the rollout ends.
- Pool misconfiguration. The total is instances multiplied by connections per node. The Valkey blog post on connection storms describes this as n x p: thousands of instances with a pool of 5 to 10 each can put tens of thousands of connections on one node.
- Retry storms. A node stalls briefly, and every client times out, closes its connection and opens a new one at the same moment. The same post describes the handshakes, TLS negotiation and authentication that then land together and keep the node busy.
How a rolling deploy becomes a connection storm
Follow the numbers: the retry loop in step 4 keeps the node full after the deploy ends
Break the loop first: raising maxclients or killing connections does not help while step 4 keeps feeding new attempts. Slow the source, then clean up.
Valkey writes the error and closes the socket before it creates a client. For TLS connections no handshake has happened yet, so the source notes that nothing is written and the connection just drops. Search application logs for both the error string and connection resets.
What should you run, in order, during a connection storm?
The order matters more than any single command. Confirm the cause, stop the source, then shed what is left. Raise limits last, and only if the host can support them.
Connection storm runbook for Valkey 9.0 and 9.1
Eight steps; steps 2 to 5 usually run while new clients are still being refused
-
Confirm it is a connection problem
A rising
rejected_connectionscounter means clients are being turned away atmaxclients.valkey-cli -h 10.0.4.12 INFO stats \ | grep rejected_connections -
Stop the source
Clients refusedPause the rollout or scale down the noisy service. If nothing can connect, cut that source at the network layer.
kubectl rollout pause deployment/checkout -
Find who holds the connections
Clients refusedGroup normal clients by source address and by client library.
valkey-cli CLIENT LIST TYPE normal \ | grep -o 'addr=[^:]*' | sort | uniq -c \ | sort -rn | head -
Fix the pool or retry settings
Clients refusedLower the pool size or add backoff on the service you found, and roll that change out slowly.
-
Shed leaked and idle connections
Clients refusedValkey 9.0 and later. The reply is the number of clients closed.
valkey-cli CLIENT KILL TYPE normal \ IDLE 600 NOT-USER ops -
Raise maxclients only if the host allows it
This fails if the open-files limit is too low.
valkey-cli CONFIG SET maxclients 15000 -
Check the memory side
Thousands of new clients also bring buffers.
valkey-cli INFO memory | grep mem_clients -
Persist the changes and keep the evidence
Write runtime changes to
valkey.confand save the client list for the postmortem.valkey-cli CONFIG REWRITE valkey-cli CLIENT LIST > clients-$(date +%s).txt
Main risk: killing connections in step 5 before step 2 is done gives the slots straight back to the storm. Stop the source first.
If the storm also shows up as latency on established connections, the Valkey latency spike runbook covers the event-loop side.
How do you read INFO clients and INFO stats during a storm?
INFO clients shows the current state and INFO stats shows what has already happened. Read both, twice, a few seconds apart: the change between the two reads tells you more than either one.
valkey-cli -h 10.0.4.12 INFO clients
valkey-cli -h 10.0.4.12 INFO stats | grep -E 'connections|evicted_clients|limit_disc'Interpretation: per the INFO documentation, maxclients is the upper limit for the sum of connected_clients, connected replicas and cluster_connections. connected_clients excludes replicas, so on a primary with replicas the server can be full while connected_clients is a little below maxclients.
INFO clients and INFO stats during a storm
Illustrative output, shaped like Valkey 9.1: the node is full and rejections are climbing
What to read: connected_clients at the limit plus a growing rejected_connections is a slot problem, not a memory problem. Field definitions are in the INFO documentation.
The two buffer-limit counters were added in Valkey 8.0. If client_output_buffer_limit_disconnections climbs, the server is also closing slow readers (see the buffers section below). For the alert rules that would have caught this earlier, see monitoring Valkey with Prometheus and COMMANDLOG.
How do you find which clients are holding the connections?
CLIENT LIST returns one line per connection. At 10,000 clients that is too much to read, so group it. Run it once from one session, not in a loop: the CLIENT LIST documentation lists it as O(N) and in the @dangerous ACL category.
# Connections per source IP (IPv4 addresses)
valkey-cli CLIENT LIST TYPE normal | grep -o 'addr=[^:]*' | sort | uniq -c | sort -rn | head
# Connections per client library and version
valkey-cli CLIENT LIST TYPE normal | grep -o 'lib-name=[^ ]* lib-ver=[^ ]*' | sort | uniq -c | sort -rnInterpretation: one address or one service far above the rest is your source. lib-name and lib-ver are filled in only by clients that send CLIENT SETINFO, so empty values mean the library did not report itself, not that the field is broken. The documentation's field list calls the second field lib-version, but the server prints lib-ver=.
Next, tell churn from leaks. A storm made of reconnects shows many young connections. A leak shows many idle ones.
# Connections younger than 60 seconds (churn)
valkey-cli CLIENT LIST TYPE normal | grep -o ' age=[0-9]*' | awk -F= '$2 < 60' | wc -l
# Connections idle for at least 300 seconds (leak); IDLE filter needs Valkey 9.0+
valkey-cli CLIENT LIST TYPE normal IDLE 300 | wc -lInterpretation: age and idle are in seconds. If most connections are young, the clients are reconnecting, and the fix is backoff and pool settings. If most are idle, an application is opening connections it never uses or never closes, and timeout or CLIENT KILL ... IDLE will reclaim them.
How do you shed connections safely with CLIENT KILL?
CLIENT KILL with filters closes every matching connection, and SKIPME yes (the default) spares your own. Valkey 9.0 added the filters NAME, IDLE, FLAGS, LIB-NAME, LIB-VER, DB, CAPA and IP, plus a NOT- form of each, to both CLIENT KILL and CLIENT LIST (CLIENT KILL documentation). Filters combine with a logical AND, so preview a kill with the same filters on CLIENT LIST.
# Valkey 9.0+: close idle application connections, keep the ops user
valkey-cli CLIENT KILL TYPE normal IDLE 600 NOT-USER ops
# Valkey 9.0+: close everything from one misbehaving host
valkey-cli CLIENT KILL IP 10.0.9.41Interpretation: each command prints the number of connections closed, which may be 0. A connection is never closed mid-command; its client notices on its next command. The first example is the safest default: IDLE leaves busy connections alone, and NOT-USER ops keeps your sessions.
On 8.x you have fewer filters, but USER and TYPE cover the common case if each service has its own ACL user:
# Any supported version: close every connection of one ACL user
valkey-cli CLIENT KILL USER checkout-svc
# Valkey 8.0+: close connections older than one hour (seconds)
valkey-cli CLIENT KILL TYPE normal MAXAGE 3600Interpretation: MAXAGE matches connections at least that old. The CLIENT KILL page gives the value in seconds, and the 9.1.3 source compares seconds. The CLIENT LIST page says milliseconds, so test the unit on staging before you rely on CLIENT LIST ... MAXAGE.
A pool that loses a connection usually opens a new one at once, so killing connections from a service that retries without backoff hands the slots straight back to the storm. Kill leaked idle connections, or kill after the source is paused.
How do maxclients and the file-descriptor limit interact?
maxclients defaults to 10000. Valkey also keeps 32 file descriptors for internal use, so it needs an open-files limit of at least maxclients + 32. If it cannot raise its own limit that far at startup, it lowers maxclients and logs it. The client handling documentation shows the last line of that warning:
Current maximum open files is 10000. maxclients has been reduced to 9968 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.Interpretation: after this message, INFO clients reports the reduced value, not the one in valkey.conf. Check the startup log after every restart.
Check what the running process actually has, and set the limit in the service manager. The sample unit in the Valkey repository, utils/systemd-valkey_server.service, sets LimitNOFILE=10032, exactly the default plus 32.
# What the running server has now
grep 'open files' /proc/$(pidof valkey-server)/limits
# Raise it for the service (the unit name depends on your package)
sudo systemctl edit valkey
# [Service]
# LimitNOFILE=65536
sudo systemctl restart valkeyInterpretation: setting the open-files limit well above maxclients + 32 gives you room to raise maxclients at runtime. Without that room, the runtime change fails:
valkey-cli CONFIG SET maxclients 15000
# If the open-files limit is too low, the server replies with:
# The operating system is not able to handle the specified number of clients, try with 9968Interpretation: the server tries to raise its own limit first. Raising maxclients buys time during a leak, but a retry storm fills the new slots too.
Three more settings shape what a burst of connections meets:
- Cluster bus. In cluster mode the cluster bus connections count against
maxclients. Readcluster_connectionsinstead of estimating it. - Accept rate. Since Valkey 8.0,
max-new-connections-per-cycle(default 10) andmax-new-tls-connections-per-cycle(default 1) cap how many connections are accepted per event-loop cycle. The 9.1.3 valkey.conf advises against raising them unless clients have limited pools and exponential backoff. - Listen backlog.
tcp-backlogdefaults to 511, and Linux silently cuts it tonet.core.somaxconn.
A burst of TLS connections is accepted one per cycle by default, so a short backlog turns a reconnect burst into kernel-level drops that never show up in rejected_connections.
How do output buffer limits and client eviction keep client memory bounded?
Every connection carries a query buffer and an output buffer, and one slow reader can hold megabytes. Valkey limits them per client and in total.
Output buffer limits apply per client, by class. The 9.1.3 defaults are:
# valkey.conf 9.1.3 defaults: class, hard limit, soft limit, soft seconds
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60Interpretation: a client is closed at once when it reaches the hard limit, or when it stays above the soft limit for the soft seconds without a break. Normal clients have no limit by default. A replica limit below repl-backlog-size is ignored and the backlog size is used. Each disconnect logs Client ... scheduled to be closed ASAP for overcoming of output buffer limits. and adds to client_output_buffer_limit_disconnections.
Since the CVE-2025-21605 fix (Valkey 7.2.9, 8.0.3 and 8.1.1, all released 23 April 2025, per the 8.1.1 release notes), a client that has never authenticated, on a server that requires authentication, is closed once its output buffer passes 1024 bytes. That value is a constant in the source, not a setting. The query buffer has its own limit, client-query-buffer-limit, 1gb by default in valkey.conf, even though the client handling page still calls it non-configurable.
Client eviction applies to all clients together. maxmemory-clients caps the combined memory of normal and pub/sub clients and, when the total passes it, closes the clients using the most memory first. Replica and primary connections are never evicted. It is 0 (off) by default, because valkey.conf notes a small performance cost under high load. The client handling page suggests 5% as a place to start.
# Turn on client eviction at 5% of maxmemory (runtime, no restart)
valkey-cli CONFIG SET maxmemory-clients 5%
# Cap pub/sub output buffers harder, if slow subscribers are the problem
valkey-cli CONFIG SET client-output-buffer-limit "pubsub 16mb 4mb 30"
# Watch the result
valkey-cli INFO stats | grep evicted_clientsInterpretation: a percentage is a share of maxmemory. In the 9.1.3 source, a percentage does nothing when maxmemory is 0, so use an absolute size such as 512mb on nodes without maxmemory. The source also never lets the limit fall below 128 KB. Each eviction logs Evicting client: with the client's details and adds to evicted_clients.
Exempt the connections you need during an incident, such as the monitoring agent and the operator session. Run this on the connection itself:
CLIENT NO-EVICT on
CLIENT INFOInterpretation: the flags field of CLIENT INFO now includes e, which means the client is excluded from client eviction (CLIENT NO-EVICT). It does not reserve a slot under maxclients. That needs the 9.2 settings in the next section.
What do priority subnets and reserved admin connections change in Valkey 9.2?
Up to 9.1, when maxclients is full, your valkey-cli is refused like every other client. The 9.2.0-rc1 release notes (16 September 2026) add two settings, which are still present in 9.2.0-rc2 (4 November 2026). They are release-candidate features. Check the names again in the 9.2.0 GA release notes before you put them in production config.
# 9.2 release candidate only: example from the 9.2.0-rc2 valkey.conf
maxclients 10000
priority-subnets "192.168.1.0/24 10.0.0.0/8"
maxclients-reserved 128Interpretation: the 9.2.0-rc2 valkey.conf explains this example: normal clients are capped at 9872 (maxclients minus maxclients-reserved), clients from the listed subnets can use up to 10000, and at least 128 slots stay available to them. Both settings default to off (0 and an empty string), and both can be changed with CONFIG SET.
Details from the rc2 source and release notes:
- The reservation applies only when
priority-subnetsis set, and matching is by client IP, so Unix socket connections never count as priority. - Changing
priority-subnetsat runtime reclassifies connected clients without a reconnect. - Raising
maxclients-reservedat runtime closes no one. New normal connections are refused until enough normal clients leave. - Priority clients carry the
Hflag inCLIENT LIST.INFO clientsaddsconnected_priority_clients, andINFO statsaddsrejected_priority_connections.
The same release candidate also counts watched keys and pub/sub channel and pattern names in client memory, so they now count toward maxmemory-clients. For clients that watch keys or subscribe to channels, expect tot-mem to read higher after an upgrade to 9.2.
A storm without and with client limits set
Switch panels to compare 9.1 defaults with client eviction on and 9.2 RC reserved slots
Defaults
| Behaviour | Default settings |
|---|---|
maxmemory-clients | 0, client eviction off |
| A client that stops reading | Normal class: buffer grows with no limit. Pub/sub: closed at 32mb, or above 8mb for 60 s |
| Monitoring and operator sessions | Can be evicted, and wait for a slot like any client |
New connection at maxclients | Refused for everyone, operators included |
| Counters that move | rejected_connections |
Limits set
| Behaviour | Hardened settings |
|---|---|
maxmemory-clients | 5% of maxmemory, or a fixed size |
| A client that stops reading | Largest clients closed first once total client memory passes the cap |
| Monitoring and operator sessions | CLIENT NO-EVICT on, and connect from a priority subnet (9.2 RC) |
New connection at maxclients | Normal clients refused at maxclients minus maxclients-reserved; priority subnets still admitted (9.2 RC) |
| Counters that move | rejected_connections, evicted_clients, rejected_priority_connections (9.2 RC) |
On 9.1 today: client eviction, NO-EVICT and the open-files headroom are all available now. Only the reserved slots wait for 9.2 GA.
How do you size pools and retries so the next deploy does not cause a storm?
Server settings limit the damage, but the fix is on the client side. The guidance below is our recommendation, not a Valkey default, so adapt the numbers to your fleet. Start with idle and dead connections:
# Close normal clients idle for 10 minutes (default 0, never)
timeout 600
# Keepalive probe period in seconds (default 300)
tcp-keepalive 300Interpretation: timeout is in seconds, applies to normal clients only (never to pub/sub), and is checked in steps, so a 10-second timeout can close a client after about 12 seconds. Set it longer than your pools' idle eviction time, or the server closes connections the pool expects to reuse. tcp-keepalive finds dead peers. On Linux, valkey.conf notes that closing a dead connection takes about twice the value.
Then size the pools against the limit you actually have:
- Budget the peak, not the steady state. Add up instances times connections per node for every service, then add the extra pods during a rolling deploy. Keep that peak well under
maxclients, minus replicas,cluster_connectionsand your operator sessions. - Fill pools lazily. A small minimum pool size means new pods connect as traffic arrives, not all at startup.
- Back off with jitter. Retry connection errors with exponential backoff, a cap and random jitter, so refused clients do not retry in step. The AWS Builders' Library article on timeouts, retries and backoff with jitter explains the method.
- Do not drop the pool on one timeout. The Valkey storms post describes Uber moving from go-redis v8 to v9, which removed aggressive connection reaping, and adding per-node rate limiters and circuit breakers.
- Consider multiplexing clients. The same post notes that Lettuce and Valkey GLIDE carry many requests on one connection, at the cost of head-of-line blocking behind a slow command.
- Give each service its own ACL user and a client name. Then
CLIENT LISTshows the source by name, andCLIENT KILL USERworks on 8.x.
For a worked method of pool sizing that applies to any connection-based database, see database connection pool sizing.
Frequently asked questions
What does "ERR max number of clients reached" mean in Valkey?
It means the server already has maxclients connections, counting clients, replicas and cluster bus links, and refused a new one. The default is 10000, lowered at startup if the open-files limit is below maxclients plus 32. In cluster mode the message reads "max number of clients + cluster connections reached". TLS clients usually see a dropped connection instead.
Is client eviction enabled by default in Valkey?
No. maxmemory-clients defaults to 0, which turns client eviction off, because it has a small performance cost under high load. Set it with CONFIG SET maxmemory-clients 5% or a fixed size. A percentage needs maxmemory to be set. Mark monitoring connections with CLIENT NO-EVICT on so they stay connected.
How do I keep an admin connection available when Valkey hits maxclients?
Before 9.2 there is no reserved slot. Keep the open-files limit above maxclients plus 32, so a session that is already open can raise maxclients, and block the noisy source at the network layer if nothing can connect. The 9.2 release candidates add priority-subnets and maxclients-reserved, which hold slots for listed subnets.
Which CLIENT KILL filters were added in Valkey 9.0?
Valkey 9.0 added NAME, IDLE, FLAGS, LIB-NAME, LIB-VER, DB, CAPA and IP, plus a NOT- form of every filter, such as NOT-USER and NOT-IP. Filters combine with a logical AND, and the command returns the number of clients closed. The same filters work with CLIENT LIST, so you can preview a kill first.
Key takeaways
- A storm shows as
connected_clientsatmaxclientsand a growingrejected_connections, often with normal CPU and memory. - Stop the source first, find it with grouped
CLIENT LISToutput, and only then shed connections withCLIENT KILLand its 9.0 filters. - Give the process an open-files limit well above
maxclients+ 32, and check the startup log for a reducedmaxclients. - Turn on
maxmemory-clients, review the pub/sub output buffer limit, and exempt monitoring connections withCLIENT NO-EVICT on. - Plan for
priority-subnetsandmaxclients-reserved, but treat them as 9.2 release-candidate features until 9.2.0 GA ships.
Get help with Valkey connection incidents from JusDB
JusDB's Valkey support team can help triage a live connection storm, find the services and pool settings behind it, and review your maxclients, file-descriptor, buffer and client eviction settings against your traffic.
Bring your INFO and CLIENT LIST captures, your client libraries and pool settings, and your deploy process. We can help write a connection storm runbook for your team, and your team approves every production change.