Skip to content

Fix: Heroku H10 App Crashed Error

FixDevs ·

Quick Answer

Fix the Heroku H10 App Crashed error by fixing your Procfile, PORT binding, missing dependencies, and build scripts with step-by-step solutions.

The Error

You deploy to Heroku and your app shows:

Application error
An error occurred in the application and your page could not be served.

In the logs (heroku logs --tail), you see:

heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" status=503
heroku[web.1]: State changed from starting to crashed
heroku[web.1]: Process exited with status 1

The H10 error code means your application process crashed immediately after starting.

Why This Happens

Heroku starts your app by running the command in your Procfile. If the process exits for any reason — missing dependencies, incorrect start command, uncaught exceptions, or failure to bind to the assigned port — Heroku marks it as crashed and returns H10 to all incoming requests.

Unlike local development, Heroku assigns a dynamic port via the PORT environment variable. Hardcoding a port like 3000 causes the app to start but become unreachable, which Heroku interprets as a crash.

Fix 1: Create or Fix Your Procfile

The Procfile tells Heroku how to start your app. It must be in the root directory with exact capitalization (Procfile, not procfile or PROCFILE):

web: node server.js

For common frameworks:

# Node.js / Express
web: node server.js

# Node.js with npm script
web: npm start

# Python / Django
web: gunicorn myproject.wsgi

# Python / Flask
web: gunicorn app:app

# Ruby / Rails
web: bundle exec rails server -p $PORT

# Java / Spring Boot
web: java -jar target/myapp.jar --server.port=$PORT

If you don’t have a Procfile, Heroku tries to detect your framework automatically. This often fails or runs the wrong command. Always create an explicit Procfile.

Verify the Procfile is tracked by Git:

git ls-files Procfile
# Should output: Procfile

Pro Tip: Heroku ignores files not tracked by Git. If your Procfile is in .gitignore or was never committed, Heroku won’t see it. Run git add Procfile && git commit -m "Add Procfile".

Fix 2: Bind to the Correct PORT

Heroku assigns a dynamic port through the PORT environment variable. Your app must listen on this port:

// Node.js / Express
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
# Python / Flask
if __name__ == '__main__':
    port = int(os.environ.get('PORT', 5000))
    app.run(host='0.0.0.0', port=port)
# Python / Django (gunicorn handles this automatically)
# In Procfile:
web: gunicorn myproject.wsgi --bind 0.0.0.0:$PORT

Common mistakes:

// Wrong - hardcoded port
app.listen(3000);

// Wrong - listening on localhost only
app.listen(PORT, '127.0.0.1');

// Correct - listen on all interfaces
app.listen(PORT, '0.0.0.0');

Heroku expects your app to bind to $PORT within 60 seconds of starting. If it takes longer, Heroku kills the process with an R10 error.

Fix 3: Fix Missing Dependencies

Dependencies listed in devDependencies are not installed by default on Heroku. If your app needs them at runtime, move them to dependencies:

{
  "dependencies": {
    "express": "^4.18.0",
    "dotenv": "^16.0.0"
  },
  "devDependencies": {
    "typescript": "^5.0.0"
  }
}

If you need devDependencies installed (e.g., for TypeScript compilation during build):

heroku config:set NPM_CONFIG_PRODUCTION=false
# Or for newer Node.js buildpacks:
heroku config:set NODE_ENV=development

For Python, ensure your requirements.txt is complete:

pip freeze > requirements.txt

Check for native dependencies that need buildpacks:

# Add buildpack for apps needing specific system libraries
heroku buildpacks:add --index 1 heroku-community/apt

Fix 4: Fix Build Scripts

If your app requires a build step (TypeScript compilation, frontend bundling), configure it properly:

{
  "scripts": {
    "build": "tsc && webpack --mode production",
    "start": "node dist/server.js",
    "heroku-postbuild": "npm run build"
  }
}

Heroku runs scripts in this order:

  1. heroku-prebuild (if exists)
  2. npm install
  3. build script (automatically, since Heroku 2023+)
  4. heroku-postbuild (if exists)
  5. Prunes devDependencies
  6. Runs Procfile command

