🚀 Oferta especial: 60% OFF no CrazyStack - Últimas vagas!Garantir vaga →
MÓDULO 2 - AULA 9

Deploy e Produção

Colocando o Restaurantix em produção com Docker, Fly.io e monitoramento

40 minutos
Avançado
Deploy do Restaurantix em Produção

O Restaurantix está configurado para deploy automático no Fly.io usando Docker e GitHub Actions.

Stack de Deploy

  • Docker: Containerização
  • Fly.io: Hosting global
  • PostgreSQL: Banco gerenciado
  • GitHub Actions: CI/CD
  • Secrets: Variáveis seguras

Características

  • Multi-região: Deploy global
  • Auto-scaling: Escala automática
  • Zero-downtime: Deploy sem parada
  • Health checks: Monitoramento
  • SSL/TLS: HTTPS automático
Dockerfile Otimizado para Bun

Dockerfile do Restaurantix

# syntax = docker/dockerfile:1

# Adjust BUN_VERSION as desired
ARG BUN_VERSION=1.2.9
FROM oven/bun:${BUN_VERSION}-slim AS base

LABEL fly_launch_runtime="Bun"

# Bun app lives here
WORKDIR /app

# Set production environment
ENV NODE_ENV="production"

# Throw-away build stage to reduce size of final image
FROM base AS build

# Install packages needed to build node modules
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential pkg-config python-is-python3

# Install node modules
COPY package.json bun.lock ./
RUN bun install

# Copy application code
COPY . .

# Build application with correct target
RUN bun run build

# Clean up and install only production dependencies
RUN rm -rf node_modules && \
    bun install --ci

# Final stage for app image
FROM base

# Copy built application
COPY --from=build /app /app

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD [ "bun", "run", "start" ]

Análise do Dockerfile

// MULTI-STAGE BUILD para otimização

// 1. BASE STAGE
FROM oven/bun:1.2.9-slim AS base
// - Imagem oficial do Bun otimizada
// - Versão slim para menor tamanho
// - Label para Fly.io reconhecer

// 2. BUILD STAGE  
FROM base AS build
// - Instala dependências de build (gcc, python)
// - Copia package.json e bun.lock primeiro (cache layer)
// - Instala dependências
// - Copia código fonte
// - Executa build
// - Limpa node_modules e reinstala só produção

// 3. FINAL STAGE
FROM base
// - Copia apenas o resultado do build
// - Imagem final mínima
// - Expõe porta 3000
// - Comando padrão: bun run start

// VANTAGENS:
// ✅ Imagem final pequena (~50MB vs ~200MB)
// ✅ Cache eficiente das dependências  
// ✅ Sem ferramentas de build na produção
// ✅ Startup rápido do Bun
Configuração do Fly.io

fly.toml

# fly.toml
app = "restaurantix"
primary_region = "gru"  # São Paulo

[build]

[env]
  NODE_ENV = "production"

[http_service]
  internal_port = 3000
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 1
  processes = ["app"]

[[vm]]
  memory = "1gb"
  cpu_kind = "shared"
  cpus = 1

[checks]
  [checks.health]
    grace_period = "10s"
    interval = "30s"
    method = "GET"
    path = "/health"
    port = 3000
    timeout = "5s"
    type = "http"

Comandos de deploy

# 1. Instalar Fly CLI
curl -L https://fly.io/install.sh | sh

# 2. Login no Fly.io
fly auth login

# 3. Criar aplicação
fly launch
# Escolher região: São Paulo (gru)
# Configurar PostgreSQL: Sim

# 4. Configurar secrets (variáveis de ambiente)
fly secrets set DATABASE_URL="postgresql://..."
fly secrets set JWT_SECRET="your-super-secret-key"
fly secrets set CSRF_SECRET_KEY="your-csrf-secret"
fly secrets set RESEND_API_KEY="re_123456789"
fly secrets set API_BASE_URL="https://restaurantix.fly.dev"
fly secrets set AUTH_REDIRECT_URL="https://app.restaurantix.com"

# 5. Deploy
fly deploy

# 6. Verificar status
fly status
fly logs

# 7. Abrir aplicação
fly open
Configuração do Banco de Dados

PostgreSQL no Fly.io

# Criar banco PostgreSQL
fly postgres create --name restaurantix-db --region gru

# Conectar aplicação ao banco
fly postgres attach --app restaurantix restaurantix-db

# Executar migrations
fly ssh console
bun run migrate

# Ou via proxy local
fly proxy 5432 -a restaurantix-db
# Em outro terminal:
DATABASE_URL="postgresql://postgres:password@localhost:5432/restaurantix" bun run migrate

Script de migração

// src/application/infra/db/migrate.ts
import { migrate } from "drizzle-orm/postgres-js/migrator";
import { db, connection } from "./connection";

async function main() {
  console.log("🔄 Running migrations...");
  
  await migrate(db, {
    migrationsFolder: "./drizzle",
  });
  
  console.log("✅ Migrations completed!");
  await connection.end();
}

