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:

  1. An import error or bad settings. ModuleNotFoundError, ImproperlyConfigured; see missing modules and empty SECRET_KEY. The variables you passed to docker run or the environment file are missing.
  2. The wrong module path in the command. gunicorn myproject.wsgi when the folder is called config, or the WORKDIR isn't where manage.py lives.
  3. No logs at all: Python buffers output. Set PYTHONUNBUFFERED=1.
  4. $PORT isn't expanded. In the JSON ("exec") form of CMD there is no shell, so "--bind", "0.0.0.0:$PORT" passes the literal text.
  5. 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).
  6. The command is something that finishes, such as python manage.py migrate or collectstatic, and not a server.
  7. 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

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

Get startedSee pricing

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.