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:

    bash
    # 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 app
    Dica
    Gunicorn é WSGI (síncrono). Para apps async com FastAPI ou Django Channels: use Uvicorn ou Daphne. Gunicorn pode usar workers Uvicorn (uvicorn.workers.UvicornWorker) para apps ASGI.

    Configurar Gunicorn para produção

    Gunicorn em produção precisa de configuração explícita de workers, timeout e bind:

    python
    # 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 = 100
    Dica
    worker_class="gthread" com threads=4 é melhor que sync para apps com I/O (banco, APIs externas): cada worker tem 4 threads que podem processar requisições enquanto esperam I/O. Para apps puramente CPU-bound: worker_class="sync" sem threads.

    Dockerfile para Flask/Django com Gunicorn

    Imagem de produção com Gunicorn configurado:

    dockerfile
    # 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
    # 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:

    python
    # 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
    Dica
    SECURE_SSL_REDIRECT=True força HTTPS para todas as requisições. Combine com o header Strict-Transport-Security no Nginx para garantia total. Nunca use DEBUG=True em produção — expõe detalhes de erro e stack traces para o usuário.

    $ 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.

    Perguntas frequentes