Esse modelo é o que roda com
make up no desenvolvimento local e com o chart de Helm no Kubernetes. Se você usa make up, não precisa fazer nada manual — a stack do Compose bloqueia a app até o runner de migração terminar.Por que um runner dedicado
O migrador in-process anterior rodava a cada boot da app a partir do diretório de trabalho. Sob um pod security context endurecido (
distroless:nonroot, runAsUser: 1000, readOnlyRootFilesystem: true, capabilities: drop [ALL]), esse caminho falhava com permission denied e causava crash-loop no pod.
O runner dedicado elimina isso:
- As migrações rodam uma única vez, como uma etapa separada, antes da app subir.
- A imagem da app não empacota mais SQL de migração e nunca escreve no schema.
- A própria imagem do runner roda limpa sob o mesmo security context endurecido (veja Postura de segurança).
Imagens do runner
Duas imagens são publicadas por release:
As duas imagens compartilham o mesmo formato:
- Base:
migrate/migratev4.19.1(pinado). - Roda como
USER 65532:65532— não-root, agnóstica ao UID. - Entrypoint POSIX shell que monta um DSN a partir de variáveis de ambiente, executa
migrate ... upe sai. - Não escreve nada em disco — o progresso é rastreado na tabela
schema_migrationsdo Postgres, entãoreadOnlyRootFilesystem: trueé seguro.
Contrato de ambiente
Cada entrypoint aceita um override de URL pronto ou as variáveis
DB_* individuais. O override de URL tem prioridade quando definido.
Runner do Ledger
Runner do Tracer
Formato do DSN
Quando o entrypoint monta o DSN a partir das variáveisDB_*, ele produz:
% @ : / ? # & + space [ ]) não quebrem o DSN. % é codificado primeiro para que escapes já inseridos não sejam duplo-codificados.
O entrypoint só registra marcadores de fase (applying onboarding migrations, applying transaction migrations, applying tracer migrations, migrations complete). Ele nunca imprime credenciais nem o DSN montado.
Desenvolvimento local
make up
Na raiz do repositório, make up:
- Sobe a infra e aguarda até o Postgres ficar healthy.
- Executa os serviços one-shot
ledger-migrateetracer-migrateno Compose. - Sobe cada app, que declara
depends_ondo seu serviço de migração comcondition: service_completed_successfully.
Targets make no host
Se você quiser rodar migrações direto contra um banco local — por exemplo enquanto desenvolve uma migração nova — cada componente expõe targets Make que usam o CLI pinado 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
Você raramente precisa invocar a imagem do runner manualmente — a gate do Compose faz isso por você. Quando precisar, aponte o runner para seus bancos comDB_* (ou *_DATABASE_URL) e execute uma vez:
0 em caso de sucesso (incluindo o caso idempotente de no-op) e com não-zero em caso de falha.
Deploy no Kubernetes
Em produção, execute cada runner de migração como um Job do Kubernetes (tipicamente um PreSync Job do Argo CD ou um hook do Helm) que termine antes do rollout da app. O chart de Helm já conecta isso para você; as seções a seguir cobrem propriedades que você deve manter em mente se escrever seus próprios manifests.
Para deploys multi-tenant, o runner é intencionalmente agnóstico ao tenant: ele migra os bancos apontados pelo seu ambiente, uma vez. O fan-out por tenant é responsabilidade do deploy — rode o Job uma vez por banco de tenant, passando os valores
DB_* (ou o override de URL) correspondentes.Postura de segurança
O runner foi desenhado para um pod security context endurecido:- Não-root — a imagem fixa
USER 65532:65532. - Agnóstica ao UID — os arquivos de migração pertencem a root e são de leitura pública, então qualquer UID pode lê-los.
- Compatível com
readOnlyRootFilesystem—migratenão escreve estado no filesystem.
runAsNonRoot: true, readOnlyRootFilesystem: true e capabilities: drop [ALL].
TLS
Os entrypoints honram osslmode do ambiente (DB_ONBOARDING_SSLMODE, DB_TRANSACTION_SSLMODE ou DB_SSL_MODE), com default disable. Deploys de produção devem configurar require (ou mais estrito). O runner shell não reproduz o enforcement de TLS em código, a classificação de erros nem a telemetria que as apps usam para as próprias conexões — o TLS das migrações é controlado inteiramente via sslmode, em troca de um runner mínimo e sem dependências.
Convenções e idempotência
- Pares up/down — toda migração tem um arquivo
.up.sqle um.down.sql. - Idempotente — rodar
migrate upquando o schema já está na versão mais recente é no-op. Re-executar o runner após um apply parcial ou completo é seguro. - CLI pinado — as imagens do runner e os targets Make no host pinam
golang-migratev4.19.1.
Atualização a partir de versões anteriores
Se você vinha de uma release do Midaz em que a app migrava no boot, nenhuma migração manual de dados é necessária — o schema em disco é o mesmo. Para adotar o novo modelo:
- Traga a release que inclui as imagens do runner.
- No Kubernetes, adicione os Jobs
midaz-ledger-migrationsemidaz-tracer-migrationsao seu pipeline de deploy para rodarem antes do rollout da app. Se você usa o chart oficial de Helm, isso já é feito para você. - No desenvolvimento local,
make upcontinua funcionando igual — a stack do Compose agora bloqueia as apps nos novos serviços one-shot de migração automaticamente.
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 deployment — visão geral do deployment BYOC.
- Variáveis de ambiente do Tracer — referência completa de configuração do Tracer.

