Migração antes da troca do container

Desde a v2.174.0, o .deploy.yml aceita release:: um comando que roda depois de a imagem estar pronta e antes de o container novo subir. O uso comum é a migração do banco.

Se o comando falha, o deploy aborta e o container que está no ar não é tocado.

Exemplo: Prisma

O Dockerfile tem um estágio builder com o CLI do Prisma e o schema. O estágio final da imagem não precisa levar essas dependências.

project: crm
system: sys
type: docker-build
port: 3000

build:
  context: .
  dockerfile: Dockerfile
  target: runtime          # estágio final, o que sobe em produção

release:
  target: builder          # estágio com o CLI do Prisma
  command: ["npx", "prisma", "migrate", "deploy"]
  retries: 4
  retry_delay: 10s
  timeout: 10m

environment:
  DATABASE_URL: "{{::DATABASE_URL}}"

Aqui o build.target escolhe o estágio da app e o release.target escolhe o estágio da migração. O runner builda o segundo como crm:<versão>-release, roda o comando e apaga a imagem no fim, com sucesso ou falha.

O que o runner faz

  1. Builda ou puxa a imagem da app.
  2. Roda o release: num docker run --rm, com o mesmo env e secrets da instância, as mesmas redes e o mesmo hosts: da app. Sem labels do Traefik.
  3. Só então sobe o candidato e faz a troca.

O log ganha a fase [5/8] RELEASE. Sem release: a numeração segue [n/7].

command aceita lista (exec direto) ou string (vira sh -c). Prefira a lista: não passa por shell.

Retentativa

retries: 4 são quatro tentativas depois da primeira, cinco no total, com retry_delay entre elas. Serve para o banco que ainda está subindo ou para um lock de migração. timeout vale por tentativa: estourou, o runner mata o container (docker kill) e conta a tentativa. Os padrões são retries: 0, retry_delay: 10s e timeout: 10m.

Quando falha

Depois da última tentativa o deploy termina com:

release_falhou: exit 1 depois de 5 tentativas — <últimas 20 linhas do stderr>
  • O container no ar continua o mesmo, na versão anterior. O symlink current é devolvido.
  • O diagnóstico e a notificação saem com a fase release.
  • O stderr e o comando logado passam pela máscara de segredos: os valores do env da app e os tokens por padrão. Isso inclui valores que não são segredo, como production.

Uma migração que falhou no meio pode ter aplicado parte das mudanças. O runner não desfaz isso.

Rollback

runner rollback não roda o release. Ele volta a imagem, não o banco. Se a versão anterior não funciona com o schema novo, a migração reversa é decisão sua.

Um runner deploy <app> --force da mesma versão roda o release de novo. Escreva a migração para ser idempotente (o prisma migrate deploy já é).

Por instância

instances:
  staging:
    release:
      retries: 1

O campo declarado na instância substitui o do app e o ausente herda.

Limitações

  • O container de release não monta os volumes: da app. Em image_mode: bind-mount, o código montado em /app não existe lá.
  • target: exige build:. Com image_mode: prebuilt ou source registry, o manifesto é recusado com release_target_sem_build; use release: sem target, que roda na imagem do deploy.
  • Com healthcheck.mode: exit o release não roda: não há troca de container.
  • A migração ocupa uma thread do runner durante a execução, até o timeout.

Veja também a referência do `.deploy.yml`.

By Borlot.com.br on 28/10/2026