Guides / Django Docker container exits immediately (exit code 1, 3 or 127)
Django Docker container exits immediately (exit code 1, 3 or 127)
You built the image, ran docker run, and the container exits straight away (Exited (1), Exited (3) or Exited (127)). A container lives as long as its main process. If gunicorn crashes at startup, or the command isn't found, there is nothing left running. The logs tell you which.
What it means
Docker runs one command (CMD or ENTRYPOINT). When it ends, the container stops. For a web app that command must keep running in the foreground. The exit code points at the cause: 1 is a crash in your app, 3 is gunicorn failing to boot a worker, 127 is "command not found", and 137 is the kernel killing it for memory.
Why it happens
Start with docker logs <container>. Then match:
- An import error or bad settings.
ModuleNotFoundError,ImproperlyConfigured; see missing modules and empty SECRET_KEY. The variables you passed todocker runor the environment file are missing. - The wrong module path in the command.
gunicorn myproject.wsgiwhen the folder is calledconfig, or theWORKDIRisn't wheremanage.pylives. - No logs at all: Python buffers output. Set
PYTHONUNBUFFERED=1. $PORTisn't expanded. In the JSON ("exec") form ofCMDthere is no shell, so"--bind", "0.0.0.0:$PORT"passes the literal text.- Binding to
127.0.0.1. The container stays up, but nothing outside can reach it (a different problem, with the same "it doesn't work" feeling). - The command is something that finishes, such as
python manage.py migrateorcollectstatic, and not a server. - Permissions: the non-root user can't write to a folder the app needs.
The fix
A reliable starting point for a Django app:
FROM python:3.12-slim
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN SECRET_KEY=build-only python manage.py collectstatic --noinput
RUN useradd --create-home app && chown -R app /app
USER app
EXPOSE 8000
CMD ["sh", "-c", "exec gunicorn myproject.wsgi:application --bind 0.0.0.0:${PORT:-8000} --workers 3"]
Notes: sh -c "exec ..." expands ${PORT} and still lets gunicorn receive stop signals as PID 1. Change myproject to your project folder. Don't run migrations in this command if you have more than one instance (unapplied migrations).
To debug, run the container with a shell and run the command by hand:
docker run --rm -it --env-file .env.production myimage sh
# inside:
python manage.py check --deploy
gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
Add a .dockerignore so the build context stays small and secrets stay out of the image:
.git
.env*
__pycache__/
*.sqlite3
node_modules/
.venv/
Making it stick in production
- Keep the container stateless: no database file, uploads or sessions on its disk.
- Pass secrets as environment variables at runtime, not as
ENVlines in the Dockerfile. - Add a health endpoint and a restart policy so a crash is retried: health check failing.
- Build once in CI, test the image, and deploy that exact image.
Or skip it
With Django Cloud you don't need a Dockerfile: it builds your Django project from your requirements.txt, or pyproject.toml and uv.lock, and deploys it with djangocloud deploy.
pip install djangocloud-cli
djangocloud deploy
Free plans are available for students, AWS Activate startups, open-source, non-profit and research projects. See how Django Cloud compares with other ways to host Django.
Django is a registered trademark of the Django Software Foundation. Django Cloud is not affiliated with or endorsed by it. Found a mistake in this guide? Tell us and we will fix it.