Flask e Django em produção com Gunicorn e Nginx
Flask e Django têm servidores de desenvolvimento embutidos que não são adequados para produção — não suportam concorrência real. O stack correto para produção é: Gunicorn como servidor WSGI (processa requisições em paralelo), Nginx como proxy reverso (gerencia TLS, compressão, assets estáticos) e Docker para empacotamento.
Por que não usar o servidor embutido em produção
Flask e Django têm servidores de desenvolvimento com avisos explícitos — entenda as limitações antes de escolher o substituto:
# Servidor de desenvolvimento (NUNCA em produção):
flask run # Warning: "Do not use the development server in a production environment"
python manage.py runserver # Django: "This is not a production server!"
# Problemas do servidor de desenvolvimento:
# - Single-threaded: uma requisição por vez
# - Sem gerenciamento de processos: crash = downtime
# - Sem TLS nativo
# - Sem compressão de resposta
# Stack de produção correto:
# Cliente → Nginx (TLS, assets, compressão) → Gunicorn (workers) → Flask/Django appConfigurar Gunicorn para produção
Gunicorn em produção precisa de configuração explícita de workers, timeout e bind:
# gunicorn.conf.py (arquivo de configuração)
import multiprocessing
# Número de workers: (2 × CPUs) + 1
workers = multiprocessing.cpu_count() * 2 + 1
# Tipo de worker
worker_class = "gthread" # threads por worker
threads = 4 # 4 threads por worker (workers × threads = concorrência total)
# Bind
bind = "0.0.0.0:8000"
# Timeouts
timeout = 120 # matar worker que não responde em 120s
graceful_timeout = 30 # tempo para worker terminar antes de ser forçado
keepalive = 5 # tempo keepalive em segundos
# Logs
accesslog = "-" # stdout
errorlog = "-" # stderr
loglevel = "info"
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'
# Segurança
limit_request_line = 4094
limit_request_fields = 100Dockerfile para Flask/Django com Gunicorn
Imagem de produção com Gunicorn configurado:
# Dockerfile
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq-dev gcc \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Coletar arquivos estáticos do Django
RUN python manage.py collectstatic --noinput || true
RUN useradd --create-home appuser && chown -R appuser /app
USER appuser
EXPOSE 8000
# Gunicorn com arquivo de config
CMD ["gunicorn", "--config", "gunicorn.conf.py", "config.wsgi:application"]
# Para Flask: CMD ["gunicorn", "--config", "gunicorn.conf.py", "app:app"]Nginx para servir arquivos estáticos do Django
Para Django, o Nginx deve servir os arquivos estáticos diretamente — sem passar pelo Gunicorn:
# nginx.conf para Django/Flask
server {
listen 443 ssl;
http2 on;
server_name app.seudominio.com.br;
ssl_certificate /etc/letsencrypt/live/app.seudominio.com.br/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.seudominio.com.br/privkey.pem;
# Servir arquivos estáticos diretamente (Django collectstatic)
location /static/ {
alias /app/staticfiles/;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Servir arquivos de upload
location /media/ {
alias /app/media/;
expires 30d;
}
# Proxy para Gunicorn
location / {
proxy_pass http://gunicorn: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;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 50M;
}
}Variáveis de ambiente e configuração Django
django-environ ou python-decouple separam configuração de código. Configure DEBUG=False e ALLOWED_HOSTS para produção:
# settings.py (usando django-environ)
import environ
env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env()
SECRET_KEY = env("SECRET_KEY")
DEBUG = env("DEBUG")
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS")
DATABASES = {
"default": env.db() # parseia DATABASE_URL automaticamente
}
# Configurações de produção (DEBUG=False)
SECURE_SSL_REDIRECT = not DEBUG
SESSION_COOKIE_SECURE = not DEBUG
CSRF_COOKIE_SECURE = not DEBUG
SECURE_HSTS_SECONDS = 31536000 if not DEBUG else 0
SECURE_HSTS_INCLUDE_SUBDOMAINS = not DEBUG
# .env (não commitado)
SECRET_KEY=chave_secreta_muito_longa
DEBUG=false
ALLOWED_HOSTS=app.seudominio.com.br,www.seudominio.com.br
DATABASE_URL=postgresql://user:pass@postgres:5432/db$ runstack deploy --plan starter
Não quer configurar manualmente?
Não quer configurar manualmente? Implante o VPS em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.