Fix AppRegistryNotReady in Celery and Django Scripts

Fix Apps not loaded yet and settings not configured errors in Celery workers, cron scripts, and Gunicorn config: set DJANGO_SETTINGS_MODULE, call django.setup() first, and avoid early model imports.

Problem statement

A Celery worker, a standalone script run from cron, or a custom Gunicorn configuration crashes during startup with one of these:

django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.

django.core.exceptions.ImproperlyConfigured: Requested setting INSTALLED_APPS, but settings are
not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call
settings.configure() before accessing settings.

Both mean code touched Django (settings, models, or something that imports models) before Django was initialised. manage.py and wsgi.py do the initialisation for you, which is why the same code works in runserver and in Gunicorn but fails in other entry points. This guide shows the correct start-up order for each entry point and how to find the import that runs too early.

Django start-up orderSettings moduleenv or setdefaultdjango.setup()load settings, appsImport modelsregistry readyRun your codetasks, scriptsAnything that imports models before django.setup() raises AppRegistryNotReady.
  1. Set DJANGO_SETTINGS_MODULE before anything imports Django settings (in code with os.environ.setdefault, or in the service environment).
  2. In standalone scripts, call django.setup() before importing models; better, turn the script into a management command.
  3. In celery.py, set the settings module before creating the Celery app, use config_from_object("django.conf:settings", namespace="CELERY") and autodiscover_tasks(), and do not import models at module level there.
  4. Remove model imports from app __init__.py files, settings, and apps.py module level; import them inside ready() or functions.
  5. Read the traceback bottom-up to find the first project file that triggered the import.

Step-by-step solution

1. Find the triggering import

Read the traceback from the bottom up and look for the first file from your project:

File "/srv/myapp/current/config/celery.py", line 6, in <module>
    from orders.models import Order
File "/srv/myapp/current/orders/models.py", line 3, in <module>
    class Order(models.Model):
...
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
models exist only after setupSettings module readDJANGO_SETTINGS_MODULEdjango.setup()apps.populate()Models importedper installed appAppConfig.ready()signals, checksYour code runssafe to use modelsImporting a model before setup() is what raises the errordjango-deployment.com
manage.py, wsgi.py, and asgi.py all call setup for you; scripts and Celery must too.

Here config/celery.py imports a model at module level, before Django is set up.

2. Fix Celery's entry point

The standard layout:

# 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()
config/celery.py, in the right orderimport osfrom celery import Celeryos.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")app = Celery("config")app.config_from_object("django.conf:settings",namespace="CELERY")app.autodiscover_tasks()1231Before anything touches Django settings.2Reads CELERY_* settings lazily.3Imports tasks.py modules after Django is ready.No model imports at the top of this filedjango-deployment.com
Import models inside task functions if a module is imported too early.
# config/__init__.py
from .celery import app as celery_app

__all__ = ("celery_app",)
  • The settings module is set before anything reads settings.
  • config_from_object reads settings lazily.
  • autodiscover_tasks() imports each app's tasks.py after Django is set up (Celery's Django integration calls django.setup() when the worker starts).
  • No model imports in this file. Import models inside task modules, which are loaded after setup.

Start workers with the project module: celery -A config worker. If the service environment sets DJANGO_SETTINGS_MODULE, it takes precedence over setdefault, which is what you want for per-environment settings.

3. Fix standalone scripts

A script run with python scripts/cleanup.py must initialise Django itself, in this order:

# scripts/cleanup.py
import os
import sys

import django

sys.path.insert(0, "/srv/myapp/current")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
django.setup()

from orders.models import Order  # noqa: E402  (must come after setup)

Order.objects.filter(status="draft", created__lt=...).delete()
scripts must set Django up firstFails✕from shop.models import Order✕at the top of the script✕AppRegistryNotReadyWorks✓set DJANGO_SETTINGS_MODULE✓django.setup(), then import models✓or write a management commandA management command is usually the cleaner fixdjango-deployment.com
Management commands also get settings, logging, and arguments for free.

