# How to Deploy a Flask App on Heroku in 2026: Step-by-Step Guide

> Learn how to deploy a Flask app on Heroku in 2026. Step-by-step guide covering Procfile, Gunicorn, runtime.txt, Postgres, env vars, and common errors.
- **Author**: emily-carter
- **Published**: 2026-05-09
- **Modified**: 2026-05-09
- **Category**: Deployment Guides
- **URL**: https://kuberns.com/blogs/how-to-deploy-flask-on-heroku/

---

Deploying a Flask app on Heroku takes about 15 minutes if your project is structured correctly. The process requires three files Heroku cannot work without: a `Procfile` that tells it how to start your app, a `requirements.txt` listing your dependencies, and `gunicorn` as your production WSGI server. Get those three right and the rest is just git push.

One important thing to know before you start: Heroku ended its free tier in November 2022. In 2026, the minimum cost to run a Flask app on Heroku is around $10 per month — $5 for an Eco dyno and $5 for a basic Postgres add-on if your app uses a database. If that is a concern, there are [Heroku alternatives](https://kuberns.com/blogs/the-ultimate-guide-to-heroku-alternatives-in-2025/) worth evaluating before you commit.

This guide covers every step from project structure to production deployment, plus how to connect Postgres, manage environment variables, and fix the most common Heroku Flask errors.

## What You Need Before You Start

Before running a single command, make sure you have:

- **Python 3.10 or higher** and a working Flask app
- **Git** installed and your project already in a repository
- **Heroku CLI** installed — download from [devcenter.heroku.com/articles/heroku-cli](https://devcenter.heroku.com/articles/heroku-cli)
- **A Heroku account** — sign up at heroku.com; a paid plan is required (minimum Eco dyno at $5/month). See the full [Heroku pricing breakdown](https://kuberns.com/blogs/heroku-pricing-explained/) to understand what you will pay.

If your project is not in a Git repo yet, run `git init && git add . && git commit -m "initial commit"` before continuing.

> Want to skip the manual setup entirely? [Kuberns](https://kuberns.com) detects your Flask stack automatically, configures Gunicorn, and deploys with HTTPS and CI/CD enabled — no Procfile, no buildpack selection, no config files.

## Step 1 — Prepare Your Flask Project

Heroku needs four files at the root of your project. Without them, the build will fail or the app will crash on startup.

### app.py — Bind to the PORT Environment Variable

The most common reason Flask apps fail on Heroku is binding to a hardcoded port. Heroku assigns a dynamic port via the `PORT` environment variable. You must use it:

```python
import os
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello from Flask on Heroku!"

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(host="0.0.0.0", port=port)
```

The `if __name__ == "__main__"` block is for local development only. Heroku runs gunicorn directly from the Procfile, so this block is not executed in production — but binding to `os.environ.get("PORT")` is still important for correct behavior.

### requirements.txt — List All Dependencies

```
flask==3.0.3
gunicorn==22.0.0
```

If your app uses SQLAlchemy and Postgres, add:

```
flask-sqlalchemy==3.1.1
psycopg2-binary==2.9.9
```

Generate your current environment's requirements with `pip freeze > requirements.txt`. Review the output and remove any development-only packages.

### runtime.txt — Pin Your Python Version

```
python-3.12.9
```

Heroku supports Python 3.10, 3.11, 3.12, and 3.13 as of 2026. Without this file, Heroku picks its default version, which may not match your local environment and can cause subtle dependency issues.

### .gitignore — Keep Secrets Out of Git

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

Never commit your `.env` file or virtual environment folder. Environment variables go into Heroku Config Vars, not source control.

## Step 2 — Create the Procfile

Create a file named `Procfile` at the root of your project. No file extension:

```
web: gunicorn app:app
```

This tells Heroku: start a web dyno and run the `app` object inside the `app.py` module using gunicorn.

**Breaking down the format:**
- `web` — the process type (this is the dyno that receives HTTP traffic)
- `gunicorn` — the production WSGI server
- `app:app` — module name : Flask application object name

If your Flask app object is named differently (e.g., `application = Flask(__name__)`), update accordingly: `web: gunicorn app:application`.

### Why Gunicorn and Not the Flask Dev Server?

Flask's built-in dev server (`flask run`) is single-threaded and designed for local development only. It cannot handle concurrent requests. Gunicorn is a battle-tested production WSGI server that spawns multiple worker processes to handle real traffic.

For a slightly higher-traffic app, you can specify worker count directly in the Procfile:

```
web: gunicorn app:app --workers 2 --timeout 120
```

On Heroku's standard dynos, 2 workers per dyno is a reasonable starting point.

## Step 3 — Deploy to Heroku

With your project files in place, deployment is four commands:

```bash
# Log in to Heroku (opens browser)
heroku login

# Create a new Heroku app (auto-generates a name, or specify one)
heroku create your-flask-app-name

# Push your code
git push heroku main

# Open the live URL
heroku open
```

You should see output like this during the build:

```
remote: -----> Python app detected
remote: -----> Installing python-3.12.9
remote: -----> Installing pip 24.x
remote: -----> Installing requirements with pip
remote:        Successfully installed flask-3.0.3 gunicorn-22.0.0
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote: -----> Launching...
remote:        Released v3
remote:        https://your-flask-app-name.herokuapp.com/ deployed to Heroku
```

If the build completes but the app shows "Application Error", jump to the troubleshooting section below.

You can also connect your GitHub repository for automatic deployments on every push — see the [Heroku GitHub integration guide](https://kuberns.com/blogs/heroku-github-integration/) for the full setup.

## Step 4 — Set Environment Variables

Never hardcode secrets, API keys, or configuration into your codebase. Heroku provides Config Vars for this:

```bash
heroku config:set SECRET_KEY=your-very-long-random-secret-key
heroku config:set FLASK_ENV=production
heroku config:set DEBUG=False
```

Access them in Flask the same way you would any environment variable:

```python
import os

SECRET_KEY = os.environ.get("SECRET_KEY")
DEBUG = os.environ.get("DEBUG", "False").lower() == "true"
```

You can also set Config Vars in the Heroku dashboard: go to your app → **Settings** → **Config Vars** → **Reveal Config Vars**. Useful when you want to rotate a key without touching the CLI.

To see all current config vars: `heroku config`

## Step 5 — Add Postgres to Your Flask App

If your Flask app needs a database, the [Heroku Postgres add-on](https://kuberns.com/blogs/heroku-postgres/) is the most straightforward option. The Essential-0 plan starts at $5/month.

### Provision the Add-on

```bash
heroku addons:create heroku-postgresql:essential-0
```

Heroku automatically sets the `DATABASE_URL` environment variable. Verify it:

```bash
heroku config | grep DATABASE_URL
```

### Connect Flask-SQLAlchemy

Update your `requirements.txt`:

```
flask==3.0.3
gunicorn==22.0.0
flask-sqlalchemy==3.1.1
psycopg2-binary==2.9.9
```

Configure SQLAlchemy in your Flask app:

```python
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# Heroku sets DATABASE_URL as postgres:// but SQLAlchemy needs postgresql://
database_url = os.environ.get("DATABASE_URL", "").replace("postgres://", "postgresql://", 1)
app.config["SQLALCHEMY_DATABASE_URI"] = database_url
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

db = SQLAlchemy(app)
```

Note the `replace("postgres://", "postgresql://", 1)` — Heroku still uses the older `postgres://` scheme in its `DATABASE_URL`, but SQLAlchemy 1.4+ requires `postgresql://`. This one-liner handles the mismatch.

### Run Migrations

If you are using Flask-Migrate:

```bash
heroku run flask db upgrade
```

This runs the migration command inside a one-off dyno on Heroku's infrastructure, against your production database.

## Common Heroku Flask Errors and Fixes

### H10 — App Crashed on Boot

**Cause:** Heroku tried to start your app and it exited immediately. Common causes: syntax error in `app.py`, missing import, or wrong Procfile entry.

**Fix:** Run `heroku logs --tail` immediately after deployment and look for the Python traceback. Fix the error and redeploy.

### H14 — No Web Dynos Running

**Cause:** Heroku deployed your app but there are no web dynos scaled up to serve traffic.

**Fix:**
```bash
heroku ps:scale web=1
```

This scales one web dyno. Without it, your app is deployed but not running.

### Application Error (Generic)

**Cause:** App is running but returning a 500 or crashing on a specific request.

**Fix:** `heroku logs --tail` while hitting the failing endpoint. The Python traceback will tell you exactly what broke.

### ModuleNotFoundError: No module named 'gunicorn'

**Cause:** `gunicorn` is missing from `requirements.txt`.

**Fix:** Add `gunicorn==22.0.0` to your `requirements.txt`, commit, and redeploy.

### Port Already in Use / App Not Starting

**Cause:** Your `app.run()` call uses a hardcoded port like `5000` instead of reading from the `PORT` environment variable.

**Fix:** Change to `app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))`.

### Build Succeeded But Wrong Python Version Behaviour

**Cause:** No `runtime.txt` in your repo — Heroku used a different Python version than your local environment.

**Fix:** Add a `runtime.txt` file with your exact version: `python-3.12.9`.

## Heroku Flask Deployment Costs in 2026

Running a Flask app on Heroku with a database costs at minimum:

| Resource | Plan | Monthly Cost |
|---|---|---|
| Web Dyno | Eco | $5/month (1000 hours shared) |
| Postgres | Essential-0 | $5/month |
| **Total** | | **~$10/month** |

Eco dynos sleep after 30 minutes of inactivity — your app will have a cold start delay of 10–30 seconds on the first request after sleeping. Basic dynos ($7/month each) do not sleep.

At higher scale, costs increase quickly. A production Flask app on multiple Standard-2X dynos with a Postgres Standard-0 plan can run $200+/month. See the full [Heroku pricing guide](https://kuberns.com/blogs/heroku-pricing-explained/) for a complete breakdown.

## Is Heroku Still Worth It for Flask in 2026?

Heroku's developer experience remains one of the best in the industry. Git push deployments, seamless add-ons, reliable uptime, and mature documentation make it genuinely easy to get a Flask app running in production.

The tradeoffs are real though. No free tier means even hobby projects cost money. Eco dynos sleep in ways that hurt user experience. And at scale, [deploying a Python app on Heroku](https://kuberns.com/blogs/how-to-deploy-python-app-on-heroku/) becomes expensive compared to alternatives that give you more compute per dollar.

For a side project or small team app, Heroku is still a solid choice. For anything where cost-per-request matters or where you need zero cold starts, the [Heroku alternatives](https://kuberns.com/blogs/the-ultimate-guide-to-heroku-alternatives-in-2025/) are worth a serious look before committing.

## Frequently Asked Questions

### Q: Can I deploy a Flask app on Heroku for free?

Heroku removed its free tier in November 2022. The cheapest option in 2026 is the Eco dyno at $5/month for 1000 shared dyno hours, plus separate add-on costs for databases. There is no free Flask hosting on Heroku anymore.

### Q: What is a Procfile and why does Flask need one on Heroku?

A Procfile is a plain text file at the root of your project that tells Heroku how to start your application. Flask needs one because Heroku does not use the built-in Flask dev server in production. The standard entry is `web: gunicorn app:app` — no file extension, exact casing required.

### Q: Why do I need Gunicorn to deploy Flask on Heroku?

Flask's built-in server is single-threaded and explicitly documented as not safe for production use. Gunicorn is a production-grade WSGI server that handles concurrent HTTP requests across multiple worker processes. Heroku requires a production WSGI server and will not accept the Flask dev server.

### Q: How do I connect a Postgres database to my Flask app on Heroku?

Run `heroku addons:create heroku-postgresql:essential-0` to provision the database. Heroku automatically sets the `DATABASE_URL` environment variable. In Flask, read it with `os.environ.get('DATABASE_URL')` and pass it to SQLAlchemy. Remember to replace `postgres://` with `postgresql://` in the URL for SQLAlchemy 1.4+ compatibility.

### Q: How do I set environment variables for a Flask app on Heroku?

Use the CLI: `heroku config:set SECRET_KEY=your-value`. Or set them in the Heroku dashboard under Settings → Config Vars. These are injected as standard environment variables at runtime, accessible via `os.environ.get('SECRET_KEY')` in your Flask app.

### Q: What Python versions does Heroku support in 2026?

Heroku supports Python 3.10, 3.11, 3.12, and 3.13 as of 2026. Pin your exact version in a `runtime.txt` file at the project root — for example, `python-3.12.9`. Without it, Heroku chooses its own default, which may cause dependency mismatches.

### Q: How do I fix the Application Error on a Heroku Flask deployment?

Run `heroku logs --tail` and look for the Python traceback. The most common causes are: missing `gunicorn` in `requirements.txt`, incorrect Procfile format, hardcoded port instead of reading from `PORT` env var, or missing required environment variables. Fix the root cause and push again.

### Q: Can I deploy a Flask REST API on Heroku?

Yes. Flask REST APIs deploy identically to standard Flask web apps. Use Gunicorn in your Procfile, add CORS headers with `flask-cors` if needed, set your `DATABASE_URL` and any API keys as Config Vars, and push. The deployment process is the same regardless of whether your Flask app serves HTML or JSON.

## Conclusion

Deploying Flask on Heroku comes down to three files — `Procfile`, `requirements.txt`, and `runtime.txt` — plus Gunicorn as your production server. Get those right, bind to the `PORT` environment variable, and the rest of the deployment is straightforward.

For teams who want to skip the manual configuration entirely, Kuberns handles all of this automatically — stack detection, Gunicorn setup, HTTPS, CI/CD, and AWS-grade infrastructure — without a single config file.

[Try Kuberns free →](https://dashboard.kuberns.com/)

---
- [More Deployment Guides articles](https://kuberns.com/blogs/category/deployment-guides/1/)
- [All articles](https://kuberns.com/blogs/)