main().catch((error) => {
  console.error("❌ Migration failed:", error);
  process.exit(1);
});

// package.json
{
  "scripts": {
    "migrate": "bun run src/application/infra/db/migrate.ts",
    "migrate:prod": "NODE_ENV=production bun run migrate"
  }
}
CI/CD com GitHub Actions

.github/workflows/deploy.yml

name: Deploy to Fly.io

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Bun
        uses: oven-sh/setup-bun@v1
        with:
          bun-version: latest
          
      - name: Install dependencies
        run: bun install
        
      - name: Run tests
        run: bun test
        
      - name: Run linter
        run: bun run lint

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Fly CLI
        uses: superfly/flyctl-actions/setup-flyctl@master
        
      - name: Deploy to Fly.io
        run: flyctl deploy --remote-only
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

Configuração de secrets

# GitHub Secrets necessários:
# 1. FLY_API_TOKEN
#    - Obter em: https://fly.io/user/personal_access_tokens
#    - Adicionar em: GitHub > Settings > Secrets and variables > Actions

# 2. Configurar secrets no Fly.io via CLI ou dashboard
fly secrets set DATABASE_URL="postgresql://..."
fly secrets set JWT_SECRET="your-jwt-secret"
fly secrets set CSRF_SECRET_KEY="your-csrf-secret"
fly secrets set RESEND_API_KEY="re_123456789"

# 3. Verificar secrets
fly secrets list

# FLUXO DE DEPLOY:
# 1. Push para main
# 2. GitHub Actions executa testes
# 3. Se testes passam, faz deploy
# 4. Fly.io builda Docker image
# 5. Deploy com zero-downtime
# 6. Health checks verificam se app está ok
Monitoramento e Observabilidade

Health Check Endpoint

// src/http/routes/health.ts
import { Elysia } from "elysia";
import { db } from "@/application/infra/db/connection";

export const health = new Elysia()
  .get("/health", async () => {
    try {
      // Verificar conexão com banco
      await db.execute(sql`SELECT 1`);
      
      return {
        status: "ok",
        timestamp: new Date().toISOString(),
        uptime: process.uptime(),
        memory: process.memoryUsage(),
        version: process.env.npm_package_version || "unknown",
        environment: process.env.NODE_ENV,
        database: "connected"
      };
    } catch (error) {
      return {
        status: "error",
        timestamp: new Date().toISOString(),
        error: error.message,
        database: "disconnected"
      };
    }
  });

// Adicionar no server.ts
app.use(health);

Logs estruturados

// src/utils/logger.ts
export class Logger {
  static info(message: string, meta?: any) {
    console.log(JSON.stringify({
      level: "info",
      message,
      timestamp: new Date().toISOString(),
      ...meta
    }));
  }
  
  static error(message: string, error?: Error, meta?: any) {
    console.error(JSON.stringify({
      level: "error", 
      message,
      error: error?.message,
      stack: error?.stack,
      timestamp: new Date().toISOString(),
      ...meta
    }));
  }
  
  static request(req: Request, responseTime: number) {
    console.log(JSON.stringify({
      level: "info",
      type: "request",
      method: req.method,
      url: req.url,
      responseTime: `${responseTime}ms`,
      timestamp: new Date().toISOString()
    }));
  }
}

// Uso nas rotas
Logger.info("User authenticated", { userId: user.id });
Logger.error("Database connection failed", error);

Comandos de monitoramento

# Verificar status da aplicação
fly status

# Ver logs em tempo real
fly logs

# Métricas de performance
fly metrics

# Conectar via SSH
fly ssh console

# Escalar aplicação
fly scale count 2  # 2 instâncias
fly scale memory 2048  # 2GB RAM

# Verificar health checks
curl https://restaurantix.fly.dev/health

# Monitorar banco de dados
fly postgres connect -a restaurantix-db
🎯 Projeto Final Completo

Parabéns! Você completou o curso de Bun.js. Agora é hora de colocar tudo em produção:

Checklist de Deploy:

  • • ✅ Projeto Restaurantix completo
  • • ✅ Todas as 18 rotas implementadas
  • • ✅ Sistema de autenticação JWT
  • • ✅ Magic links funcionando
  • • ✅ Métricas de dashboard
  • • ✅ Sistema de email
  • • ✅ Dockerfile otimizado
  • • ✅ Deploy no Fly.io
  • • ✅ PostgreSQL configurado
  • • ✅ CI/CD com GitHub Actions
  • • ✅ Monitoramento ativo
  • • ✅ HTTPS e domínio custom

🚀 Próximos Passos:

  • • Adicione testes E2E com Playwright
  • • Implemente cache com Redis
  • • Configure CDN para assets estáticos
  • • Adicione rate limiting avançado
  • • Monitore com Sentry ou DataDog
  • • Implemente backup automático do banco

Parabéns! 🎉

Você dominou Bun.js e construiu um SaaS completo em produção!