Deploy do primeiro app

Suba uma aplicação Node.js com banco Postgres em 50 linhas de YAML. Inclui health check, rolling deploy e rollback.

Deploy·10 min·

Um deploy no HeroCtl é um arquivo YAML enviado ao cluster. O cluster decide onde rodar, quando atualizar e como reagir se algo quebra. Você só descreve o desejo.

Anatomia do job spec

Um job define um serviço completo: imagem, réplicas, recursos, ingresso, segredos. Tudo em um arquivo só. Cinco blocos importam:

BlocoFunção
metanome, versão, tags
taskimagem, comando, env, recursos
countquantas réplicas
healthcomo saber se está vivo
ingressdomínio público e TLS

50 linhas cobrem 90% dos casos.

Exemplo completo: web Node.js + Postgres

Vamos subir uma API Node.js com 2 réplicas e um Postgres adjacente. Crie o arquivo app.yaml:

job: api-vendas
version: 1

tasks:
  - name: postgres
    image: postgres:16-alpine
    count: 1
    resources:
      cpu: 500
      memory: 512
    env:
      POSTGRES_DB: vendas
      POSTGRES_USER: app
    secrets:
      POSTGRES_PASSWORD: db-password
    volumes:
      - name: pgdata
        path: /var/lib/postgresql/data
        size: 10Gi
    health:
      tcp: 5432
      interval: 10s
      timeout: 3s

  - name: web
    image: minhaempresa/api-vendas:1.4.2
    count: 2
    resources:
      cpu: 250
      memory: 256
    env:
      DATABASE_URL: postgres://app@postgres.local:5432/vendas
      NODE_ENV: production
    secrets:
      DATABASE_PASSWORD: db-password
      JWT_SECRET: jwt-secret
    health:
      http: /healthz
      port: 3000
      interval: 5s
      healthy_after: 2
      unhealthy_after: 3
    ingress:
      host: api.minhaempresa.com
      port: 3000
      tls: true

Cinquenta linhas, um app inteiro com banco persistente, segredos injetados, health check e domínio com certificado.

Nota: segredos referenciados (db-password, jwt-secret) precisam existir antes. Crie com heroctl secret create db-password --value '...'. Veja Referência do CLI para todos os comandos.

Submeter o job

Com o arquivo pronto, um comando envia o desejo ao cluster:

heroctl job submit app.yaml

Saída:

job:       api-vendas
version:   1 (new)
tasks:     2 (postgres, web)
plan:
  + create alloc postgres-a1b2  on node-2
  + create alloc web-c3d4       on node-1
  + create alloc web-e5f6       on node-3
deploy:    rolling
status:    accepted (id: dep-2026-04-26-001)

O cluster planejou onde cada contêiner vai rodar e iniciou a execução em segundo plano. O comando volta em 1–2 segundos. Não espera o app subir.

Acompanhar o progresso

Para ver o que está acontecendo de fato:

heroctl alloc list --job api-vendas
ALLOC          TASK      NODE     STATUS    HEALTH    AGE
postgres-a1b2  postgres  node-2   running   healthy   12s
web-c3d4       web       node-1   running   starting  8s
web-e5f6       web       node-3   running   healthy   8s

Os estados que importam:

StatusSignifica
pendingaguardando recursos no nó
running + startingcontêiner subiu, health check ainda não passou
running + healthyrecebendo tráfego
failedcrashou. Veja logs.

Logs em tempo real

Para ver a saída do app enquanto sobe:

heroctl logs -f --job api-vendas --task web

A flag -f segue o stream. Saia com Ctrl+C. Para um alloc específico:

heroctl logs -f --alloc web-c3d4

Logs ficam armazenados por 7 dias por padrão. Para histórico longo, integre com um destino externo (veja Observabilidade).

Health check é obrigatório

O HeroCtl não rola deploy sem health check. Não é restrição cosmética: sem ele, não há como distinguir contêiner subindo de contêiner quebrado.

Sua aplicação precisa expor um endpoint que:

  1. Retorna 200 OK apenas quando o app está pronto para receber tráfego.
  2. Valida dependências reais (banco, cache, fila).
  3. Responde em menos de 1 segundo.

Exemplo mínimo em Node.js:

app.get('/healthz', async (req, res) => {
  try {
    await db.query('SELECT 1')
    res.status(200).json({ ok: true })
  } catch (err) {
    res.status(503).json({ ok: false, error: err.message })
  }
})

Atenção: um /healthz que sempre retorna 200 é pior do que não ter. Ele esconde quebras e o deploy passa achando que está tudo bem.

Rolling deploy padrão

Sem configuração extra, atualizações são rolling. O cluster troca uma réplica por vez, espera ela ficar saudável, e só então mexe na próxima.

Defaults do rolling:

ParâmetroValorO que faz
max_parallel1quantas réplicas atualizar ao mesmo tempo
min_healthy_time10squanto tempo a nova precisa ficar saudável
healthy_deadline300squanto esperar antes de considerar falha
auto_reverttruevolta sozinho se falhar

Para customizar, adicione um bloco update no task:

update:
  max_parallel: 2
  min_healthy_time: 30s
  healthy_deadline: 600s
  auto_revert: true

Mais paralelismo = deploy rápido + janela maior de risco. Mais tempo saudável = deploy lento + confiança maior.

Atualizar o app

Mudou código, fez build, push da imagem com tag nova. Para promover:

  1. Edite app.yaml mudando a tag da imagem (1.4.21.4.3).
  2. Submeta de novo:
heroctl job submit app.yaml

O cluster compara as duas versões, percebe que só a imagem do web mudou e roda rolling apenas naquele task. O Postgres continua intocado.

Acompanhe com:

heroctl deploy status dep-2026-04-26-002
# task: web
# strategy: rolling
# progress: 1/2 (50%)
# state: rolling
# next: web-e5f6 (em 8s)

Rollback

Se algo dá errado e você precisa voltar:

heroctl job revert api-vendas --version 1

O comando reaplica o spec da versão anterior, com o mesmo rolling. Em 30–60 segundos você está de volta ao estado bom conhecido. Não há "rollback de banco" — schema migrations são responsabilidade da aplicação.

Nota: versões antigas ficam guardadas indefinidamente. Liste com heroctl job history api-vendas para ver todas as versões e quem submeteu cada uma.

Próximos passos

#deploy#primeiros-passos#yaml#rolling