Objetivos de aprendizado
Depois de ler este artigo, você vai conseguir:
- Escolher o servidor de produção certo para cada framework
- Preparar e iniciar um app FastAPI, Flask ou Django fora do seu computador
- Corrigir os erros mais comuns de um app Python recém-publicado
Nesta página
Os três frameworks trazem um servidor embutido que sobe com um comando, e é aí que mora o erro mais comum: levar esse servidor para produção. Ele foi feito para desenvolver. Para publicar, o app precisa de um servidor de aplicação (Uvicorn ou Gunicorn) que escute na interface certa e leia a configuração do ambiente.
Este guia mostra o mínimo de cada framework, o comando de produção correspondente e os ajustes que o Django exige. O raciocínio geral de publicar uma API está em como hospedar uma API.
Qual servidor de produção cada framework usa?#
A diferença está na interface entre o framework e o servidor:
| Framework | Interface | Servidor de produção | Comando típico |
|---|---|---|---|
| FastAPI | ASGI | Uvicorn (vem com fastapi[standard]) |
fastapi run ou uvicorn main:app |
| Flask | WSGI | Gunicorn | gunicorn 'app:app' |
| Django | WSGI (ou ASGI) | Gunicorn | gunicorn meusite.wsgi |
O Gunicorn não roda no Windows (só no WSL). Servidores de produção costumam ser Linux, então isso afeta só quem testa localmente.
Como hospedar um app FastAPI?#
Instale com os extras padrão, que incluem o Uvicorn:
pip install "fastapi[standard]"main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health():
return {"status": "ok"}Em produção, sem --reload (a documentação do FastAPI avisa que ele consome mais recursos e é mais instável):
uvicorn main:app --host 0.0.0.0 --port "${PORT:-8000}" --workers 2main:app significa "o objeto app do arquivo main.py". Cada worker é um processo separado, e o código de inicialização roda uma vez por worker. O fastapi run main.py é a alternativa curta: também escuta em 0.0.0.0, na porta 8000 por padrão.
Como hospedar um app Flask?#
pip install flask gunicornapp.py:
import os
from flask import Flask
app = Flask(__name__)
app.config["SECRET_KEY"] = os.environ["SECRET_KEY"]
@app.get("/health")
def health():
return {"status": "ok"}Inicie com o Gunicorn:
gunicorn -w 4 -b "0.0.0.0:${PORT:-8000}" 'app:app'O argumento app:app segue o formato {módulo}:{variável}. Com o padrão application factory, use 'app:create_app()'. O -w define o número de processos, e a documentação do Flask sugere CPU * 2 como ponto de partida. Sem -b, o Gunicorn escuta em 0.0.0.0 na porta da variável PORT, se ela existir; sem PORT, escuta só em 127.0.0.1:8000, e o app parece fora do ar. Passar -b deixa o endereço explícito nos dois casos.
Como hospedar um app Django?#
O Django exige mais ajustes: com DEBUG ligado, ele expõe código e configuração, e em produção ninguém serve os arquivos estáticos por você.
pip install django gunicorn whitenoiseNo settings.py, leia do ambiente o que muda entre desenvolvimento e produção:
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ["SECRET_KEY"]
DEBUG = os.environ.get("DEBUG") == "1"
ALLOWED_HOSTS = os.environ["ALLOWED_HOSTS"].split(",")
CSRF_TRUSTED_ORIGINS = [
o for o in os.environ.get("CSRF_TRUSTED_ORIGINS", "").split(",") if o
]
STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"
},
}
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware", # logo após o SecurityMiddleware
# ... demais middlewares
]Com ALLOWED_HOSTS=meuapp.exemplo.com e CSRF_TRUSTED_ORIGINS=https://meuapp.exemplo.com, o domínio público fica liberado. O CSRF_TRUSTED_ORIGINS leva o esquema (https://) e vale para requisições POST vindas de outra origem.
Antes de iniciar, reúna os estáticos e rode as migrações:
python manage.py collectstatic --noinput
python manage.py migrate
gunicorn meusite.wsgi -b "0.0.0.0:${PORT:-8000}"O WhiteNoise serve os arquivos de STATIC_ROOT direto do processo Python, então você dispensa um servidor web só para eles. Por fim, rode python manage.py check --deploy com as configurações de produção: ele aponta o que falta.
Se o proxy da hospedagem termina o HTTPS e repassa HTTP ao Django, o request.is_secure() devolve False. A saída é SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https"), mas a documentação do Django só autoriza isso se o proxy descartar o X-Forwarded-Proto enviado pelo cliente e só o definir em requisições que chegaram por HTTPS. Sem essa garantia, mantenha None.
Como saber se o deploy deu certo?#
Teste de fora do servidor, com o endereço público:
curl -i https://meuapp.exemplo.com/healthVocê espera HTTP/2 200 e o JSON {"status":"ok"}. No Django, crie uma view simples em /health ou abra a página inicial. Se a resposta for outra, leia os logs do processo: o traceback do Python mostra a linha exata.
Quais são os erros mais comuns?#
A conexão é recusada ou expira. O servidor escuta em 127.0.0.1 ou numa porta diferente da exposta. Passe --host 0.0.0.0 (Uvicorn) ou -b 0.0.0.0:PORTA (Gunicorn).
O Django responde Bad Request (400). O domínio não está em ALLOWED_HOSTS. Com DEBUG = False e a lista sem o domínio (ou vazia), o Django responde 400 a todas as requisições.
Formulário do Django falha com 403 (CSRF verification failed). A origem do site não está em CSRF_TRUSTED_ORIGINS, ou o Django não sabe que a conexão original era HTTPS. Confira a lista e o cabeçalho do proxy.
O CSS do admin do Django não carrega. Faltou o collectstatic ou o WhiteNoise ficou fora do MIDDLEWARE. Rode o collectstatic a cada deploy.
KeyError: 'SECRET_KEY'. A variável de ambiente não foi definida no servidor. A falha é proposital: melhor não subir do que subir sem segredo. Veja o que são variáveis de ambiente.
ModuleNotFoundError. Uma dependência ficou fora do requirements.txt. Gere o arquivo com pip freeze > requirements.txt dentro do ambiente virtual do projeto.
O app reinicia sozinho ou fica lento. Muitos workers para pouca RAM: cada um é um processo com a sua cópia do app. Reduza --workers ou aumente a memória.
O que mais cuidar antes de abrir ao público?#
- Banco de dados: um arquivo SQLite local ao lado do app é frágil quando há vários workers e quando o disco do servidor é trocado. Para uso real, prefira PostgreSQL ou MySQL; veja como hospedar um banco de dados.
- Migrações: rode
migrate(Django) ou o seu sistema de migração a cada deploy, antes de liberar o tráfego novo. - CORS: no FastAPI, use o
CORSMiddlewarecom origens exatas; em Flask e Django, os pacotesflask-corsedjango-cors-headers. - Domínio próprio: aponte o domínio para o app e atualize
ALLOWED_HOSTSjunto.
Como fazer isso na Square Cloud#
Os comandos de produção deste guia viram o comando de início da aplicação na Square Cloud. Você envia o projeto por ZIP no painel, pela CLI, pelo GitHub ou pela extensão do VS Code; no start, a plataforma roda pip install -r requirements.txt e define a variável PORT. O arquivo squarecloud.app é opcional: o painel o gera no deploy. No campo de comando de início do formulário, use o servidor do seu framework:
- FastAPI:
uvicorn main:app --host 0.0.0.0 --port $PORT --workers 2 - Flask:
gunicorn -w 2 -b "0.0.0.0:$PORT" 'app:app' - Django:
python manage.py collectstatic --noinput && python manage.py migrate && gunicorn meusite.wsgi -b "0.0.0.0:$PORT"
Como não há etapa de build, o Django reúne os estáticos e aplica as migrações ali mesmo, a cada início. Todo plano dá à aplicação web um subdomínio *.squareweb.app com TLS automático, e é esse host que entra nas variáveis do Django: ALLOWED_HOSTS=meuapp.squareweb.app e CSRF_TRUSTED_ORIGINS=https://meuapp.squareweb.app, ao lado do SECRET_KEY. As variáveis ficam no painel, já existem durante a instalação e valem no próximo restart, sem rebuild.
Cada worker ocupa memória, e uma aplicação web usa no mínimo 512 MB da RAM do plano; o Hobby começa em R$ 12,49 por mês com 1 GB. Se os workers estourarem a memória, a aplicação para até você aumentar a RAM ou reduzir os workers. O reinício automático, ligado por padrão no deploy pelo painel, cobre só os processos que encerram com erro. O disco persiste entre deploys, mas o SQLite continua frágil com vários workers: a hospedagem de PostgreSQL e MySQL começa no Standard (a partir de R$ 44,99 por mês), no mesmo datacenter das aplicações.
Veja os planos em hospedagem de APIs e o runtime em Python.
Hospedagem de APIs
APIs em containers sempre ligados, sem cold start
Perguntas frequentes
Posso usar o servidor de desenvolvimento do Flask ou o runserver do Django em produção?
Não. Eles servem para desenvolver, não para carga real nem para segurança. Em produção, use Gunicorn (Flask e Django) ou Uvicorn (FastAPI).
Quantos workers devo usar?
Comece com poucos e ajuste pelo uso real. A documentação do Flask sugere partir de CPU * 2 no Gunicorn, mas cada worker é um processo com a sua própria memória, então o limite de RAM também conta.
Por que o Django responde 400 (Bad Request) depois do deploy?
Com DEBUG desligado, o Django só aceita os domínios listados em ALLOWED_HOSTS. Adicione à lista o host que aparece na URL.
Preciso do Nginx na frente do Gunicorn?
Num servidor próprio (VPS), a documentação do Flask recomenda um proxy reverso na frente do Gunicorn. Em plataformas que já entregam HTTPS e roteamento, essa camada já vem pronta.
Mais em APIs
Ver todos os 15 artigos- Como hospedar uma APIHospedar uma API é rodar o servidor HTTP 24 horas num lugar com IP público e HTTPS. Veja o passo a passo com Node.js e Python, testes e erros comuns.
- Como hospedar uma aplicação Node.jsPara hospedar uma aplicação Node.js, prepare o package.json, leia porta e segredos do ambiente, escolha uma versão LTS e rode com node. Veja o passo a passo.
- O que é cron job e como agendar tarefasCron job é uma tarefa que roda sozinha em horários definidos por uma expressão de 5 campos. Veja a sintaxe, exemplos, erros comuns e como agendar em Node.js.
- Checklist para colocar uma API em produçãoChecklist para colocar uma API em produção: variáveis de ambiente, health check, shutdown limpo, erros padronizados, autenticação, rate limit, CORS e logs.