Imports of models placed above django.setup() (for example sorted to the top by an import formatter) bring the error back. A management command avoids all of this, because manage.py performs setup before running it:

# orders/management/commands/cleanup_drafts.py
from django.core.management.base import BaseCommand

from orders.models import Order


class Command(BaseCommand):
    help = "Delete stale draft orders"

    def handle(self, *args, **options):
        deleted, _ = Order.objects.filter(status="draft").delete()
        self.stdout.write(f"deleted {deleted}")

Run it from cron or a systemd timer with python manage.py cleanup_drafts.

4. Remove early model imports inside the project

Django imports each app's package, then its apps.py, then its models.py, while populating the registry. Code that imports models during that process runs too early:

# orders/__init__.py  — BAD
from .models import Order
# orders/apps.py  — BAD: module-level import
from django.apps import AppConfig
from .models import Order


class OrdersConfig(AppConfig):
    name = "orders"
# orders/apps.py  — GOOD: import inside ready()
from django.apps import AppConfig


class OrdersConfig(AppConfig):
    name = "orders"

    def ready(self):
        from . import signals  # noqa: F401

Settings files must never import models or modules that import models. Use string references instead (for example AUTH_USER_MODEL = "accounts.User", dotted paths for backends and middleware).

To refer to a model class without importing it at module level, use apps.get_model("orders", "Order") inside functions, or settings.AUTH_USER_MODEL / get_user_model() for the user model.

5. Gunicorn configuration and hooks

A gunicorn.conf.py is loaded before the WSGI application. Hooks such as post_fork or when_ready that touch Django models must call django.setup() first or, better, avoid Django entirely. Keep the configuration file to Gunicorn settings.

6. Test entry points outside Django's runner

# Celery app loads cleanly
DJANGO_SETTINGS_MODULE=config.settings.production python -c "from config.celery import app; print(app.main)"

# Worker starts and lists tasks
celery -A config inspect registered --timeout 5

# Script runs in the service environment
sudo -u myapp bash -c 'set -a; . /etc/myapp.env; set +a; cd /srv/myapp/current && .venv/bin/python scripts/cleanup.py'

Verification check:

The worker log shows celery@host ready. and lists your tasks under [tasks], and the script or command completes without an import error when run exactly as cron or systemd runs it.

Explanation

Django's app registry is filled in django.setup(): settings are loaded, each installed app is imported, and each app's models are registered. Model classes need the registry to exist when they are defined, so importing models.py before setup raises AppRegistryNotReady, and reading settings before DJANGO_SETTINGS_MODULE is known raises ImproperlyConfigured. manage.py, wsgi.py, asgi.py, and Celery's Django integration all perform setup in the right order; problems appear in entry points that do not, or when module-level imports sneak model loading in before setup.

Edge cases / notes

pytest and tests

pytest-django sets up Django before collecting tests; a conftest.py that imports models at module level can still run too early if it is loaded before the plugin configures Django. Import inside fixtures.

Circular imports that look similar

ImportError: cannot import name 'X' from partially initialized module is a circular import, not a registry problem, although both often involve models.py importing from services.py and back. Move shared code to a module that does not import models at top level.

When to automate this

Run the Celery import check and each script or command in CI with production settings. A short smoke test that starts a worker and calls inspect registered catches entry-point regressions before deploy.

Next steps

FAQ

What does "Apps aren't loaded yet" mean?

Code imported a model or used the app registry before django.setup() finished. Move the import after setup or into a function.

How do I use Django models in a standalone Python script?

Set DJANGO_SETTINGS_MODULE, call django.setup(), then import models. A management command does this for you and is usually the better choice.

Why does Celery raise AppRegistryNotReady at startup?

celery.py or a module it imports loads models at module level. Keep celery.py free of model imports and let autodiscover_tasks() load task modules.

Can I import models in an app's init.py?

No. App packages are imported before models are ready. Import models where they are used, or in AppConfig.ready().

Keep going