Fix Celery Connecting to amqp://guest@localhost Instead of Redis
Fix Celery workers that log Cannot connect to amqp://guest@127.0.0.1:5672 when you configured Redis: load Django settings in celery.py, use the CELERY_ namespace correctly, and pass the broker URL to every process.
Problem statement
You configured Celery to use Redis, but the worker starts and immediately complains about RabbitMQ:
[ERROR/MainProcess] consumer: Cannot connect to amqp://guest:**@127.0.0.1:5672//:
[Errno 111] Connection refused.
Trying again in 2.00 seconds... (1/100)
Or the web process hangs for a few seconds on task.delay() and then raises kombu.exceptions.OperationalError: [Errno 111] Connection refused. Nobody asked for amqp://guest@localhost. It is Celery's built-in default broker, and seeing it means Celery never read your broker setting at all. The configuration is either not loaded, loaded under the wrong name, or missing from the environment of the process that failed.
- In
celery.py, callapp.config_from_object("django.conf:settings", namespace="CELERY")after settingDJANGO_SETTINGS_MODULE. - With that namespace, the setting must be named
CELERY_BROKER_URL, notBROKER_URLorbroker_url. - Make sure
config/__init__.pyimports the Celery app so the web process uses the same configuration. - Give the worker, Beat, and web processes the same environment, including the variable that holds the Redis URL.
- Check with
celery -A config inspect pingand look at thetransport:line in the worker banner.
Step-by-step solution
1. Read the worker banner
Start a worker in the foreground and look at the banner it prints:
celery -A config worker -l info
- ** ---------- .> transport: amqp://guest:**@localhost:5672//
- ** ---------- .> results: disabled://
If transport shows amqp://guest, the problem is configuration loading. If it shows your Redis URL but connection still fails, the URL is read correctly and Redis itself is unreachable; follow Fix Redis Connection Refused in Django instead.
2. Load Django settings in celery.py
The usual layout puts the Celery app beside settings.py:
# config/celery.py
import os
from celery import Celery
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
app = Celery("config")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()
Two mistakes produce the amqp fallback here. The first is a missing config_from_object line, so Celery never looks at Django settings. The second is setting DJANGO_SETTINGS_MODULE after creating the app or pointing it at a settings module that does not define the broker.
3. Use the right setting name for the namespace
With namespace="CELERY", Celery reads every option with that prefix, upper-cased:
# settings/production.py
CELERY_BROKER_URL = env("CELERY_BROKER_URL") # redis://redis:6379/0
CELERY_RESULT_BACKEND = env("CELERY_RESULT_BACKEND", default=None)
BROKER_URL (the old Celery 3 name) and broker_url (the name without a namespace) are both ignored under this configuration. If you drop the namespace argument, you must use the lowercase new-style names instead. Mixing the two styles is the most common cause of this error.
4. Import the app in the project package
# config/__init__.py
from .celery import app as celery_app
__all__ = ("celery_app",)
Without this, shared_task functions called from Django views bind to Celery's default app, which has no configuration, so the web process tries amqp://guest even though the worker is fine. This is why the error sometimes appears only in Gunicorn logs.
5. Give every process the same environment
The broker URL usually comes from an environment variable. Each process needs it:
- systemd: the worker unit needs the same
EnvironmentFile=as the Gunicorn unit. See Fix systemd EnvironmentFile Not Loaded. - Docker Compose: the
workerandbeatservices need the sameenv_file:asweb. - Kubernetes: mount the same Secret or ConfigMap into every Deployment.
If the variable is missing and your settings use a default such as env("CELERY_BROKER_URL", default=""), an empty string also falls back to amqp. Fail loudly instead:
CELERY_BROKER_URL = env("CELERY_BROKER_URL") # raises if unset
Verification check:
celery -A config inspect ping
python manage.py shell -c "from config.celery import app; print(app.conf.broker_url)"
Both should show your Redis URL, and ping should return pong from each worker.
Explanation
Celery ships with RabbitMQ as its default transport because that was its original broker. When no broker URL is configured, kombu builds amqp://guest:guest@localhost:5672//. Nothing about that is an error until the connection is attempted, which is why the symptom looks like a network problem. The real problem is always one step earlier: the option was never set in the configuration object Celery is using.
Edge cases / notes
- Celery 5 and 6 naming. Uppercase old names were deprecated in Celery 4 and are being removed. Use
CELERY_prefixed names with the namespace, or lowercase names without it. - Settings with typos.
CELERY_BROKER_URlorCELERY_BROKERURLis just an unknown Django setting. Printapp.conf.broker_urlto see the effective value. - Redis password characters. A password with
@or/must be URL-encoded in the broker URL, otherwise parsing fails in a different way. - Tests. Test settings often set
CELERY_TASK_ALWAYS_EAGER; if a test still tries amqp, the test settings module lacks the broker entry or eager mode.
Next steps
- Build the full setup with Configure Redis and Celery for Django in Production.
- If workers connect but do nothing, use Debug Celery Not Running in Django Production.
FAQ
Do I need RabbitMQ installed for Celery?
No. Redis works as a broker. The amqp URL appears only because Celery did not read your Redis setting.
Why does only the web app show the error?
Usually because config/__init__.py does not import the Celery app, so tasks called from Django use the default, unconfigured app.
Is BROKER_URL still valid?
It is the deprecated Celery 3 name. With namespace="CELERY" it is ignored; use CELERY_BROKER_URL.
How do I see which broker a worker uses?
Look at the transport: line in the worker startup banner, or print app.conf.broker_url in a shell with production settings.