Troubleshooting
Platforms & PaaS

Fix Heroku H10 App Crashed for Django

Fix Heroku H10 App crashed errors for Django apps: read the crash in heroku logs, fix the Procfile and gunicorn command, bind to $PORT, set config vars like SECRET_KEY and DATABASE_URL, and handle release-phase failures.

Problem statement

The Heroku app shows "Application error" and the router logs:

heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=myapp.herokuapp.com

H10 means the web dyno is not running: it started, crashed, and Heroku's restart attempts failed too. The H10 line itself never contains the reason. The cause is a few lines earlier in the logs, in the dyno's own output: a traceback from Django, a Gunicorn error, or a "process exited with status" message.

What H10 hidesDyno startsruns Procfile webProcess crashesimport or config errorRestarts failcrash loopRouter says H10no dyno to serveH10 is the router's summary; the reason is in the dyno log lines just before it.
  1. Run heroku logs --tail (or --num 300) and scroll to the first traceback after Starting process.
  2. Check the Procfile: web: gunicorn config.wsgi --log-file - with your real project module.
  3. Make sure gunicorn is in requirements.txt.
  4. Set required config vars: SECRET_KEY, DATABASE_URL (via the Postgres add-on), ALLOWED_HOSTS, DJANGO_SETTINGS_MODULE.
  5. Gunicorn binds to $PORT automatically on Heroku; do not hard-code --bind 127.0.0.1:8000.
  6. Restart with heroku restart after fixing, and watch the logs.

Step-by-step solution

1. Find the real error

heroku logs --tail --app myapp
heroku ps --app myapp

Look for the sequence after a restart:

heroku[web.1]: Starting process with command `gunicorn mysite.wsgi`
app[web.1]: ModuleNotFoundError: No module named 'mysite'
heroku[web.1]: Process exited with status 3
heroku[web.1]: State changed from starting to crashed

heroku ps shows crashed for the web dyno. Run the process interactively to see full output:

heroku run python manage.py check --deploy --app myapp

2. Fix the Procfile and Gunicorn command

The Procfile lives in the repository root, named exactly Procfile with no extension:

web: gunicorn config.wsgi --log-file -
release: python manage.py migrate --noinput

config.wsgi must match the package containing wsgi.py. A project created with django-admin startproject mysite uses mysite.wsgi. If manage.py is in a subdirectory, use --chdir:

web: gunicorn --chdir backend config.wsgi --log-file -

bash: gunicorn: command not found in the logs means gunicorn is missing from requirements.txt.

log line before the crashDyno log showsFixgunicorn: command not foundadd gunicorn to requirements.txtNo module named 'mysite'Procfile module pathor --chdir is wrongSECRET_KEY must not be emptyheroku config:set SECRET_KEY=...Error R10 boot timeoutnot bound to $PORT within 60 sno Procfile, no web processadd Procfile with aweb line, scale web=1heroku logs shows the traceback a few lines above the H10django-deployment.com
Fix the first error in the dyno output; H10 disappears with it.

3. Set config vars

Production settings that read the environment need Heroku config vars:

heroku config:set SECRET_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(50))')" --app myapp
heroku config:set DJANGO_SETTINGS_MODULE=config.settings.production ALLOWED_HOSTS=myapp.herokuapp.com --app myapp
heroku addons:create heroku-postgresql:essential-0 --app myapp   # sets DATABASE_URL
heroku config --app myapp

ImproperlyConfigured or KeyError: 'SECRET_KEY' in the logs points here.

4. Bind to $PORT

Heroku assigns the port in the PORT environment variable, and Gunicorn reads it automatically. A Procfile with --bind 127.0.0.1:8000 or a settings file that starts its own server ignores it, and the dyno fails with Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch, followed by H10s.

5. Release phase and build issues

If the release phase (release: python manage.py migrate) fails, the new release is not deployed and the old one keeps running, so that alone does not cause H10. But a release that deploys code requiring a migration that failed can crash on startup. Check heroku releases and heroku releases:output.

collectstatic failures happen at build time and stop the deploy; see Fix collectstatic Errors in Django.

what a Django dyno needs to bootProcfile web line with the right modulegunicorn config.wsgigunicorn in requirements.txtinstalled at buildConfig vars for every required settingSECRET_KEY, ALLOWED_HOSTSDATABASE_URL from the Postgres add-onparsed in settingsNo hard-coded bind addressGunicorn uses $PORTheroku run python manage.py check --deploy catches most of thesedjango-deployment.com
Run the check in a one-off dyno before scaling web up.

Verification check:

heroku ps --app myapp          # web.1: up
curl -sI https://myapp.herokuapp.com | head -1
heroku logs --num 50 --app myapp | grep -c H10

Explanation

Heroku's router forwards requests to running web dynos. When the web process exits, Heroku restarts it, and after repeated quick failures it backs off, leaving the dyno in a crashed state. The router has nothing to forward to and records H10 for each request. Because the router and the dyno log to the same stream, the dyno's exit reason sits just above the H10 lines, but it is easy to miss when the log is flooded with H10s from incoming traffic.

Edge cases / notes

  • Memory. R14 and R15 errors (memory quota exceeded) can lead to crashes on small dynos; reduce Gunicorn workers with WEB_CONCURRENCY.
  • Python version. A .python-version or runtime.txt naming an unsupported version fails the build, not the boot.
  • Stack upgrades. Moving to a new Heroku stack can break compiled dependencies; rebuild and check logs.
  • H14 means no web dynos are running at all; scale with heroku ps:scale web=1.

Next steps

FAQ

What does Heroku H10 mean?

The web dyno crashed and is not running, so the router cannot serve requests.

Where is the real error?

In the dyno log lines before the H10 entries, after Starting process with command.

Do I need to set --bind for Gunicorn on Heroku?

No. Gunicorn reads the PORT variable automatically. Hard-coding a bind address causes boot timeouts.

How do I restart after fixing?

Deploy the fix or run heroku restart, then watch heroku logs --tail.

Keep going