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 gunicornRun 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:appapp: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.targetsudo systemctl enable --now myflaskappRestart=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.