Deploy a Python Flask App with Nginx and Gunicorn

Why Flask's own dev server isn't safe for production, and how Gunicorn plus an Nginx reverse proxy replaces it correctly.

By DevStudio Online Team · Published September 3, 2026

Running flask run or app.run() in production is the single most common Flask deployment mistake — Flask's own docs say as much. Its dev server is single-threaded, has no process management, and isn't built to be exposed directly to the internet. Gunicorn plus Nginx is the standard replacement.

Install Gunicorn

pip install gunicorn

Run the app under Gunicorn

Assuming your Flask app object is called app inside app.py:

gunicorn --workers 3 --bind 127.0.0.1:8000 app:app

app:app means "the module app.py, the variable named app inside it." --workers 3 runs three separate worker processes — a reasonable starting point is (2 × CPU cores) + 1, so an app on a 2-core server would use --workers 5.

Keep it running with systemd

Create /etc/systemd/system/myflaskapp.service:

[Unit]
Description=Gunicorn instance for my Flask app
After=network.target
 
[Service]
User=www-data
WorkingDirectory=/var/www/my-flask-app
ExecStart=/var/www/my-flask-app/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 app:app
Restart=always
 
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now myflaskapp

Restart=always is what turns "the app crashed at 3 AM" into "the app restarted itself at 3 AM" — worth having on any long-running service.

The Nginx reverse proxy

server {
    listen 80;
    server_name myapp.example.com;
 
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Same pattern as fronting any other app server: Nginx handles the public-facing port, TLS, and static-file serving; Gunicorn handles running your actual Python code, bound only to 127.0.0.1 where the outside world can't reach it directly.

Serving static files efficiently

Flask can serve static files itself, but it's wasteful to route them through Gunicorn and your Python code when Nginx can serve them directly:

location /static/ {
    alias /var/www/my-flask-app/static/;
}

Any request under /static/ never touches Gunicorn or a single line of Python — Nginx serves the file straight off disk, which is both faster and lighter on your worker processes.

More Nginx guides