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.

Where the amqp default comes fromCelery app startsno broker givenReads settingswrong name or noneFalls backamqp://guest@localConnection refusedno RabbitMQ thereamqp://guest is the default, so the error means your broker setting was never read.
  1. In celery.py, call app.config_from_object("django.conf:settings", namespace="CELERY") after setting DJANGO_SETTINGS_MODULE.
  2. With that namespace, the setting must be named CELERY_BROKER_URL, not BROKER_URL or broker_url.
  3. Make sure config/__init__.py imports the Celery app so the web process uses the same configuration.
  4. Give the worker, Beat, and web processes the same environment, including the variable that holds the Redis URL.
  5. Check with celery -A config inspect ping and look at the transport: 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.

the name must match how celery.py loads settingsName Celery readsNames it ignoresnamespace="CELERY"โœ“CELERY_BROKER_URL!BROKER_URL, broker_urlno namespaceโœ“broker_url!CELERY_BROKER_URLold Celery 3 style!BROKER_URL (deprecated)!removed in Celery 6A name Celery ignores silently leaves the amqp default in placedjango-deployment.com
Pick one style and use it for every Celery option, not only the broker.

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 worker and beat services need the same env_file: as web.
  • 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
every process must read the same broker URLcelery.py calls config_from_objectnamespace="CELERY"Setting named CELERY_BROKER_URLmatches the namespaceProject __init__.py imports the appweb process uses it tooWorker and Beat share the web env filesystemd, Compose, k8sEmpty default for the broker URLsilently means amqpThe banner transport line confirms which URL the worker really usesdjango-deployment.com
Make a missing broker URL crash at startup instead of quietly falling back.

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_URl or CELERY_BROKERURL is just an unknown Django setting. Print app.conf.broker_url to 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

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.

Keep going