Voltar ao blog Tutoriais

Como fazer deploy de FastAPI com Docker no Brasil

Guia prático para publicar uma API FastAPI em container no Brasil, com uvicorn, variáveis de ambiente, health check e HTTPS usando a Guara Cloud.

9 min de leitura

Por Guara Cloud Editorial

Testado com Python 3.12 / FastAPI 0.115 / Docker / Guara Cloud

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 PORT do ambiente. A plataforma define a porta, não o contrário.
  • Adicione um endpoint /health que 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-settings para gerenciar variáveis de ambiente com tipagem. É mais seguro que acessar os.environ direto.
  • Instale dependências com pip install --no-cache-dir e copie o requirements.txt antes 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.

Dockerfile
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:

entrypoint.sh
#!/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

  1. Acesse app.guaracloud.com e crie um novo projeto
  2. Clique em "Novo Serviço" e selecione "Container"
  3. Conecte o repositório Git ou informe a URL da imagem Docker
  4. Defina a porta HTTP na configuração do serviço
  5. Configure as variáveis de ambiente no painel (DATABASE_URL, LOG_LEVEL, WORKERS)
  6. Escolha o plano de recursos (CPU e memória)
  7. 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.

Criar conta grátis