Esse modelo é o que roda sob
make up no desenvolvimento local e sob o Helm chart no Kubernetes. Se você usa make up, não precisa de nenhuma etapa manual. O compose trava a aplicação no runner de migração.Por que um runner dedicado
O antigo migrador embutido no processo rodava a cada inicialização da aplicação, a partir do diretório de trabalho. Sob um contexto de segurança de pod reforçado (
distroless:nonroot, runAsUser: 1000, readOnlyRootFilesystem: true, capabilities: drop [ALL]), esse caminho falhava com permission denied e entrava em crash loop no pod.
O runner dedicado evita isso:
- As migrações rodam uma vez, como uma etapa separada, antes de a aplicação iniciar.
- A imagem da aplicação não inclui mais o SQL de migração e nunca escreve no esquema.
- A própria imagem do runner roda sem problemas sob o mesmo contexto de segurança reforçado (veja Postura de segurança).
Imagens do runner
Cada release inclui duas imagens:
As duas imagens compartilham a mesma estrutura:
- Base:
migrate/migratev4.19.1(fixada). - Roda como
USER 65532:65532(non-root, independente de UID). - Entrypoint em shell POSIX que monta uma DSN a partir de variáveis de ambiente, executa
migrate ... upe encerra. - Não escreve nada em disco. O Postgres controla o progresso das migrações na tabela
schema_migrations, entãoreadOnlyRootFilesystem: trueé seguro.
Contrato de ambiente
Cada entrypoint aceita um override de URL pronto ou as variáveis individuais
DB_*. O override de URL tem precedência quando definido.
Runner do Ledger
Runner do Tracer
Formato da DSN
Quando o entrypoint monta a DSN a partir das variáveisDB_*, ele produz:
% @ : / ? # & + space [ ]) não quebrem a DSN. O % é codificado primeiro para que escapes já inseridos não sejam codificados duas vezes.
O entrypoint registra apenas marcadores de fase (applying onboarding migrations, applying transaction migrations, applying tracer migrations, migrations complete). Ele nunca imprime credenciais nem a DSN montada.
Desenvolvimento local
make up
A partir da raiz do repositório, make up:
- Inicia a infraestrutura e aguarda até que o Postgres esteja saudável.
- Inicia os serviços de compose
ledger-migrateetracer-migrate, que rodam uma única vez. - Inicia cada serviço de aplicação, que usa
depends_onpara seu serviço de migração comcondition: service_completed_successfully.
Targets de make no host
Cada componente expõe targets de Make no host, que rodam migrações diretamente contra um banco de dados local, por exemplo quando você desenvolve uma nova migração. Os targets usam a CLI fixa do golang-migrate e leem as configurações de conexão de .env.
Ledger (components/ledger):
Tracer (
components/tracer):
Exemplo: rodar o runner do Ledger manualmente
Raramente você precisa invocar a imagem do runner manualmente, porque o travamento do compose faz isso por você. Quando precisar, aponte o runner para seus bancos de dados viaDB_* (ou *_DATABASE_URL) e rode uma vez:
0 em caso de sucesso (incluindo o caso idempotente de no-op) e com código diferente de zero em caso de falha.
Deploy no Kubernetes
Em produção, rode cada runner de migração como um Job do Kubernetes (normalmente um PreSync Job do Argo CD ou um hook do Helm) que é concluído antes do rollout da aplicação. O Helm chart já configura isso para você. As seções abaixo cobrem as propriedades que importam se você criar seus próprios manifestos.
Para deploys multi-tenant, o runner é intencionalmente independente de tenant: ele migra os bancos de dados apontados pelo seu ambiente, uma vez. O fan-out por tenant é uma responsabilidade do deploy. Rode o Job uma vez por banco de dados de tenant, com os valores de
DB_* (ou override de URL) daquele tenant.Postura de segurança
O runner funciona sob um contexto de segurança de pod reforçado:- Non-root: a imagem define
USER 65532:65532. - Independente de UID: os arquivos de migração pertencem ao root e têm permissão de leitura para todos, então qualquer UID pode lê-los.
- Seguro para
readOnlyRootFilesystem: omigratenão escreve nenhum estado no sistema de arquivos.
runAsNonRoot: true, readOnlyRootFilesystem: true e capabilities: drop [ALL].
TLS
Os entrypoints respeitam osslmode definido no ambiente (DB_ONBOARDING_SSLMODE, DB_TRANSACTION_SSLMODE ou DB_SSL_MODE), com padrão disable. Recomenda-se que deploys de produção definam require (ou algo mais restritivo). O runner em shell não reproduz a aplicação de TLS em código, a classificação de erros ou a telemetria que as aplicações usam para suas próprias conexões. Você controla o TLS das migrações apenas por meio do sslmode, em troca de um runner mínimo e sem dependências.
Convenções e idempotência
- Pareadas up/down: cada migração tem um arquivo
.up.sqle um.down.sql. - Idempotente: rodar
migrate upquando o esquema já está na versão mais recente é um no-op. Rodar o runner novamente após uma aplicação parcial ou completa é seguro. - CLI fixa: as imagens do runner e os targets
makedo host fixam todos ogolang-migratena versãov4.19.1.
Atualizando a partir de versões anteriores
Você não precisa de nenhuma migração manual de dados ao atualizar a partir de uma versão do Midaz em que a aplicação migrava na inicialização. O esquema em disco é o mesmo. Para adotar o novo modelo:
- Baixe o release que inclui as imagens do runner.
- No Kubernetes, adicione os Jobs
midaz-ledger-migrationsemidaz-tracer-migrationsao seu pipeline de deploy para que rodem antes do rollout da aplicação. Se você usa o Helm chart oficial, isso já é feito para você. - No desenvolvimento local, o
make upcontinua funcionando como antes. O compose agora trava automaticamente as aplicações nos novos serviços de migração de execução única.
Páginas relacionadas
- Instalando o Midaz: configuração local completa com
make up. - Atualizando o Midaz: fluxos de atualização para local e Helm.
- Estratégias de deploy: visão geral do deploy BYOC.
- Variáveis de ambiente do Tracer: referência completa de configuração do Tracer.