If your build creates output in dist/ but your Procfile points to src/server.js, the app crashes because the compiled files don’t exist:

# Wrong - source file, not compiled
web: node src/server.js

# Correct - compiled output
web: node dist/server.js

Common Mistake: Forgetting to commit the build output or expecting Heroku to compile TypeScript without the build script. Heroku doesn’t install TypeScript globally — it must be in your dependencies and invoked through a script.

Fix 5: Fix Dyno Startup Issues

Check the actual crash reason in the logs:

heroku logs --tail --app your-app-name

Look for the error message before the crash line:

Error: Cannot find module './config/database'
heroku[web.1]: Process exited with status 1
heroku[web.1]: State changed from starting to crashed

Common startup errors:

# Missing module
Error: Cannot find module 'express'
# Fix: npm install express --save

# Syntax error
SyntaxError: Unexpected token 'export'
# Fix: Use CommonJS or configure ESM properly

# Missing environment variable
TypeError: Cannot read properties of undefined (reading 'split')
# Fix: Set config vars on Heroku

Restart the dyno after making changes:

heroku restart --app your-app-name

Fix 6: Handle Memory Limits

Heroku free/hobby dynos have 512 MB memory. If your app exceeds this, it’s killed with an R14 (memory quota exceeded) or R15 error, which can appear as H10 on the next restart:

# Check memory usage
heroku logs --tail | grep "memory"

Reduce memory usage:

// Node.js - limit heap size
// In Procfile:
web: node --max-old-space-size=460 server.js
# Python - use fewer Gunicorn workers
# In Procfile:
web: gunicorn myapp:app --workers 2 --threads 2

For Node.js memory issues, common culprits are:

  • Loading large datasets into memory
  • Not closing database connections
  • Memory leaks from event listeners
  • Large npm packages

Fix 7: Configure Environment Variables (Config Vars)

Missing config vars are a silent killer. Your app starts, can’t find required configuration, and crashes:

# Set config vars
heroku config:set DATABASE_URL=postgres://...
heroku config:set SECRET_KEY=your-secret-key
heroku config:set NODE_ENV=production

# View current config
heroku config

# Remove a config var
heroku config:unset DEBUG

Don’t commit .env files. Use Heroku config vars instead:

# Copy from .env to Heroku
while IFS='=' read -r key value; do
  heroku config:set "$key=$value"
done < .env

Verify your app handles missing variables gracefully:

const requiredVars = ['DATABASE_URL', 'SECRET_KEY', 'API_KEY'];
for (const varName of requiredVars) {
  if (!process.env[varName]) {
    console.error(`Missing required environment variable: ${varName}`);
    process.exit(1);
  }
}

Fix 8: Fix Buildpack Issues

Wrong or missing buildpacks cause the app to build incorrectly:

# Check current buildpacks
heroku buildpacks

# Set the correct buildpack
heroku buildpacks:set heroku/nodejs
heroku buildpacks:set heroku/python

# Multiple buildpacks (order matters)
heroku buildpacks:add --index 1 heroku/nodejs
heroku buildpacks:add --index 2 heroku/python

If Heroku auto-detects the wrong language (e.g., it sees both package.json and requirements.txt), set the buildpack explicitly.

For monorepos where the app isn’t in the root:

heroku config:set PROJECT_PATH=packages/api

Clear the build cache if you changed buildpacks:

heroku plugins:install heroku-repo
heroku repo:purge_cache --app your-app-name
git push heroku main

Still Not Working?

  • Run the app locally with production settings. Use NODE_ENV=production npm start or heroku local with your Procfile to reproduce the issue.

  • Check for Heroku-specific limitations. Heroku has an ephemeral filesystem — files written to disk are lost on dyno restart. Use S3 or similar for file storage.

  • Try scaling down and up. heroku ps:scale web=0 then heroku ps:scale web=1 forces a fresh start.

  • Check your Git remote. Ensure you’re pushing to the correct Heroku app: git remote -v.

  • Look for port binding race conditions. If your app performs async initialization before calling listen(), it might time out. Bind the port first, then initialize.

  • Review the release phase. If you have a release command in your Procfile that fails, it prevents the new release from deploying: release: python manage.py migrate.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles