NestJS tem uma pegadinha na hora de deploy que não existe em projetos Express puros: o build do TypeScript precisa rodar antes do container iniciar, e a imagem de produção não deveria carregar o compilador nem os arquivos .ts. Muita gente resolve isso com imagens de 1GB ou com um Dockerfile que copia tudo pra dentro. Dá pra fazer melhor.
Este tutorial cobre o caminho inteiro, do Dockerfile multi-stage até o serviço rodando com HTTPS na Guara Cloud, em infraestrutura no Brasil.
Resposta rápida
Para publicar uma aplicação NestJS no Brasil, compile o TypeScript em um estágio de build do Dockerfile, copie apenas o diretório dist e as dependências de produção para a imagem final, escute na porta definida por ENV PORT e publique o container na Guara Cloud. A plataforma cuida de HTTPS, domínio público, logs e cobra em Real.
Principais pontos
- Use Dockerfile multi-stage. O estágio de build compila TypeScript, o estágio final carrega só
dist/enode_modulesde produção. - Leia
PORTdo ambiente. Não hardcode 3000. - Use o
ConfigModuledo NestJS para ler variáveis de ambiente com tipagem. - Configure segredos pelo painel da Guara Cloud, nunca no repositório.
- Adicione um health check com
@nestjs/terminuspara a plataforma saber quando o serviço está pronto.
Quando este tutorial se aplica
Use este fluxo para APIs REST, GraphQL (com Apollo ou Mercurius) e microsserviços construídos com NestJS. Se a aplicação usa fila (BullMQ), websocket (Gateway) ou gRPC, o container funciona da mesma forma. A diferença fica nas portas expostas e nas variáveis de ambiente.
Quando não usar este fluxo
Se a aplicação é puramente um worker sem porta HTTP (só consome fila), o deploy é similar mas você não precisa do health check HTTP. Se o projeto usa serverless (Lambda, Cloud Functions), este guia não se aplica. Para monorepos com Nx onde vários apps NestJS convivem, ajuste os caminhos de build no Dockerfile para apontar para o app correto.
Antes de começar
- Um projeto NestJS criado com @nestjs/cli (nest new meu-app)
- Node.js 20+ instalado localmente para testes
- Docker instalado para validar a imagem
- Uma conta na Guara Cloud
1. Configure a porta via variável de ambiente
O NestJS padrão escuta em 3000. Em container, a plataforma define a porta. Abra src/main.ts e ajuste:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const port = Number(process.env.PORT ?? 3000);
await app.listen(port, '0.0.0.0');
}
bootstrap(); O 0.0.0.0 é obrigatório. Sem ele, a aplicação escuta apenas no loopback e o balanceador de carga não consegue alcançar o container.
2. Dockerfile multi-stage para NestJS
Aqui está o ponto que separa uma imagem de 50MB de uma de 800MB. O primeiro estágio instala tudo, compila TypeScript e roda os testes se quiser. O segundo estágio copia só o que precisa pra rodar.
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
ENV NODE_ENV=production
CMD ["node", "dist/main.js"] Esse Dockerfile produz uma imagem com aproximadamente 80MB. O npm ci --omit=dev no estágio final garante que TypeScript, ESLint e Jest não vão pra produção.
3. Configure variáveis de ambiente com ConfigModule
O NestJS tem um módulo oficial para tipar e validar variáveis de ambiente. Se ainda não instalou:
npm install @nestjs/config Depois, registre no AppModule:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
})
export class AppModule {} No painel da Guara Cloud, configure as variáveis que a aplicação consome:
Variáveis de ambiente comuns
| Nome | Valor |
|---|---|
NODE_ENV | production |
PORT | 3000 |
DATABASE_URL | postgres://usuario:senha@host:5432/meubanco |
JWT_SECRET | string-aleatoria-longa |
Nunca commite .env no repositório. Use .env.example como referência com valores fictícios.
4. Adicione health check
A Guara Cloud usa probes HTTP para decidir se o container está saudável. Instale o Terminus e crie um endpoint simples:
npm install @nestjs/terminus import { Controller, Get } from '@nestjs/common';
import { HealthCheckService, HealthCheck, HttpHealthIndicator } from '@nestjs/terminus';
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private http: HttpHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.http.pingCheck('ping', 'https://www.google.com'),
]);
}
} Para algo mais simples (sem dependências externas), basta retornar { status: 'ok' } em /health. A plataforma só precisa de um 200 OK.
5. Faça o deploy na Guara Cloud
Com o repositório no GitHub e o Dockerfile na raiz, o processo na Guara Cloud é:
Passo a passo no painel
- Crie um novo serviço e conecte o repositório GitHub
- A plataforma detecta o Dockerfile automaticamente
- Confirme a porta HTTP (o valor de PORT, geralmente 3000)
- Adicione as variáveis de ambiente pelo painel
- Inicie o deploy e acompanhe os logs de build em tempo real
O build roda o Dockerfile completo: instala dependências, compila TypeScript, gera a imagem final. O primeiro deploy leva entre 2 e 4 minutos dependendo do tamanho do projeto. Deploys seguintes são mais rápidos por causa do cache de camadas Docker.
6. Valide o serviço em produção
Após o deploy completar, verifique:
- A URL pública responde com 200.
- O endpoint
/healthretorna status ok. - Os logs mostram a aplicação escutando na porta correta.
- O consumo de CPU e memória está dentro do esperado (no painel da Guara Cloud, aba de observabilidade).
Se algo falhar, os logs de build e de runtime ficam disponíveis no painel. A maioria dos problemas aparece nos primeiros segundos.
Problemas comuns
- Problema O container sobe e morre imediatamente
- Solução Verifique se main.ts escuta em 0.0.0.0 e usa process.env.PORT. Localmente, rode "docker run -e PORT=3000 -p 3000:3000 minha-imagem" para reproduzir.
- Problema Erro de módulo não encontrado (Cannot find module)
- Solução O dist/ não foi copiado para a imagem final. Confirme a linha "COPY --from=builder /app/dist ./dist" no Dockerfile.
- Problema A aplicação conecta no banco local em vez do banco da nuvem
- Solução Verifique se DATABASE_URL está configurada no painel da Guara Cloud e se o ConfigModule está lendo a variável corretamente.
- Problema Build do Docker demora mais de 10 minutos
- Solução Garanta que node_modules e dist estão no .dockerignore. Camadas de npm ci são reutilizadas entre builds quando package.json não muda.
- Problema O health check falha mas a aplicação responde normalmente
- Solução O probe pode estar batendo antes da aplicação inicializar. Aumente o initialDelaySeconds ou simplifique o health check para não depender de recursos externos.
Dicas de performance para NestJS em container
Algumas configurações fazem diferença real quando a aplicação está sob carga:
-
Limite de workers. O NestJS não clusteriza sozinho. Se precisar de múltiplos processos, use o
@nestjs/microservicescom múltiplas réplicas na Guara Cloud (escale horizontalmente) em vez de PM2 dentro do container. -
Lazy loading de módulos. Módulos carregados com
LazyModuleLoaderreduzem o tempo de cold start em aplicações com muitos módulos que nem toda request usa. -
Fastify em vez de Express. Troque o adapter para
@nestjs/platform-fastifyse a API recebe mais de mil requests por segundo. A diferença de throughput é mensurável.
NestJS funciona com Docker sem modificações no projeto?
Quase. Basta garantir que main.ts escute em 0.0.0.0 e use process.env.PORT. O resto (Dockerfile, variáveis, build) é configuração externa ao código NestJS.
Como usar PostgreSQL com NestJS na Guara Cloud?
Crie o PostgreSQL pelo catálogo da Guara Cloud, copie a URL de conexão e configure como DATABASE_URL nas variáveis de ambiente do serviço. Use TypeORM ou Prisma no NestJS para consumir a conexão.
A cobrança é em Real?
Sim. A Guara Cloud cobra em BRL via Stripe. Sem conversão de dólar, sem surpresa no cartão.
Preciso de Nginx na frente do NestJS?
Não. A Guara Cloud entrega HTTPS, domínio público e roteamento sem você configurar Nginx ou certificados TLS manualmente.
Publique sua API NestJS na Guara Cloud
Deploy com Docker, HTTPS gerenciado, logs e cobrança em Real. Infraestrutura em São Paulo.