Deploy a Django ML API to Heroku: Settings and Verification

AI Published Updated 5 mins read By Leon Wei
Deploy a Django ML API to Heroku: Settings and Verification cover image

Quick summary

Summarize this blog with AI

Deploy the exact small Django API from Part I, keeping its input limits and explicit model version. This companion adds the release files, environment configuration, HTTPS/proxy assumptions and smoke checks you need to evaluate a Heroku deployment. It does not download an old template or provision a database the service does not use.

The packaged application, Django request checks and a Gunicorn process were executed locally with Python 3.12.14, Django 5.2.18, scikit-learn 1.8.0 and Gunicorn 26.2.0. Heroku instructions were checked against official documentation on October 17, 2026. No Heroku app was created or deployed during this revision, so this is a tested local rehearsal plus a documented cloud procedure, not a cloud execution transcript.

Use the complete Part I project as the release unit

The directory must contain sentiment.py, service.py, test_service.py and the pinned requirements.txt from Part I. Rerun the local request checks before packaging. The eight-row training fixture is embedded source data; no untrusted model deserialization or remote dataset is required. An installed real model should instead be an independently evaluated and versioned artifact with a trusted origin.

Add .python-version with the supported minor line:

3.12

Using a minor line allows the platform’s supported patch release to change. Record the actual Python patch in each build and rerun your tests when it changes; do not infer bit-for-bit equivalence from the local 3.12.14 receipt. Check current platform support before upgrading dependencies.

Add a file named Procfile, with no extension:

web: gunicorn service:application --bind 0.0.0.0:$PORT --workers 1 --timeout 30

The platform supplies PORT. One worker makes the toy model’s startup and memory use easy to inspect; choose production concurrency from measured load and memory. Each worker imports and fits this small model independently. The timeout is a worker control, not a complete cancellation or request-size policy. This JSON-only application has no static files to collect, so do not add static-serving or database packages solely because a generic template includes them.

Keep local environments and secrets out of the release:

.venv/
__pycache__/
.env
.env.*
*.pyc

Set configuration on the intended application

Create or select a disposable practice app you own, then verify its name and selected source branch before configuring or deploying it. Heroku hosting can incur charges; use current plan information rather than assuming the historical free tier exists. Set these config values through the app’s configuration interface without committing them:

NameRequired value
DEPLOYMENTheroku
PUBLIC_HOSTThe exact assigned lowercase hostname, without scheme, path or wildcard.
DJANGO_SECRET_KEYA separately generated random secret of at least 50 characters.
API_TOKENA separately generated random token of at least 32 characters; give it only to authorized clients.
DISABLE_COLLECTSTATIC1 for this JSON-only application.

The service fails startup when required secrets or the host are absent/invalid. DEBUG stays False. The Heroku mode redirects HTTP to HTTPS and trusts the router’s X-Forwarded-Proto header. That trust is appropriate only behind the documented trusted proxy path; on another host, verify that clients cannot bypass or forge the proxy’s authoritative scheme signal. Do not enable this setting blindly on a publicly reachable direct server.

The API uses a shared bearer token, not a multi-user authorization system. Plan token rotation, access logs without secrets/body text, per-client controls, abuse limits and failure monitoring before exposing a valuable or sensitive service. Adding session/cookie authentication changes the CSRF requirements. HSTS policy is deliberately left for verified domain rollout; do not enable subdomain or preload commitments without understanding their impact.

Rehearse hosted settings without contacting Heroku

With the secrets already in your terminal environment, use a local hostname for this rehearsal:

DEPLOYMENT=heroku PUBLIC_HOST=api.example.test   .venv/bin/python -m gunicorn service:application --bind 127.0.0.1:8000 --workers 1 --timeout 30

A loopback client can emulate the trusted proxy for a test only. A genuine deployment must receive the scheme signal from the actual router. Confirm that an ordinary HTTP request redirects and the emulated HTTPS request reaches the health endpoint:

curl --silent --output /dev/null --write-out '%{http_code}
' --max-time 5   -H 'Host: api.example.test' http://127.0.0.1:8000/health/
curl --fail-with-body --max-time 5 -H 'Host: api.example.test'   -H 'X-Forwarded-Proto: https' http://127.0.0.1:8000/health/

Expected output:

301
{"status": "ready", "model_version": "toy-sentiment-v1"}

Also repeat valid and invalid prediction requests with that Host and proxy header. The local rehearsal verified 200 for the expected label, 401 for a missing token, 400 for an unexpected host and the HTTP redirect. This tests configuration and WSGI startup; it does not prove a Linux build, platform router, TLS certificate, scaling behavior or external availability.

Build, release, and verify the actual deployment

  1. Commit only the reviewed project files to its own repository. Connect the intended repository/branch through the app’s supported deployment workflow, then request a build for that exact commit.
  2. Inspect the build result and runtime versions. A successful source upload is not a successful release. Resolve dependency or startup failures before routing real usage.
  3. Start the web process under the selected plan and inspect its boot logs. Model import or configuration errors must fail startup rather than return a fabricated ready response.
  4. Call the assigned HTTPS health URL and verify status ready plus the expected model version. Confirm an unauthorized prediction returns 401, a valid request returns the expected label, and invalid input still returns the documented status.
  5. Record the release identifier, source commit, dependency/runtime versions and observed checks. Keep a previous known-good release and its compatible configuration available.

A rollback creates a new release using a prior build; confirm the platform’s current behavior for config vars, add-ons and databases. This example has no database migrations, but a real data/schema change may not be reversible by rolling back code. If secrets rotate, preserve a deliberate client transition plan rather than restoring compromised credentials.

Separate service health from model validity

The health endpoint proves that a worker started with this toy model version. It does not measure prediction quality, latency under load, ingestion freshness or downstream availability. The small classifier is intentionally unsuitable as evidence of real sentiment accuracy. Replace it only through a reproducible evaluation and release process, keeping the API’s request/response and failure contract stable.

References and next steps

Heroku Python support, Heroku Django applications, Heroku Procfile, Heroku GitHub deployment, Heroku HTTP routing, Release rollback, Django deployment checklist.

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles
Flask vs django cover image
python Oct 17, 2026

Flask vs django

Introduction to Flask and Django Welcome to the fascinating world of Python web development, where Flask and Django shine as two of the most po…