Se você já passou da fase de desenvolvimento com o servidor embutido do Flask, o próximo passo natural é colocar um servidor WSGI de verdade na frente — e o Gunicorn continua sendo a escolha mais pragmática.
Aqui vai o setup mínimo, com os pontos que realmente impactam comportamento em produção.
Instalação
pip install gunicorn
Sem surpresa aqui. Mantém isso no seu requirements.txt.
Estrutura mínima da aplicação
Exemplo simples:
# app.py
from flask import Flaskapp = Flask(__name__)@app.route("/")
def hello():
return "Hello, Gunicorn!"
Rodando o Gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 app:app
O que está acontecendo:
-w 4→ número de workers (processos)-b→ bind (host:porta)app:app→arquivo:instância
Como escolher número de workers
Regra clássica (não perfeita, mas útil):
workers = (2 * CPU) + 1
Exemplo: máquina com 2 vCPUs → 5 workers
Mas isso quebra rápido se:
- sua app faz IO pesado (APIs externas, banco lento)
- ou usa threads
Ajuste real vem de teste, não de fórmula.
Adicionando threads (quando faz sentido)
gunicorn -w 3 --threads 2 -b 0.0.0.0:8000 app:app
Use threads quando:
- você tem muita espera por IO
- quer reduzir número de processos (menos RAM)
Evite threads quando:
- CPU-bound (ex: processamento pesado)
- uso intensivo de libs não thread-safe
Timeout (onde muita gente se ferra)
Default: 30s
gunicorn --timeout 60 ...
Se sua aplicação demora mais que isso:
- ou você aumenta o timeout
- ou você tem um problema arquitetural (provavelmente isso)
Timeout alto demais = workers presos = fila crescendo = meltdown.
Logging básico
gunicorn \
--access-logfile - \
--error-logfile - \
-w 4 \
-b 0.0.0.0:8000 \
app:app
-= stdout → funciona bem com Docker / systemd / Cloud logs
Config via arquivo (melhor que CLI gigante)
Crie gunicorn.conf.py:
bind = "0.0.0.0:8000"
workers = 4
threads = 2
timeout = 60accesslog = "-"
errorlog = "-"
loglevel = "info"
Rodar:
gunicorn -c gunicorn.conf.py app:app
Rodando com Application Factory (Flask moderno)
Se você usa factory:
# app/__init__.py
def create_app():
app = Flask(__name__)
return app
Rodar:
gunicorn "app:create_app()"
Aspas são importantes aqui.
Rodando com Docker (mínimo viável)
FROM python:3.11-slimWORKDIR /app
COPY . .RUN pip install -r requirements.txtCMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "app:app"]
Reverse proxy (não pule isso)
Gunicorn não é servidor de borda.
Coloque algo na frente:
- Nginx
- Caddy
- Cloudflare (já que você mencionou usar)
Funções do proxy:
- TLS (HTTPS)
- cache
- compressão
- proteção contra tráfego lixo
Gargalos reais (onde observar)
Se algo degrada:
- CPU alta → workers demais ou carga real
- RAM alta → muitos workers
- latência → IO externo / DB
- fila crescendo → poucos workers ou endpoints lentos
Gunicorn não resolve arquitetura ruim — só expõe.
Setup mínimo “decente” (resumo)
gunicorn \
-w 4 \
--threads 2 \
--timeout 60 \
--access-logfile - \
--error-logfile - \
-b 0.0.0.0:8000 \
app:app