Celery Redis SSL: rediss:// URL must have parameter ssl_cert_reqs (tested)

Add ?ssl_cert_reqs=required to both the broker and backend rediss:// URLs (with &ssl_ca_certs=/abs/path/ca.crt for a private CA). If the certificate can't be verified, Celery hides the cause: after about 19 seconds it raises RuntimeError: Retry limit exceeded while trying to reconnect to the Celery result store backend.

Using Redis over TLS (rediss://, as on most managed Redis services) with Celery needs an explicit ssl_cert_reqs in the URL, and a certificate problem shows up as a misleading "Retry limit exceeded" error. Measured with Celery 5.6.3 and redis-py 8.1.0 on Python 3.13.5 against Redis 7 with TLS only and a self-signed certificate, used as both broker and result backend.

1. rediss:// without ssl_cert_reqs: ValueError

$ cat tasks.py call3.py
import os
from celery import Celery
url = os.environ["URL"]
app = Celery("tasks", broker=url, backend=url)

@app.task
def add(x, y):
    return x + y
import time
from tasks import add
t = time.time()
try:
    r = add.delay(2, 2)
    print("result:", r.get(timeout=5))
    del r
except Exception as e:
    print(f"after {time.time()-t:.0f}s:", type(e).__name__, str(e).strip())
$ URL='rediss://127.0.0.1:8966/0' python call3.py 2>&1 | tail -2
after 0s: ValueError A rediss:// URL must have parameter ssl_cert_reqs and this must be set to CERT_REQUIRED, CERT_OPTIONAL, or CERT_NONE

Celery refuses the URL immediately. Add ?ssl_cert_reqs=required (or CERT_REQUIRED; also optional / none) to both the broker and the backend URL.

2. Certificate not trusted: "Retry limit exceeded" after 19 seconds

$ URL='rediss://127.0.0.1:8966/0?ssl_cert_reqs=required' python call3.py 2>/dev/null
after 19s: RuntimeError Retry limit exceeded while trying to reconnect to the Celery result store
backend. The Celery application must be restarted.
$ cat rp.py
import redis
r = redis.Redis.from_url("rediss://127.0.0.1:8966/0?ssl_cert_reqs=required")
try:
    r.ping()
except Exception as e:
    print(type(e).__name__, str(e)[:160])
$ python rp.py
ConnectionError Error 1 connecting to 127.0.0.1:8966. [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1029).

With ssl_cert_reqs=required and a certificate the client cannot verify, Celery retries silently for about 19 seconds and then raises RuntimeError: Retry limit exceeded while trying to reconnect to the Celery result store backend. The real cause is invisible there; connecting with redis-py directly (same URL) shows it at once: CERTIFICATE_VERIFY_FAILED ... self-signed certificate. Run that two-line check whenever Celery reports retry-limit errors against a TLS Redis.

3. Working configurations

$ URL='rediss://127.0.0.1:8966/0?ssl_cert_reqs=CERT_NONE' celery -A tasks worker -n w1@example -c 1 > worker.log 2>&1 &
$ URL='rediss://127.0.0.1:8966/0?ssl_cert_reqs=CERT_NONE' python call3.py
result: 4
$ URL='rediss://127.0.0.1:8966/0?ssl_cert_reqs=required&ssl_ca_certs=/tmp/rs388/tls/redis.crt' python call3.py
result: 4

Both work. Prefer ssl_cert_reqs=required&ssl_ca_certs=/path/to/ca.crt (use an absolute path) so the connection is still verified; ssl_cert_reqs=CERT_NONE encrypts but accepts any certificate, so use it only on trusted networks. Managed services with a public CA certificate usually work with plain ssl_cert_reqs=required.

Related: Redis connection refused, Celery Error 111 connection refused, Celery logging configuration.

Frequently asked questions

How do I fix 'A rediss:// URL must have parameter ssl_cert_reqs'?

Append ?ssl_cert_reqs=required (or CERT_REQUIRED, optional, none) to the rediss:// broker and result backend URLs.

Why does Celery say Retry limit exceeded with Redis SSL?

In the test, a certificate that failed verification produced only that RuntimeError after about 19 seconds. Connecting with redis-py directly shows the real CERTIFICATE_VERIFY_FAILED error.

Is ssl_cert_reqs=CERT_NONE safe?

It still encrypts, but accepts any certificate, so it does not protect against a man in the middle. Prefer required with ssl_ca_certs pointing at your CA.

Written with AI assistance. Measured on 2026-10-23 with Celery 5.6.3, kombu 5.6.2 and redis-py 8.1.0 on Python 3.13.5, one worker, Redis 7 (Docker) with TLS only and a self-signed certificate. Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Celery and Redis are free.