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.txtorpyproject.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://: addProxyFix.