Websites & apps

Deploy a Flask app to production: factory, config and Gunicorn

Prepare a Flask app for production: app factory and config from environment, Flask-Migrate database upgrades, ProxyFix behind Nginx, Gunicorn workers and a systemd service.

IntermediateLinux VPS3 min read

flask run is a development server. To deploy Flask to production you need a few app-level changes, configuration from the environment, correct proxy handling and migrations, plus Gunicorn and systemd. This guide focuses on the Flask-specific parts; the general Python server setup is in the Gunicorn and Nginx guide.

Before you start

  • A Linux VPS with Python 3, Nginx and a domain (see the linked Gunicorn guide for the base setup).
  • Your Flask app in Git with a requirements.txt including gunicorn.

Use an application factory

A factory makes configuration explicit and works cleanly with Gunicorn:

import os
from flask import Flask
from werkzeug.middleware.proxy_fix import ProxyFix

def create_app():
    app = Flask(__name__)
    app.config.update(
        SECRET_KEY=os.environ["SECRET_KEY"],
        SQLALCHEMY_DATABASE_URI=os.environ["DATABASE_URL"],
        SESSION_COOKIE_SECURE=True,
        PREFERRED_URL_SCHEME="https",
    )
    app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)
    # register blueprints, init extensions here
    return app
  • SECRET_KEY signs sessions; generate it with python -c "import secrets; print(secrets.token_hex(32))" and never commit it.
  • ProxyFix makes Flask trust Nginx's X-Forwarded-* headers, so url_for(..., _external=True) builds https URLs and request.remote_addr shows the real visitor IP. Set the counts to the number of proxies in front.
  • Make sure debug mode is off; never deploy with debug=True, which exposes an interactive debugger.

Create a WSGI entry point

wsgi.py in the project root:

from myapp import create_app

app = create_app()

Environment file

/srv/flaskapp/.env (chmod 600, owned by the app user):

SECRET_KEY=generated-secret
DATABASE_URL=postgresql+psycopg://app:password@127.0.0.1/app

Run database migrations

With Flask-Migrate (Alembic), apply migrations on each deploy:

cd /srv/flaskapp/app && set -a && . ../.env && set +a && FLASK_APP=wsgi.py ../venv/bin/flask db upgrade

Gunicorn under systemd

/etc/systemd/system/flaskapp.service:

[Unit]
Description=Flask app
After=network.target

[Service]
User=flaskapp
Group=flaskapp
WorkingDirectory=/srv/flaskapp/app
EnvironmentFile=/srv/flaskapp/.env
ExecStart=/srv/flaskapp/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 --access-logfile - --error-logfile - wsgi:app
Restart=on-failure

[Install]
WantedBy=multi-user.target
systemctl daemon-reload && systemctl enable --now flaskapp

For async or long-polling apps, consider --worker-class gthread --threads 4; for WebSockets with Flask-SocketIO use the eventlet or gevent worker and a single worker process (or a message queue).

Nginx in front

Proxy to 127.0.0.1:8000 and pass Host, X-Forwarded-For and X-Forwarded-Proto, and serve /static/ directly from the app's static folder with alias. Add SSL with Certbot.

Deploy updates

cd /srv/flaskapp/app && sudo -u flaskapp git pull && sudo -u flaskapp ../venv/bin/pip install -r requirements.txt && systemctl restart flaskapp

Run migrations between install and restart when models changed.

Troubleshooting

  • KeyError: 'SECRET_KEY': the env file is not loaded; check EnvironmentFile and run commands with the variables exported.
  • Redirects go to http://: ProxyFix is missing or Nginx does not send X-Forwarded-Proto.
  • Users logged out randomly: SECRET_KEY changes between restarts or differs between servers.
  • 502 Bad Gateway: Gunicorn crashed on import; journalctl -u flaskapp shows the traceback.

Flask deployment FAQ

How many Gunicorn workers?

Start with 2 × cores + 1 for sync workers and adjust by memory use.

Can I host Flask on cPanel?

Yes, with Setup Python App, using a small passenger_wsgi.py that imports your app.

Do I need Docker?

No. systemd and a virtualenv work well; Docker helps when you run several services together.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.