Skip to content

Dockerfile CMD runs only gunicorn (no migrate/collectstatic) and hardcodes gunicorn tuning also declared in compose, so the image is not self-sufficient outside the exact compose file #298

Description

@erskingardner

Summary

The built image's CMD runs gunicorn only. Database migrations and collectstatic live exclusively in the compose service command. Any run of the image outside that one compose file boots gunicorn against an unmigrated database with no collected static — a 500 on first ORM query under DEBUG=False. Separately, the gunicorn worker/timeout tuning is hardcoded in the Dockerfile and re-declared in compose, so the two can silently drift.

Code

Dockerfile:33-35:

CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", \
     "--workers", "3", "--threads", "4", "--timeout", "300", \
     "--max-requests", "500", "--max-requests-jitter", "50"]

docker-compose.yml web service command is where python manage.py migrate … && … collectstatic … && exec gunicorn … lives — so the image alone never migrates or collects static, and it re-declares the same gunicorn flags.

Concrete failure scenario

  • docker run <image>, a Kubernetes/Nomad Deployment, docker compose run web, or a future second compose target boots gunicorn against an unmigrated DB and with no collected static. Under DEBUG=False (the production settings path) the first request that touches the ORM 500s, and static assets are missing.
  • The worker tuning (--workers 3 --threads 4 --timeout 300 --max-requests 500) exists in two places with identical defaults; updating one and not the other ships stale tuning. The Dockerfile comment documents the streaming-export capacity model those numbers encode, so a drift here silently violates that model.

Why not a duplicate

#225 is that CI never builds or runs the image (so CMD/command/healthcheck regressions ship unverified). #218 is base-image/uv pinning for reproducibility. #202 is the (already-fixed-here) gunicorn 30s default timeout. Neither covers the CMD-vs-compose-command behavioral divergence or the image not being self-sufficient (no migrate/collectstatic in its own entrypoint).

Suggested fix

Give the image a self-sufficient entrypoint that runs migrate (and collectstatic where appropriate) before exec gunicorn, and single-source the gunicorn tuning (an entrypoint script or env-driven flags) so it isn't duplicated between Dockerfile and compose. At minimum, document that the image must not be run without the compose command.

Severity: LOW — operational/reproducibility gap; no data-integrity impact.

Metadata

Metadata

Assignees

No one assigned

    Labels

    LOWSeverity: minor correctness, polish, or maintainability issue

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions