Incident Response

Redis "ERR max number of clients reached": Causes and Fixes

Redis is up, memory is fine, and new connections bounce with "max number of clients reached". The limit is rarely the real problem. Usually something is leaking connections, or the OS quietly lowered maxclients at startup. Here is how to tell which and fix it.

· 14 min read · AG Data Team

The Redis error "ERR max number of clients reached" means the server has as many open connections as maxclients allows, so it is turning new ones away. Commands on existing connections keep working, and memory and CPU may look completely normal. It is a capacity limit, but in most incidents the limit is not what needs fixing. Something is holding far more connections than it should, or the operating system capped Redis lower than you think.

What exactly happens at the limit

The check is in networking.c, and it runs when a connection is accepted, before a client object is even created. If the number of clients plus cluster bus connections is at or above maxclients, Redis writes the error, increments a counter and closes the socket. The client sees:

ERR max number of clients reached

A few details from that code matter in practice:

Valkey has the same check and the same message. Valkey 9.2, a release candidate as of this writing, adds maxclients-reserved and priority-subnets to its configuration. They keep a share of the limit for connections from listed admin or monitoring networks, so operators are not locked out. Redis itself has no equivalent setting.

First check: what is maxclients really?

The default is 10000. The Redis client handling docs explain the catch: Redis needs maxclients plus 32 file descriptors (32 are reserved for internal use). At startup it checks its open files limit and, if that is too low, tries to raise it itself. The code in server.c steps down until the OS accepts a value. If it can't get enough, Redis lowers maxclients to fit. It doesn't refuse to start. It logs a warning and carries on with the smaller limit.

So if the process's hard limit is 1024 open files, 10000 quietly becomes 992 without anyone changing the config. Check what the running process actually has:

# the real limit of the running process
grep 'open files' /proc/$(pidof redis-server)/limits

# what Redis decided at startup
grep -i 'maxclients\|open files' /var/log/redis/redis-server.log

The warnings to look for say "maxclients has been reduced to N to compensate for low ulimit", or "Unable to obtain the current NOFILE limit" when Redis couldn't read it and assumed 1024. If Redis runs under systemd, the ulimit in your shell does not apply to the service. The limit comes from the unit's LimitNOFILE= setting.

You can change maxclients at runtime with CONFIG SET. The config code re-runs the file limit adjustment first, and if the OS can't support the new value, the command fails with "The operating system is not able to handle the specified number of clients, try with N". So CONFIG SET won't silently go past the OS limit.

Second check: who is holding the connections

Start with the numbers. If you can't get in because all slots are taken, run these from a host whose connection is already open, use a monitoring agent's session, or free a few slots first (see below).

redis-cli INFO clients
# connected_clients:9968
# cluster_connections:0
# maxclients:10000
# blocked_clients:12

redis-cli INFO stats | grep -E 'rejected_connections|total_connections_received'

A rejected_connections counter that keeps climbing confirms the limit is being hit right now. Then group the connections. CLIENT LIST prints one line per connection with fields like addr, name, age, idle, flags, cmd and, from Redis 7.2, lib-name and lib-ver:

# connections per client IP
redis-cli CLIENT LIST | grep -o 'addr=[^:]*' | sort | uniq -c | sort -rn | head

# connections per client name / library (if clients set them)
redis-cli CLIENT LIST | grep -o 'name=[^ ]*' | sort | uniq -c | sort -rn | head
redis-cli CLIENT LIST | grep -o 'lib-name=[^ ]*' | sort | uniq -c | sort -rn | head

The patterns we tend to find:

  1. One host or service with thousands of connections. A connection leak: a client created per request and never closed, a pool with no upper bound, or a framework that opens a new pool in every worker process.
  2. Huge idle values on many connections. Connections abandoned by clients that crashed or were killed without closing them. Redis's own timeout defaults to 0, so such connections stay open until TCP keepalive notices the peer is gone. Keepalive is on by default at 300 seconds, and the config comment says it takes about twice that to close a dead connection on Linux.
  3. Everything scaled at once. An autoscaler doubled the pods and each pod opens a fixed pool. Pool size × instances is the number to compare with maxclients.
  4. Many blpop/xread or subscribers. Blocking commands and Pub/Sub keep a connection busy for as long as they wait. Each such consumer needs its own connection, so they add up quickly.

Fixes, from immediate to permanent

# free slots now: close one connection by its address
redis-cli CLIENT KILL ADDR 10.20.0.15:53122
redis-cli CLIENT KILL TYPE normal MAXAGE 86400   # Redis 7.4+, by connection age

# close idle connections automatically (seconds, 0 = never)
redis-cli CONFIG SET timeout 300

# raise the ceiling if the OS limit allows it
redis-cli CONFIG SET maxclients 20000

Free slots now. CLIENT KILL can close connections by ID, ADDR, LADDR, USER or TYPE, and since Redis 7.4 by MAXAGE. Note that MAXAGE closes clients older than the given number of seconds: it is connection age, not idle time. There is no idle filter, so to close only idle connections, take the IDs from CLIENT LIST and kill those. Restarting the leaking service often frees more slots, faster, than anything you do on the server.

Set an idle timeout. timeout closes normal connections idle for longer than the given number of seconds. According to timeout.c, it does not apply to replicas, the master link, blocked clients or Pub/Sub subscribers, so it is fairly safe to enable. The docs warn the check runs incrementally, so connections close a bit later than the exact value. Set it longer than your pools' own idle timeout, so the pool closes the connection first and doesn't hit a dead socket.

Raise the limit if the load is real. If the connection count is legitimate, raise the OS file limit first (LimitNOFILE= or ulimit -n), then maxclients in the config file and with CONFIG SET. Each connection also uses memory for its buffers. Check tot-mem in CLIENT LIST and our Redis OOM and maxmemory guide before you multiply the client count.

Don't fix a leak by raising maxclients. If one service grows its connection count without limit, a higher maxclients only buys time, and the next incident comes with more memory in use and more file descriptors open. Fix the client: one shared pool per process, a hard upper bound on pool size, and connections returned after use.

Prevent the next one

Key takeaways

Redis Valkey Connections Troubleshooting
Work with us

Redis refusing connections?

Connection exhaustion is usually a client problem showing up on the server. We trace it to the service that leaks, fix the limits properly and set up alerts so it doesn't come back.

Get in touch More from the blog