> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Migraciones de base de datos

> Ejecuta las migraciones de esquema de Midaz Ledger y Tracer con imágenes de runner dedicadas para que las apps arranquen contra una base ya migrada.

Midaz aplica las migraciones de esquema de PostgreSQL a través de **imágenes de runner dedicadas** — una para Ledger, otra para Tracer — desacopladas de los binarios de la aplicación. Las apps ya no migran en el arranque: se inician contra un esquema que ya fue migrado por el runner.

<Note>
  Este modelo es el que corre con `make up` en desarrollo local y con el chart de Helm en Kubernetes. Si usas `make up`, no necesitas hacer nada manual — el stack de Compose bloquea la app hasta que el runner de migración termine.
</Note>

## Por qué un runner dedicado

***

El antiguo migrador in-process se ejecutaba en cada arranque de la app desde el directorio de trabajo. Bajo un contexto de seguridad de pod endurecido (`distroless:nonroot`, `runAsUser: 1000`, `readOnlyRootFilesystem: true`, `capabilities: drop [ALL]`), esa ruta fallaba con `permission denied` y provocaba crash-loop en el pod.

El runner dedicado evita eso por completo:

* Las migraciones corren **una sola vez, como un paso separado**, antes de que arranque la app.
* La imagen de la app ya no incluye SQL de migración y nunca escribe en el esquema.
* La propia imagen del runner corre limpia bajo el mismo contexto de seguridad endurecido (ver [Postura de seguridad](#postura-de-seguridad)).

## Imágenes del runner

***

Se publican dos imágenes por release:

| Imagen                    | Aplica                                  | Bases de datos                   |
| ------------------------- | --------------------------------------- | -------------------------------- |
| `midaz-ledger-migrations` | Migraciones de onboarding + transaction | Dos: `onboarding`, `transaction` |
| `midaz-tracer-migrations` | Esquema de Tracer                       | Una                              |

Ambas imágenes comparten la misma forma:

* Base: [`migrate/migrate`](https://github.com/golang-migrate/migrate) `v4.19.1` (pineada).
* Se ejecuta como `USER 65532:65532` — no-root, agnóstica al UID.
* Entrypoint POSIX shell que ensambla un DSN a partir de variables de entorno, ejecuta `migrate ... up` y sale.
* No escribe nada en disco — el progreso se guarda en la tabla `schema_migrations` de Postgres, por lo que `readOnlyRootFilesystem: true` es seguro.

El runner de Ledger aplica **primero onboarding y luego transaction**, en una sola invocación. El runner de Tracer aplica su única base en un solo paso.

## Contrato de entorno

***

Cada entrypoint acepta o bien un override de URL prebuilt o las variables `DB_*` individuales. **El override de URL tiene prioridad cuando está definido.**

### Runner de Ledger

| Base de datos | Override de URL            | Ensamblado a partir de                                                                                                                                                         |
| ------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| onboarding    | `ONBOARDING_DATABASE_URL`  | `DB_ONBOARDING_HOST`, `DB_ONBOARDING_PORT` (default `5432`), `DB_ONBOARDING_USER`, `DB_ONBOARDING_PASSWORD`, `DB_ONBOARDING_NAME`, `DB_ONBOARDING_SSLMODE` (default `disable`) |
| transaction   | `TRANSACTION_DATABASE_URL` | `DB_TRANSACTION_*` (misma forma)                                                                                                                                               |

### Runner de Tracer

| Override de URL | Ensamblado a partir de                                                                                        |
| --------------- | ------------------------------------------------------------------------------------------------------------- |
| `DATABASE_URL`  | `DB_HOST`, `DB_PORT` (default `5432`), `DB_USER`, `DB_PASSWORD`, `DB_NAME`, `DB_SSL_MODE` (default `disable`) |

### Forma del DSN

Cuando el entrypoint ensambla el DSN a partir de las variables `DB_*`, produce:

```text theme={null}
postgres://<user>:<encoded-password>@<host>:<port>/<name>?sslmode=<sslmode>
```

El password se codifica en porcentajes para que los caracteres reservados de URI (`% @ : / ? # & + space [ ]`) no rompan el DSN. `%` se codifica primero para no doble-codificar escapes ya insertados.

El entrypoint solo registra marcadores de fase (`applying onboarding migrations`, `applying transaction migrations`, `applying tracer migrations`, `migrations complete`). Nunca imprime credenciales ni el DSN ensamblado.

## Desarrollo local

***

### `make up`

Desde la raíz del repositorio, `make up`:

1. Levanta la infra y espera hasta que Postgres esté healthy.
2. Arranca los servicios one-shot `ledger-migrate` y `tracer-migrate` en Compose.
3. Arranca cada app, que declara `depends_on` sobre su servicio de migración con `condition: service_completed_successfully`.

La app nunca se inicia contra una base sin migrar.

### Targets `make` en el host

Si quieres ejecutar migraciones directamente contra una base local — por ejemplo mientras desarrollas una migración nueva — cada componente expone targets de Make que usan el CLI pineado de `golang-migrate` y leen la configuración de conexión desde `.env`.

Ledger (`components/ledger`):

| Target                     | Efecto                                           |
| -------------------------- | ------------------------------------------------ |
| `make migrate`             | Aplica onboarding y luego transaction (agregado) |
| `make migrate-onboarding`  | Aplica solo onboarding                           |
| `make migrate-transaction` | Aplica solo transaction                          |
| `make migrate-down`        | Revierte ambas bases                             |

Tracer (`components/tracer`):

| Target                 | Efecto                                                          |
| ---------------------- | --------------------------------------------------------------- |
| `make migrate`         | Aplica el esquema de tracer                                     |
| `make migrate-down`    | Revierte el esquema de tracer                                   |
| `make migrate-version` | Imprime la versión actual del esquema                           |
| `make migrate-force`   | Fuerza la versión del esquema (recuperación de un estado dirty) |

### Ejemplo: ejecutar el runner de Ledger a mano

Rara vez necesitas invocar la imagen del runner manualmente — la gate de Compose lo hace por ti. Cuando lo hagas, apunta el runner a tus bases con `DB_*` (o `*_DATABASE_URL`) y ejecútalo una vez:

```bash theme={null}
docker run --rm \
  --network=infra-network \
  -e DB_ONBOARDING_HOST=postgres \
  -e DB_ONBOARDING_PORT=5432 \
  -e DB_ONBOARDING_USER=midaz \
  -e DB_ONBOARDING_PASSWORD='s3cret!' \
  -e DB_ONBOARDING_NAME=onboarding \
  -e DB_ONBOARDING_SSLMODE=disable \
  -e DB_TRANSACTION_HOST=postgres \
  -e DB_TRANSACTION_PORT=5432 \
  -e DB_TRANSACTION_USER=midaz \
  -e DB_TRANSACTION_PASSWORD='s3cret!' \
  -e DB_TRANSACTION_NAME=transaction \
  -e DB_TRANSACTION_SSLMODE=disable \
  lerianstudio/midaz-ledger-migrations:<tag>
```

El runner sale con `0` si tiene éxito (incluido el caso idempotente sin cambios) y con no-cero si falla.

## Despliegue en Kubernetes

***

En producción, ejecuta cada runner de migración como un **Job** de Kubernetes (típicamente un PreSync Job de Argo CD o un hook de Helm) que termine **antes** del rollout de la app. El chart de Helm ya lo conecta por ti; las secciones siguientes cubren las propiedades que debes tener presentes si escribes tus propios manifests.

<Note>
  Para despliegues multi-tenant, el runner es intencionadamente **agnóstico al tenant**: migra las bases apuntadas por su entorno, una vez. El fan-out por tenant es una preocupación del deploy — ejecuta el Job una vez por base de tenant, pasando los valores `DB_*` (o el override de URL) correspondientes.
</Note>

### Postura de seguridad

El runner está diseñado para un contexto de seguridad de pod endurecido:

* **No-root** — la imagen fija `USER 65532:65532`.
* **Agnóstica al UID** — los archivos de migración son propiedad de root y de lectura pública, por lo que cualquier UID puede leerlos.
* **Compatible con `readOnlyRootFilesystem`** — `migrate` no escribe estado en el filesystem.

La misma imagen corre limpia bajo `runAsNonRoot: true`, `readOnlyRootFilesystem: true` y `capabilities: drop [ALL]`.

### TLS

Los entrypoints honran `sslmode` desde el entorno (`DB_ONBOARDING_SSLMODE`, `DB_TRANSACTION_SSLMODE` o `DB_SSL_MODE`), con valor por defecto `disable`. **Los despliegues productivos deberían configurar `require`** (o más estricto). El runner shell no reproduce la aplicación en código de TLS, la clasificación de errores ni la telemetría que las apps usan para sus propias conexiones — el TLS para las migraciones se controla enteramente vía `sslmode`, a cambio de un runner mínimo y sin dependencias.

## Convenciones e idempotencia

***

* **Pares up/down** — cada migración tiene un archivo `.up.sql` y uno `.down.sql`.
* **Idempotente** — ejecutar `migrate up` cuando el esquema ya está en la última versión es un no-op. Re-ejecutar el runner tras un apply parcial o completo es seguro.
* **CLI pineado** — las imágenes del runner y los targets Make en host pinean `golang-migrate` `v4.19.1`.

## Actualización desde versiones anteriores

***

Si venías de una release de Midaz donde la app migraba al arrancar, no se requiere migración manual de datos — el esquema en disco es el mismo. Para adoptar el nuevo modelo:

1. Trae la release que incluye las imágenes del runner.
2. En Kubernetes, agrega los Jobs `midaz-ledger-migrations` y `midaz-tracer-migrations` a tu pipeline de deploy para que corran antes del rollout de la app. Si usas el chart oficial de Helm, esto ya está hecho.
3. En desarrollo local, `make up` sigue funcionando igual — el stack de Compose ahora bloquea las apps sobre los nuevos servicios one-shot de migración de forma automática.

## Páginas relacionadas

***

* [Instalando Midaz](/es/midaz/midaz-setup) — configuración local completa con `make up`.
* [Actualizando Midaz](/es/midaz/updating-midaz) — flujos de actualización para local y Helm.
* [Estrategias de despliegue](/es/midaz/deployment) — visión general del despliegue BYOC.
* [Variables de entorno de Tracer](/es/tracer/tracer-environment-variables) — referencia completa de configuración de Tracer.
