Deploy a Flask app

Deploy a Flask app on Hangar with gunicorn, detected and started for you, with PostgreSQL, migrations on release and HTTPS on your domain.

Updated

A Flask app runs on Hangar behind gunicorn, as a long-lived server in a container. The automatic builder recognises Flask and starts it for you.

Before you start

  • A Flask app in a Git repository, with its dependencies in requirements.txt or pyproject.toml, including gunicorn:
flask
gunicorn

1. Create the service

Create a project, add a service and pick your repository and branch. Choose the region closest to your users and leave the builder on Automatic. It detects Python, installs your dependencies with pip, uv, Poetry, PDM or Pipenv depending on the files in the repository, and starts gunicorn on 0.0.0.0 with the app in main:app.

To pin the Python version, set RAILPACK_PYTHON_VERSION, for example to 3.12, in the Variables tab.

2. Point gunicorn at your app

If your Flask instance is not the app variable of main.py, set the start command in Settings → Runtime:

gunicorn --bind 0.0.0.0:8000 --workers 2 app:app

app:app is the module and the variable. With an application factory, use myapp:create_app() as the target, without quotes: the start command does not go through a shell. --bind 0.0.0.0 is required: on 127.0.0.1 the proxy cannot reach the app.

3. Set the variables

In the Variables tab:

SECRET_KEY=<a long random string>

4. Deploy and add a domain

Deploy the service. In Settings → Networking, add a domain with container port 8000 and HTTPS on.

Hangar terminates HTTPS in front of the app. So that url_for(..., _external=True) and redirects use https://, wrap the app with Werkzeug's proxy fix:

from werkzeug.middleware.proxy_fix import ProxyFix

app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)

Adding PostgreSQL

Add a PostgreSQL service in the same project and region, copy its internal connection URL and set it on the app:

DATABASE_URL=<the internal connection URL>

With Flask-SQLAlchemy:

import os

app.config["SQLALCHEMY_DATABASE_URI"] = os.environ["DATABASE_URL"]

Migrations

With Flask-Migrate, run the migration before gunicorn so every release migrates first. Chained commands need a shell: set the start command to sh and add two arguments, -c and this line:

flask db upgrade && gunicorn --bind 0.0.0.0:8000 --workers 2 app:app

Set FLASK_APP in the variables if the flask command cannot find your app. See Start command.

Troubleshooting

  • 502 on the domain: gunicorn is bound to 127.0.0.1, or the container port is not the one gunicorn listens on.
  • Failed to find attribute 'app': the module and variable in the start command do not match your code.
  • Links and redirects use http://: add ProxyFix.

Frequently asked questions

What start command does Flask need on Hangar?
None, in the common case. When the automatic builder detects Flask it starts gunicorn on 0.0.0.0 with your app in main:app. If your app lives elsewhere, set the start command to gunicorn --bind 0.0.0.0:8000 with your module and variable, such as app:app.
Should I use flask run in production?
No. The development server is not built for production traffic. Use gunicorn, which the builder runs for you when Flask is detected.

Deploy your app on Hangar

Connect a repository and it is live in minutes.

Start deploying