Execute code when Django starts ONCE only?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Django gives you startup hooks, but "run once when Django starts" is trickier than it sounds. Development auto-reload, multiple worker processes, management commands, and container restarts can all cause initialization code to run more than once, so the real solution is usually a combination of the right hook and idempotent logic.
Use AppConfig.ready for app initialization, not one-time global jobs
The standard Django hook is AppConfig.ready. It runs after Django loads installed apps, which makes it a good place to register signals or wire lightweight initialization.
This is the correct place for setup that is safe to run more than once in a process. It is not a guarantee of exactly one execution across the whole deployment.
Why ready is not truly "once"
During development, Django's auto-reloader can import the app twice. In production, a process manager such as Gunicorn or uWSGI can start multiple worker processes, and each process may execute the startup hook. Management commands can also load Django and trigger the same code path.
That means code with side effects such as sending emails, seeding data blindly, or starting background threads is risky inside ready unless you protect it carefully.
Make startup logic idempotent
If the work must happen at application boot time, design it so repeated execution is harmless. Database-backed guards are often the simplest solution.
This pattern is useful when the job is tied to shared state and must not run multiple times across workers.
Prefer management commands or migrations for one-off setup
If the code is truly a one-time task, it often should not run at server startup at all. Use a data migration, deployment step, or management command instead.
This is easier to reason about than hidden startup behavior. Operators can run it intentionally during deployment, and logs show when it happened.
Use process startup only for process-local concerns
Some tasks really do belong at startup, but only when they are local to a process. Examples include signal registration, connecting metrics hooks, or initializing in-memory caches that are safe per worker.
This process-local guard prevents duplicate initialization within one process, but it does not coordinate across processes or machines.
Avoid starting unmanaged background threads inside Django startup
A frequent anti-pattern is starting a scheduler, consumer, or infinite loop from ready. That tends to break under reloads, scale-out deployments, and process restarts. If you need recurring work, use Celery beat, a cron job, or your platform scheduler. If you need long-running consumers, run them as separate processes.
Django startup hooks are for application wiring, not for embedding your whole job system into the web process.
Common Pitfalls
- Treating
AppConfig.readyas a hard guarantee that code runs exactly once in the entire deployment. - Performing destructive or non-idempotent side effects during startup.
- Starting background threads or schedulers inside Django web workers.
- Forgetting that management commands also load Django and may trigger startup hooks.
- Hiding deployment tasks in startup code instead of using migrations or explicit commands.
Summary
- Use
AppConfig.readyfor lightweight app initialization. - Assume startup hooks may run multiple times across reloads and worker processes.
- Make any startup side effect idempotent if repetition is possible.
- Prefer migrations or management commands for true one-time tasks.
- Keep long-running jobs outside the Django web process.

