> ## 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 PostgreSQL de Midaz Ledger y Tracer con imágenes de runner dedicadas, para que las aplicaciones arranquen contra una base de datos ya migrada.

Midaz aplica las migraciones de esquema de PostgreSQL mediante imágenes de runner de migración dedicadas (una para el Ledger, otra para Tracer), desacopladas de los binarios de la aplicación. Las aplicaciones ya no migran al iniciar. Arrancan contra un esquema que el runner ya migró.

<Note>
  Este modelo es el que se ejecuta bajo `make up` en desarrollo local y bajo el chart de Helm en Kubernetes. Si usas `make up`, no necesitas ningún paso manual. El stack de Compose bloquea la aplicación hasta que se complete el runner de migración.
</Note>

## Por qué un runner dedicado

***

El antiguo migrador en proceso se ejecutaba en cada arranque de la aplicación desde el directorio de trabajo. Bajo un contexto de seguridad de pod reforzado (`distroless:nonroot`, `runAsUser: 1000`, `readOnlyRootFilesystem: true`, `capabilities: drop [ALL]`), esa ruta fallaba con `permission denied` y el pod entraba en un ciclo de fallos.

El runner dedicado evita eso:

* Las migraciones se ejecutan una vez, como un paso separado, antes de que la aplicación arranque.
* La imagen de la aplicación ya no incluye el SQL de migración y nunca escribe en el esquema.
* La propia imagen del runner funciona sin problemas bajo el mismo contexto de seguridad reforzado (consulta [Postura de seguridad](#security-posture)).

## Imágenes del runner

***

Cada versión incluye dos imágenes:

| Imagen                    | Aplica                                                | Bases de datos                   |
| ------------------------- | ----------------------------------------------------- | -------------------------------- |
| `midaz-ledger-migrations` | Migraciones de onboarding y de transaction del Ledger | 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` (fijada).
* Se ejecuta como `USER 65532:65532` (sin root, agnóstico al UID).
* Punto de entrada en shell POSIX que arma un DSN a partir de variables de entorno, ejecuta `migrate ... up` y termina.
* No escribe nada en disco. Postgres registra el progreso de la migración en la tabla `schema_migrations`, por lo que `readOnlyRootFilesystem: true` es seguro.

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

## Contrato de entorno

***

Cada punto de entrada acepta una anulación de URL ya construida o las variables individuales `DB_*`. La anulación de URL tiene prioridad cuando está definida.

### Runner del Ledger

| Base de datos | Anulación de URL           | Se arma a partir de                                                                                                                                                                          |
| ------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| onboarding    | `ONBOARDING_DATABASE_URL`  | `DB_ONBOARDING_HOST`, `DB_ONBOARDING_PORT` (predeterminado `5432`), `DB_ONBOARDING_USER`, `DB_ONBOARDING_PASSWORD`, `DB_ONBOARDING_NAME`, `DB_ONBOARDING_SSLMODE` (predeterminado `disable`) |
| transaction   | `TRANSACTION_DATABASE_URL` | `DB_TRANSACTION_*` (misma forma)                                                                                                                                                             |

### Runner de Tracer

| Anulación de URL | Se arma a partir de                                                                                                         |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `DATABASE_URL`   | `DB_HOST`, `DB_PORT` (predeterminado `5432`), `DB_USER`, `DB_PASSWORD`, `DB_NAME`, `DB_SSL_MODE` (predeterminado `disable`) |

### Forma del DSN

Cuando el punto de entrada arma el DSN a partir de las variables `DB_*`, produce:

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

La contraseña se codifica en formato percent-encoding para que los caracteres reservados de URI (`% @ : / ? # & + space [ ]`) no rompan el DSN. El `%` se codifica primero para que los escapes ya insertados no se codifiquen dos veces.

El punto de entrada 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. Inicia la infraestructura y espera hasta que Postgres esté disponible.
2. Inicia los servicios de Compose de ejecución única `ledger-migrate` y `tracer-migrate`.
3. Inicia cada servicio de la aplicación, que depende (`depends_on`) de su servicio de migración con `condition: service_completed_successfully`.

La aplicación nunca arranca contra una base de datos sin migrar.

### Objetivos de `make` en el host

Cada componente expone objetivos de Make en el host que ejecutan migraciones directamente contra una base de datos local, por ejemplo cuando desarrollas una migración nueva. Los objetivos usan la CLI fijada de `golang-migrate` y leen la configuración de conexión desde `.env`.

Ledger (`components/ledger`):

| Objetivo                   | 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 de datos                    |

Tracer (`components/tracer`):

| Objetivo               | 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 desde un estado inconsistente) |

### Ejemplo: ejecutar el runner del Ledger manualmente

Rara vez necesitas invocar la imagen del runner manualmente, porque la puerta de Compose lo hace por ti. Cuando lo necesites, apunta el runner a tus bases de datos mediante `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` en caso de éxito (incluido el caso idempotente sin efecto) y con un valor distinto de cero en caso de error.

## Despliegue en Kubernetes

***

En producción, ejecuta cada runner de migración como un Job de Kubernetes (normalmente un Job PreSync de Argo CD o un hook de Helm) que se completa antes del rollout de la aplicación. El chart de Helm configura esto por ti. Las secciones siguientes cubren las propiedades que importan si escribes tus propios manifiestos.

<Note>
  En despliegues multi-tenant, el runner es intencionalmente agnóstico al tenant: migra las bases de datos indicadas por su entorno, una vez. El fan-out por tenant es un asunto del despliegue. Ejecuta el Job una vez por cada base de datos de tenant con los valores `DB_*` (o la anulación de URL) de ese tenant.
</Note>

<h3 id="security-posture">
  Postura de seguridad
</h3>

El runner funciona bajo un contexto de seguridad de pod reforzado:

* **Sin root**: la imagen establece `USER 65532:65532`.
* **Agnóstico al UID**: los archivos de migración pertenecen a root y son legibles por cualquier usuario, de modo que cualquier UID puede leerlos.
* **Seguro para `readOnlyRootFilesystem`**: `migrate` no escribe ningún estado en el sistema de archivos.

La misma imagen funciona sin problemas bajo `runAsNonRoot: true`, `readOnlyRootFilesystem: true` y `capabilities: drop [ALL]`.

### TLS

Los puntos de entrada respetan `sslmode` desde el entorno (`DB_ONBOARDING_SSLMODE`, `DB_TRANSACTION_SSLMODE` o `DB_SSL_MODE`), con `disable` como valor predeterminado. En producción se recomienda establecer `require` (o algo más estricto). El runner en shell no reproduce la aplicación de TLS en código, la clasificación de errores ni la telemetría que las aplicaciones usan para sus propias conexiones. Controlas el TLS de las migraciones únicamente a través de `sslmode`, a cambio de un runner mínimo y sin dependencias.

## Convenciones e idempotencia

***

* **Up/down emparejados**: cada migración tiene un archivo `.up.sql` y un archivo `.down.sql`.
* **Idempotente**: ejecutar `migrate up` cuando el esquema ya está en la última versión no tiene efecto. Volver a ejecutar el runner después de una aplicación parcial o completa es seguro.
* **CLI fijada**: las imágenes del runner y los objetivos de `make` en el host fijan todos `golang-migrate` en `v4.19.1`.

## Actualización desde versiones anteriores

***

No necesitas ninguna migración manual de datos si actualizas desde una versión de Midaz en la que la aplicación migraba al iniciar. El esquema en disco es el mismo. Para adoptar el nuevo modelo:

1. Descarga la versión que incluye las imágenes del runner.
2. En Kubernetes, agrega los Jobs `midaz-ledger-migrations` y `midaz-tracer-migrations` a tu pipeline de despliegue para que se ejecuten antes del rollout de la aplicación. Si usas el chart de Helm oficial, esto ya está hecho por ti.
3. En desarrollo local, `make up` sigue funcionando como antes. El stack de Compose ahora bloquea automáticamente las aplicaciones hasta que se completen los nuevos servicios de migración de ejecución única.

## Páginas relacionadas

***

* [Instalación de Midaz](/es/products/midaz/midaz-setup): configuración local completa con `make up`.
* [Actualización de Midaz](/es/products/midaz/updating-midaz): flujos de actualización para local y Helm.
* [Estrategias de despliegue](/es/products/midaz/deployment): resumen del despliegue en BYOC.
* [Variables de entorno de Tracer](/es/products/tracer/tracer-environment-variables): referencia completa de configuración de Tracer.
