Troubleshooting
Redis & caching

Fix Redis "max number of clients reached" With Django

Fix the Redis ERR max number of clients reached error in Django and Celery: count connections per process, cap connection pools, set timeouts for idle clients, and raise maxclients and file limits safely.

Problem statement

Cache reads, session lookups, or Celery task submissions fail intermittently:

redis.exceptions.ConnectionError: max number of clients reached

Redis refuses new connections once it holds maxclients (10,000 by default, or fewer if the process file limit is lower). A Django stack rarely needs anywhere near that many. Hitting the limit almost always means something opens connections and never closes them: a connection pool per request, unbounded pools across many processes, or idle clients that Redis never times out. Raising the limit hides the leak until it grows past the new number.

How Redis runs out of clientsMany processesweb, workers, beatEach opens poolscache, broker, resultsIdle never closedtimeout 0 defaultLimit reachednew clients refusedCount connections per client type before raising maxclients.
  1. Run redis-cli INFO clients and redis-cli CLIENT LIST to see how many connections exist and who owns them.
  2. Never create a redis.Redis() client per request; create one module-level client or use Django's cache.
  3. Cap pools with max_connections in CACHES options and CELERY_BROKER_POOL_LIMIT.
  4. Set timeout 300 in redis.conf so idle clients are closed.
  5. Only then raise maxclients, together with the LimitNOFILE of the Redis service.

Step-by-step solution

1. Count and attribute connections

redis-cli INFO clients
redis-cli CLIENT LIST | awk '{print $2}' | cut -d= -f2 | cut -d: -f1 | sort | uniq -c | sort -rn

The second command groups connections by source IP. Then look at idle time:

redis-cli CLIENT LIST | grep -o 'idle=[0-9]*' | sort -t= -k2 -n | tail

Thousands of connections with idle times of hours point to a leak. Name your clients to make this easier: django-redis and redis-py accept client_name (or set it with CLIENT SETNAME), so CLIENT LIST shows name=web or name=celery.

2. Stop creating clients per request

This pattern leaks a pool on every call:

def view(request):
    r = redis.Redis.from_url(settings.REDIS_URL)   # new pool every request
    r.incr("hits")

Each Redis() creates its own connection pool. Create the client once at module level, or use Django's cache API which reuses one pool per process:

# core/redis.py
import redis
from django.conf import settings

client = redis.Redis.from_url(settings.REDIS_URL, max_connections=20, health_check_interval=30)

3. Cap pool sizes per process

Total connections are roughly processes × pools × pool size. Make every term finite:

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": env("REDIS_CACHE_URL"),
        "OPTIONS": {
            "CONNECTION_POOL_KWARGS": {"max_connections": 20},
            "SOCKET_CONNECT_TIMEOUT": 2,
            "SOCKET_TIMEOUT": 2,
        },
    }
}
CELERY_BROKER_POOL_LIMIT = 10
CELERY_REDIS_MAX_CONNECTIONS = 20

With Django's built-in django.core.cache.backends.redis.RedisCache, pass max_connections in OPTIONS directly. Remember that Gunicorn with threads or gevent can use many connections per process, so size the pool to the concurrency inside each process.

connections budget, capped pools (illustrative)Gunicorn 4 x 20 pool80Celery workers 2 x 3060Beat + Flower10Channels layer40Total, all capped190 of 10,0000150300Capped pools keep the total small and predictabledjango-deployment.com
If the real count is far above your budget, look for a pool created per request.

4. Close idle clients on the server

Redis keeps idle connections forever by default (timeout 0). Set a timeout so leaked clients are reclaimed, and keep TCP keepalive on so dead peers are detected:

# /etc/redis/redis.conf
timeout 300
tcp-keepalive 60

Apply with redis-cli CONFIG SET timeout 300 for an immediate effect, and persist it in the config file. Clients with health_check_interval reconnect transparently after an idle close.

5. Raise limits only after fixing the cause

If the stack legitimately needs more connections, raise both Redis and OS limits together:

# redis.conf
maxclients 20000
# systemctl edit redis-server
[Service]
LimitNOFILE=65535

Redis logs a warning at startup if the file limit is too low for maxclients and silently lowers it. On managed Redis, the client limit is tied to the plan size.

fix the leak before raising the ceiling1Attribute connectionsCLIENT LIST by address and name2Remove per-request clientsone client per process3Cap every poolcache, broker, results, channels4Set server timeoutidle clients closed after 300 s5Raise maxclients if still neededwith LimitNOFILEA higher limit on a leaking app only delays the next outagedjango-deployment.com
Raising maxclients first hides the leak and moves the failure to a worse moment.

Verification check: watch connected_clients from INFO clients for a full day. It should rise with traffic and fall back, staying near your calculated budget instead of climbing steadily.

Explanation

Every Redis connection is a file descriptor in the Redis process and a small buffer in memory. Clients in Python reuse connections through pools, but a pool is per client object and per process. Prefork servers multiply this by the number of processes, and code that creates new client objects multiplies it again. Because Redis never closes idle connections unless told to, the count only ever grows until the hard limit is reached, which is why the error often appears days after a deploy rather than immediately.

Edge cases / notes

  • Celery result backend. Each AsyncResult.get() can hold connections, and Redis pub/sub for results keeps one per waiting client. Avoid waiting on results in web requests.
  • Channels. channels_redis opens connections per consumer group; cap them with the layer's pool options.
  • Serverless or autoscaling. Hundreds of short-lived instances each with a pool can exceed limits during scale-up. Use small pools and a server timeout.
  • Proxy. For very large fleets, a proxy such as Twemproxy or Envoy in front of Redis multiplexes many client connections.

Next steps

FAQ

What is the default maxclients in Redis?

10,000, reduced automatically if the process's open file limit is lower.

Is it safe to set timeout in redis.conf?

Yes for clients with pools and health checks; they reconnect. Avoid it for long-lived pub/sub clients that do not reconnect.

How do I see which app opens the connections?

Set a client name in each app's Redis options and group CLIENT LIST output by name.

Does Django close Redis connections after each request?

The cache backend reuses pooled connections across requests, which is good. The leak comes from creating new clients or pools.

Keep going