FastAPI virou a escolha padrão de quem constrói APIs em Python. Validação com Pydantic, documentação automática via OpenAPI, async nativo. Tudo funciona bem no desenvolvimento. O problema começa quando chega a hora de colocar em produção. O uvicorn app:app que você usa no terminal não serve pra servir tráfego real, empacotar a aplicação em container tem algumas pegadinhas e configurar workers e health check é o tipo de coisa que fica esquecida até o primeiro outage.
Este tutorial cobre o caminho completo: Dockerfile enxuto, configuração de produção com uvicorn e gunicorn, health check, variáveis de ambiente e deploy na Guara Cloud com HTTPS em São Paulo.
Resposta rápida
Para fazer deploy de uma API FastAPI no Brasil, crie um Dockerfile com Python slim, defina o comando de execução com gunicorn e uvicorn workers, leia a porta do ambiente via PORT, adicione um endpoint /health e publique o container na Guara Cloud. A plataforma cuida de HTTPS, domínio público e reinício automático.
Principais pontos
- Use gunicorn com uvicorn workers em produção. O uvicorn sozinho funciona, mas o gunicorn gerencia múltiplos processos e faz graceful reload.
- Leia
PORTdo ambiente. A plataforma define a porta, não o contrário. - Adicione um endpoint
/healthque responde 200 quando o serviço está pronto para receber tráfego. - Configure workers baseado na quantidade de CPU disponível. A fórmula geral é
2 * CPUs + 1. - Use
pydantic-settingspara gerenciar variáveis de ambiente com tipagem. É mais seguro que acessaros.environdireto. - Instale dependências com
pip install --no-cache-dire copie orequirements.txtantes do resto do código para aproveitar o cache de camadas do Docker.
Quando este tutorial se aplica
Use este fluxo para APIs REST construídas com FastAPI que rodam como serviço HTTP de longa duração. Se a sua API conecta com PostgreSQL, Redis, RabbitMQ ou qualquer outro serviço externo, o container funciona da mesma forma. A diferença fica nas variáveis de ambiente que você injeta e nas portas que precisa abrir.
Também funciona para aplicações FastAPI que usam WebSockets via Starlette. O uvicorn suporta WebSockets nativamente, e o gunicorn com uvicorn workers mantém essa compatibilidade.
Quando não usar este fluxo
Se o seu projeto usa FastAPI apenas como parte de um monorepo maior com múltiplos serviços Python, ajuste o Dockerfile para instalar as dependências do projeto inteiro e apontar para o módulo correto. Se a aplicação é um worker de processamento de filas (Celery, Dramatiq) sem porta HTTP, o deploy é parecido, mas você não precisa do endpoint de health check HTTP nem do gunicorn. Para esses casos, o worker roda como um comando direto no Dockerfile.
Se a aplicação depende de extensões CPython que compilam native code (por exemplo, numpy com BLAS otimizado ou grpcio-tools), a imagem base pode precisar mudar de slim para bookworm para incluir as bibliotecas do sistema. Isso aumenta o tamanho da imagem de 150MB para uns 400MB, mas não muda o fluxo de deploy.
Antes de começar
- Um projeto FastAPI com pelo menos um endpoint funcional
- Python 3.12+ instalado localmente
- Docker instalado para validar a imagem
- Conta na Guara Cloud
1. Crie o endpoint de health check
A plataforma precisa saber quando o container está pronto para receber requisições. Adicione um endpoint simples que checa se as dependências estão disponíveis:
from fastapi import FastAPI, HTTPException
import asyncio
app = FastAPI()
@app.get("/health")
async def health_check():
try:
await asyncio.sleep(0)
return {"status": "healthy"}
except Exception as e:
raise HTTPException(status_code=503, detail=str(e))
Se a sua API depende de banco de dados, vale a pena checar a conexão no health check. Mas cuidado: se o banco está fora do ar, a plataforma vai ficar reiniciando o container sem parar. Eu prefiro ter dois endpoints separados: /health pra liveness (o processo está vivo) e /ready pra readiness (as dependências estão acessíveis).
2. Configure variáveis de ambiente com pydantic-settings
Em vez de acessar os.environ direto, use pydantic-settings. Isso valida tipos e falha cedo se uma variável obrigatória está faltando.
Instale o pacote:
pip install pydantic-settings
Crie o arquivo de configuração:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "minha-api"
database_url: str = ""
log_level: str = "info"
workers: int = 2
class Config:
env_prefix = ""
settings = Settings()
O Config com env_prefix = "" faz com que as variáveis sejam lidas diretamente (DATABASE_URL, LOG_LEVEL). Se preferir usar prefixo, mude para env_prefix = "APP_" e as variáveis ficam APP_DATABASE_URL, etc.
3. Dockerfile de produção
O Dockerfile tem duas preocupações: tamanho da imagem e velocidade de build. Copiar o requirements.txt antes do código permite que o Docker reuse a camada de dependências quando só o código muda.
FROM python:3.12-slim
WORKDIR /app
# Copia requirements primeiro para cache de camada
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copia o código da aplicação
COPY . .
# Não roda como root
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
ENV PYTHONUNBUFFERED=1
COPY entrypoint.sh .
RUN chmod +x entrypoint.sh
CMD ["./entrypoint.sh"] Algumas coisas que merecem atenção aqui:
O 0.0.0.0 no bind do gunicorn é obrigatório. Sem ele, o gunicorn escuta apenas no loopback e o load balancer da plataforma não alcança o container.
O PYTHONUNBUFFERED=1 garante que os logs apareçam em tempo real, sem buffering. Isso importa muito quando você está debugando algo em produção.
O useradd cria um usuário não-root. Rodar container como root é um risco de segurança bobo e fácil de evitar.
4. Script de entrada com gunicorn e porta dinâmica
Crie um script que lê a porta do ambiente e configura o gunicorn:
#!/bin/sh
PORT=${PORT:-8000}
WORKERS=${WORKERS:-2}
exec gunicorn app.main:app --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:$PORT --workers $WORKERS --timeout 120 --access-logfile - --error-logfile - O --timeout 120 evita que o gunicorn mate workers que demoram mais de 30 segundos (o default). Isso é particularmente importante se a sua API faz chamadas síncronas para serviços externos ou queries pesadas no banco.
O --access-logfile - direciona os access logs para stdout, que é onde a Guara Cloud coleta logs.
5. Quantos workers usar
O número de workers define quantos processos Python rodam em paralelo. Cada worker aceita múltiplas conexões simultâneas quando o uvicorn está em modo async, mas processamento pesado de CPU ainda bloqueia o worker inteiro.
A fórmula é 2 * CPUs + 1. Na Guara Cloud, se o container tem 1 CPU, use 3 workers. Se tem 2 CPUs, use 5.
# No painel da Guara Cloud, adicione a variável:
WORKERS=3
Para APIs que fazem muita I/O (banco de dados, chamadas HTTP para outros serviços), o async do FastAPI já resolve a concorrência dentro de cada worker. Para APIs com processamento de CPU (machine learning, geração de PDFs), workers adicionais ajudam, mas o ideal é mover esse trabalho para um worker de fila separado.
6. Publique na Guara Cloud
Com a imagem pronta, publique o serviço:
Passos para publicar
- Acesse app.guaracloud.com e crie um novo projeto
- Clique em "Novo Serviço" e selecione "Container"
- Conecte o repositório Git ou informe a URL da imagem Docker
- Defina a porta HTTP na configuração do serviço
- Configure as variáveis de ambiente no painel (DATABASE_URL, LOG_LEVEL, WORKERS)
- Escolha o plano de recursos (CPU e memória)
- Faça o deploy
7. Configure variáveis de ambiente
O painel da Guara Cloud tem um editor de variáveis de ambiente para cada serviço.
Variáveis recomendadas
| Variável | Valor |
|---|---|
DATABASE_URL | postgresql://user:***@host:5432/db |
LOG_LEVEL | info |
WORKERS | 3 |
APP_NAME | minha-api |
A Guara Cloud injeta automaticamente a variável PORT quando o serviço é criado. Você não precisa configurar essa manualmente.
Troubleshooting
Problemas comuns
- Problema Container sobe mas retorna 502 Bad Gateway
- Solução O gunicorn não está escutando em 0.0.0.0 ou na porta certa. Verifique se o entrypoint.sh usa --bind 0.0.0.0:$PORT e se a porta configurada no painel bate com a variável PORT. Veja os logs do container para confirmar que o gunicorn iniciou corretamente.
- Problema ImportError: No module named app.main
- Solução O caminho do módulo no comando gunicorn está errado. Se o arquivo se chama src/main.py, o caminho é src.main:app. Ajuste o entrypoint.sh.
- Problema Pip install demora muito no build
- Solução Verifique se o requirements.txt é copiado antes do COPY . . no Dockerfile. Isso permite que o Docker cache a camada de dependências e só reinstale quando requirements.txt mudar.
- Problema Workers morrem com Worker timeout
- Solução Aumente o --timeout do gunicorn. O default é 30 segundos. Se a API faz chamadas externas síncronas ou queries pesadas, 120 segundos é mais seguro.
- Problema Logs não aparecem no painel da Guara Cloud
- Solução Certifique-se de que PYTHONUNBUFFERED=1 está configurado e que os logs do gunicorn vão para stdout/stderr (não para arquivos). A Guara Cloud coleta apenas o que é escrito nos file descriptors padrão do container.
FAQ
Preciso usar gunicorn ou posso rodar só o uvicorn em produção?
Uvicorn sozinho funciona em produção para tráfego baixo (menos de 100 req/s). Acima disso, gunicorn com uvicorn workers é mais estável porque adiciona gerenciamento de processos, graceful restart e respawning automático quando um worker morre.
Quantos workers devo configurar?
A fórmula geral é 2 * CPUs + 1. Para um container com 1 CPU, use 3 workers. Para 2 CPUs, use 5. Monitore o uso de CPU depois do deploy e ajuste conforme necessário.
Posso usar Poetry em vez de pip?
Sim. Exporte as dependências com poetry export -f requirements.txt --output requirements.txt --without-hashes e use o mesmo Dockerfile. Se quiser manter o pyproject.toml no container, instale o Poetry no estágio de build e use poetry install --only main.
Como conecto minha API FastAPI ao PostgreSQL na Guara Cloud?
Crie o serviço de PostgreSQL pelo catálogo da Guara Cloud no mesmo projeto. A plataforma injeta automaticamente as variáveis de conexão (DATABASE_URL, DATABASE_HOST) como variáveis de ambiente no seu serviço FastAPI. Basta ler essas variáveis no pydantic-settings.
O deploy suporta WebSockets com FastAPI?
Sim. O uvicorn suporta WebSockets nativamente, e o gunicorn com uvicorn workers mantém essa compatibilidade. Se a sua API usa WebSocket endpoints via Starlette, o tráfego passa pelo mesmo load balancer da Guara Cloud.
Publique sua API FastAPI no Brasil
HTTPS, domínio, logs e cobrança em Real. Deploy em container com infraestrutura em São Paulo.