# How to Deploy a Flask App to Production (Gunicorn + PostgreSQL, 2026)

Take Flask from app.run() to production: Gunicorn, configuration from the environment, PostgreSQL with Flask-SQLAlchemy, migrations and a live HTTPS URL.

URL: https://blog.dockhosting.dev/deploy-flask-app
Published: 2026-10-23 · Updated: 2026-10-23 · Author: Doughmi ILYAS

## Summary

- Never use Flask's built-in server (app.run) in production: run under Gunicorn, which DockHosting does for you.
- Load SECRET_KEY and DATABASE_URL from the environment; Flask-SQLAlchemy reads DATABASE_URL directly.
- The default start command is gunicorn app:app, and you can override it if your app object lives elsewhere.

ort const meta = {
  title: "How to Deploy a Flask App to Production (Gunicorn + PostgreSQL, 2026)",
  seoTitle: "How to Deploy a Flask App to Production (2026)",
  description:
    "Take Flask from app.run() to production: Gunicorn, configuration from the environment, PostgreSQL with Flask-SQLAlchemy, migrations and a live HTTPS URL.",
  date: "2026-10-23",
  category: "Guides",
  tags: ["Flask", "Python", "Gunicorn", "PostgreSQL", "Tutorial"],
  stack: ["flask", "gunicorn", "postgresql"],
  command: "dock deploy",
  takeaways: [
    "Never use Flask's built-in server (app.run) in production: run under Gunicorn, which DockHosting does for you.",
    "Load SECRET_KEY and DATABASE_URL from the environment; Flask-SQLAlchemy reads DATABASE_URL directly.",
    "The default start command is gunicorn app:app, and you can override it if your app object lives elsewhere.",
  ],
  faq: [
    {
      q: "Do I need a Procfile to deploy Flask?",
      a: "No. DockHosting detects Flask from your requirements and runs it under Gunicorn (gunicorn app:app by default). If your app is created elsewhere, for example by a factory in wsgi.py, override the start command in the project settings.",
    },
    {
      q: "Why does Flask say 'WARNING: This is a development server'?",
      a: "Because app.run() starts Werkzeug's development server, which isn't built for production traffic or security. Use Gunicorn instead.",
    },
    {
      q: "Which Python versions are supported?",
      a: "Python 3.11 and 3.12, with pip (requirements.txt) or Poetry (pyproject.toml).",
    },
  ],
};

Flask's development server prints a warning for a reason: it's for your laptop. In production you want a real WSGI server, configuration from the environment, and a database that isn't SQLite on a disk that disappears. Here's the whole path to a live HTTPS URL.

## 1. Structure the app

```python
# app.py
import os
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SECRET_KEY"] = os.environ["SECRET_KEY"]
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ["DATABASE_URL"].replace(
    "postgresql://", "postgresql+psycopg://", 1
)
db = SQLAlchemy(app)

class Note(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    text = db.Column(db.String(500), nullable=False)

@app.get("/health")
def health():
    return {"status": "ok"}

@app.get("/notes")
def notes():
    return jsonify([{"id": n.id, "text": n.text} for n in Note.query.all()])
```

The `replace` call selects the psycopg 3 driver for SQLAlchemy.

```text
# requirements.txt
flask
flask-sqlalchemy
flask-migrate
gunicorn
psycopg[binary]
```

> Remove any `app.run(debug=True)` from code that runs in production. The debugger allows arbitrary code execution from the browser.

## 2. Gunicorn, configured for you

DockHosting detects Flask from `flask` in your requirements and runs it under **Gunicorn**:

```bash
gunicorn app:app
```

That's `module:variable`. If you use an application factory, point Gunicorn at it by overriding the start command in the project settings:

```bash
gunicorn "myapp:create_app()" --workers 2 --bind 0.0.0.0:$PORT
```

Two workers per 512 MB is a safe start. Each worker is a full copy of your app in memory.

## 3. Migrations with Flask-Migrate

```bash
flask --app app db init       # once, locally
flask --app app db migrate -m "notes table"
```

Commit the `migrations/` folder, and apply migrations on each release as part of the start command:

```bash
flask --app app db upgrade && gunicorn app:app
```

## 4. Deploy

```bash
curl -fsSL https://cli.dockhosting.dev/install.sh | sh   # Windows: irm https://cli.dockhosting.dev/install.ps1 | iex
dock login
dock deploy
```

Then:

1. **Create a managed PostgreSQL** database and attach it. `DATABASE_URL` is injected over the private network.
2. **Set `SECRET_KEY`** in the environment variables (`python -c "import secrets; print(secrets.token_hex(32))"`).
3. `dock restart`, then open `https://<project>.dockhosting.cloud/health`.

## 5. Production checklist

- `SECRET_KEY` comes from the environment, never from the code.
- Debug is off, and `app.run()` isn't called at import time.
- Gunicorn workers are sized to your plan's memory (512 MB on Free, 1 GB on Hobby).
- Static files are served from `static/` or a CDN for heavy assets.
- A `/health` route makes failed deploys obvious.

Seeing 502s? Work through the [8 causes of a 502](/fix-502-bad-gateway-after-deploy). Building a larger app? Compare with the [Django](/deploy-django-app-production) and [FastAPI](/deploy-fastapi-app) guides.

## FAQ

### Do I need a Procfile to deploy Flask?

No. DockHosting detects Flask from your requirements and runs it under Gunicorn (gunicorn app:app by default). If your app is created elsewhere, for example by a factory in wsgi.py, override the start command in the project settings.

### Why does Flask say 'WARNING: This is a development server'?

Because app.run() starts Werkzeug's development server, which isn't built for production traffic or security. Use Gunicorn instead.

### Which Python versions are supported?

Python 3.11 and 3.12, with pip (requirements.txt) or Poetry (pyproject.toml).
