# Migrax documentation Migrax generates SQL migrations from JPA/Hibernate entities and applies them, with locking, checksums, rollback scripts, linting and verification. It runs as a command line tool, a Maven or Gradle plugin, a container image, and at startup in Spring Boot, Quarkus, Micronaut and Helidon, on PostgreSQL, CockroachDB, MySQL, MariaDB, SQL Server, Oracle, H2 and SQLite. --- Source: https://migrax.org/ See it in 30 seconds Add a field to an entity, and Migrax writes the SQL. ```java title="Customer.java" hl_lines="9" @Entity @Table(name = "customers") public class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Long id; public String email; public String phone; // new } ``` ```sql title="0002_add_customers_phone.sql" -- Generated by Migrax 0.4.0 for postgresql ALTER TABLE customers ADD COLUMN phone varchar(255); ``` The migration is plain SQL you can read, review and commit, with a rollback script next to it. Nothing is hidden: you can edit every statement before it runs. { .mx-centered .mx-reveal } Why teams use Migrax Everything a migration needs, checked before it reaches production. - :material-auto-fix:{ .lg .middle } __Migrations written for you__ --- Change an entity, run `migrax generate`. Tables, columns, keys, indexes, join tables, element collections, sequences and inheritance come out exactly as Hibernate maps them. - :material-shield-check:{ .lg .middle } __Checked before production__ --- Every migration is linted for statements that lock big tables, fail on existing rows or break running instances. `migrax verify` has your Hibernate version validate the result. - :material-undo-variant:{ .lg .middle } __Rollback included__ --- Each generated migration comes with a rollback script, and `verify` proves it works by rolling everything back and forward again. - :material-account-question:{ .lg .middle } __Asks before it loses data__ --- A dropped column that looks renamed? Migrax asks, and renames it so the data stays. Drops only happen when you say `--allow-destructive`. - :material-radar:{ .lg .middle } __Catches manual changes__ --- `migrax drift` compares the live database with what your migrations produce and lists every difference, including columns someone made narrower. - :material-cog-outline:{ .lg .middle } __Zero setup__ --- Run it in your service folder. It compiles the project, finds the dependencies and reads the database settings from your application configuration. Works with your stack Detected automatically. No plugin configuration needed to get started. Frameworks Spring Boot Quarkus Micronaut 4 and 5 Helidon 4 Jakarta EE Plain Hibernate Hibernate 5.4 to 7.4, each version reading its own mapping Databases PostgreSQL CockroachDB MySQL 8 MariaDB SQL Server Oracle 12c+ H2 SQLite Run it from Command line Maven plugin Gradle plugin Application startup ## Ready when your next entity changes Install the command line tool in a minute, then run it in any service folder. [Install Migrax :material-arrow-right:](getting-started/installation.md){ .md-button .md-button--primary } [Read how it works](guides/how-it-works.md){ .md-button } --- Source: https://migrax.org/download/ # Download Migrax needs **Java 17 or newer**. Download the command line tool below, then run its installer as described in [Installation](getting-started/installation.md). ## Migrax 0.4.0 Latest release, published 2026-10-11. [:material-download: Download migrax-0.4.0.zip](downloads/0.4.0/migrax-0.4.0.zip){ .md-button .md-button--primary } Then follow [Installation](getting-started/installation.md).
| File | What it is | Size | SHA-256 | |---|---|---|---| | [`migrax-0.4.0.zip`](downloads/0.4.0/migrax-0.4.0.zip) | **Command line tool** with installers for Windows, macOS and Linux | 485 KB | `9152e56d26c4582b6c05e1344c206367e77fd45062ef1c8c944e31de49cc7cf2` | | [`migrax-0.4.0.jar`](downloads/0.4.0/migrax-0.4.0.jar) | Core library and Maven plugin | 496 KB | `dbc7995925a841601cff22928f09d8fe04ee1b46c50da9e7a36d459e4337dcd5` | | [`migrax-gradle-plugin-0.4.0.jar`](downloads/0.4.0/migrax-gradle-plugin-0.4.0.jar) | Gradle plugin | 7 KB | `1a7a599fa28793a472eea94ef228efc2a85e4e5ca711ee16fe288d08ebc76b1c` | | [`migrax-helidon-0.4.0.jar`](downloads/0.4.0/migrax-helidon-0.4.0.jar) | Helidon MP startup integration | 8 KB | `56eb631a98d4e38a0c18d0cef5f75d4db029755a72230bf760d604b3bcdeae84` | | [`migrax-micronaut-0.4.0.jar`](downloads/0.4.0/migrax-micronaut-0.4.0.jar) | Micronaut startup integration | 13 KB | `b2d77d20b8d4e231a4963c05493d04a6cf3369d33ff0547a0198ba4542136527` | | [`migrax-quarkus-0.4.0.jar`](downloads/0.4.0/migrax-quarkus-0.4.0.jar) | Quarkus startup integration | 9 KB | `f70c12ab92d88dbeb0529eef3a84efe5642511fafd5cf76786d9f71345cdf7ea` | | [`migrax-spring-boot-starter-0.4.0.jar`](downloads/0.4.0/migrax-spring-boot-starter-0.4.0.jar) | Spring Boot startup integration | 16 KB | `14cd77f9cf4ebf7ca9949bc6c133d97a802b563d21aae04187a89e8dd1ebbfc4` |
All checksums: [`SHA256SUMS`](downloads/0.4.0/SHA256SUMS) ??? note "What's new in 0.4.0" The 0.4 release: Migrax moves to `org.migrax`. Nothing else changes in how it works; see below for the one-line upgrade. ### Changed - Migrax now lives under `org.migrax` (the project's domain is migrax.org): the Maven groupId, the Gradle plugin ID and the Java packages all change from `io.migrax` to `org.migrax`. Commands, options, configuration keys, environment variables, migration files, the snapshot and the database tables are unchanged. The migration lock has a new name too, so a 0.3 and a 0.4 instance don't wait for each other: during the upgrade, make sure only one of them runs migrations against a database at a time. To upgrade, replace `io.migrax` with `org.migrax` in your build: - Maven: `org.migrax` for the plugin and the integrations. - Gradle: `plugins { id("org.migrax") version "..." }` and `org.migrax:...` dependencies. - Java migrations: `import org.migrax.api.JavaMigration;`. - Logging configuration: the logger is now `org.migrax`. ## Older versions | Version | Published | Command line tool | All files | |---|---|---|---| | 0.3.0 | 2026-10-11 | [`migrax-0.3.0.zip`](downloads/0.3.0/migrax-0.3.0.zip) | [`SHA256SUMS`](downloads/0.3.0/SHA256SUMS) | | 0.3.0-rc.3 (pre-release) | 2026-10-10 | [`migrax-0.3.0-rc.3.zip`](downloads/0.3.0-rc.3/migrax-0.3.0-rc.3.zip) | [`SHA256SUMS`](downloads/0.3.0-rc.3/SHA256SUMS) | | 0.3.0-rc.2 (pre-release) | 2026-10-10 | [`migrax-0.3.0-rc.2.zip`](downloads/0.3.0-rc.2/migrax-0.3.0-rc.2.zip) | [`SHA256SUMS`](downloads/0.3.0-rc.2/SHA256SUMS) | | 0.3.0-rc.1 (pre-release) | 2026-10-10 | [`migrax-0.3.0-rc.1.zip`](downloads/0.3.0-rc.1/migrax-0.3.0-rc.1.zip) | [`SHA256SUMS`](downloads/0.3.0-rc.1/SHA256SUMS) | | 0.2.0 | 2026-10-10 | [`migrax-0.2.0.zip`](downloads/0.2.0/migrax-0.2.0.zip) | [`SHA256SUMS`](downloads/0.2.0/SHA256SUMS) | | 0.2.0-rc.6 (pre-release) | 2026-10-10 | [`migrax-0.2.0-rc.6.zip`](downloads/0.2.0-rc.6/migrax-0.2.0-rc.6.zip) | [`SHA256SUMS`](downloads/0.2.0-rc.6/SHA256SUMS) | | 0.2.0-rc.5 (pre-release) | 2026-10-10 | [`migrax-0.2.0-rc.5.zip`](downloads/0.2.0-rc.5/migrax-0.2.0-rc.5.zip) | [`SHA256SUMS`](downloads/0.2.0-rc.5/SHA256SUMS) | | 0.2.0-rc.4 (pre-release) | 2026-10-09 | [`migrax-0.2.0-rc.4.zip`](downloads/0.2.0-rc.4/migrax-0.2.0-rc.4.zip) | [`SHA256SUMS`](downloads/0.2.0-rc.4/SHA256SUMS) | | 0.2.0-rc.3 (pre-release) | 2026-10-09 | [`migrax-0.2.0-rc.3.zip`](downloads/0.2.0-rc.3/migrax-0.2.0-rc.3.zip) | [`SHA256SUMS`](downloads/0.2.0-rc.3/SHA256SUMS) | | 0.2.0-rc.2 (pre-release) | 2026-10-09 | [`migrax-0.2.0-rc.2.zip`](downloads/0.2.0-rc.2/migrax-0.2.0-rc.2.zip) | [`SHA256SUMS`](downloads/0.2.0-rc.2/SHA256SUMS) | | 0.2.0-rc.1 (pre-release) | 2026-10-09 | [`migrax-0.2.0-rc.1.zip`](downloads/0.2.0-rc.1/migrax-0.2.0-rc.1.zip) | [`SHA256SUMS`](downloads/0.2.0-rc.1/SHA256SUMS) | | 0.1.4 | 2026-10-10 | [`migrax-0.1.4.zip`](downloads/0.1.4/migrax-0.1.4.zip) | [`SHA256SUMS`](downloads/0.1.4/SHA256SUMS) | | 0.1.3 | 2026-10-09 | [`migrax-0.1.3.zip`](downloads/0.1.3/migrax-0.1.3.zip) | [`SHA256SUMS`](downloads/0.1.3/SHA256SUMS) | | 0.1.2 | 2026-10-09 | [`migrax-0.1.2.zip`](downloads/0.1.2/migrax-0.1.2.zip) | [`SHA256SUMS`](downloads/0.1.2/SHA256SUMS) | | 0.1.1 | 2026-10-09 | [`migrax-0.1.1.zip`](downloads/0.1.1/migrax-0.1.1.zip) | [`SHA256SUMS`](downloads/0.1.1/SHA256SUMS) | | 0.1.0 | 2026-10-08 | [`migrax-0.1.0.zip`](downloads/0.1.0/migrax-0.1.0.zip) | [`SHA256SUMS`](downloads/0.1.0/SHA256SUMS) | ## Container image From Migrax 0.3.0 on, every release is also a container image with the common JDBC drivers, for running migrations in Kubernetes or CI without a Java project: ``` docker pull ghcr.io/fsmutimeer/migrax:0.4.0 ``` See [Containers and Kubernetes](guides/containers-and-kubernetes.md). ## Check your download Each file's SHA-256 checksum is listed above and in `SHA256SUMS`. To check a file: === "Windows" ```bat certutil -hashfile migrax-0.4.0.zip SHA256 ``` === "macOS" ```bash shasum -a 256 migrax-0.4.0.zip ``` === "Linux" ```bash sha256sum migrax-0.4.0.zip ``` The result must match the checksum on this page. ## Which file do I need? - **To use the `migrax` command:** the `.zip`. It contains the command line tool and its installers. - **The `.jar` files** are libraries for build tools and frameworks: the core library with the Maven plugin, the Gradle plugin, and the startup integrations for Spring Boot, Quarkus, Micronaut and Helidon. Maven and Gradle normally download these themselves; see [Frameworks](frameworks/index.md). --- Source: https://migrax.org/getting-started/installation/ # Installation Migrax is a command line tool. It needs **Java 17 or newer**; nothing else has to be installed, and no administrator rights are needed. ## 1. Get Migrax === "From a release" 1. Open the [Download](../download.md) page and click **Download migrax-<version>.zip**. 2. Extract it (on Windows: right-click the file, **Extract All…**). You get a folder `migrax-` containing `install.ps1`, `install.sh`, `bin` and `lib`. !!! warning "Download the `.zip`, not a `.jar`" The Download page also lists `.jar` files. Those are libraries for build tools and frameworks (the Maven plugin, the Gradle plugin, the startup integrations); they don't contain the command line tool or the installer. Only the ZIP does. === "From source" ```bash git clone https://github.com/fsmutimeer/migrax.git cd migrax mvn clean install ``` This also installs the Maven plugin and the libraries the integrations use into your local Maven repository. ## 2. Run the installer Open a terminal **in the extracted folder** (or the source folder) and run the installer: === "Windows" In Command Prompt or PowerShell, for example with the folder extracted to `D:\`: ```bat cd /d D:\migrax-0.4.0 powershell -ExecutionPolicy Bypass -File install.ps1 ``` (In PowerShell, use `cd D:\migrax-0.4.0`.) The installer copies Migrax to `%LOCALAPPDATA%\migrax` and adds its `bin` folder to your user `PATH`. After that you can delete the extracted folder and the ZIP. === "macOS and Linux" ```bash sh install.sh ``` Installs to `~/.local/share/migrax` and links `~/.local/bin/migrax`. Set `MIGRAX_INSTALL_DIR` and `MIGRAX_BIN_DIR` to choose other folders. ## 3. Check it **Close the terminal and open a new one**, then: ```console $ migrax version migrax 0.4.0 ``` !!! tip "`migrax` is not recognized?" A terminal reads `PATH` once, when it starts, so a window that was open while the installer ran doesn't see the new entry: open a new terminal. Terminals inside an editor (VS Code, IntelliJ IDEA) copy the editor's `PATH` from when the editor started, so close every editor window and open it again. To keep using the current window instead: === "Command Prompt" ```bat set "PATH=%PATH%;%LOCALAPPDATA%\migrax\bin" ``` === "PowerShell" ```powershell $env:Path = [Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [Environment]::GetEnvironmentVariable('Path','User') ``` The `D:\>` prompt is Command Prompt; `PS D:\>` is PowerShell. Their commands differ. ## In a container To run migrations in Kubernetes or a CI job without installing anything, use the container image `ghcr.io/fsmutimeer/migrax` (from Migrax 0.3.0); see [Containers and Kubernetes](../guides/containers-and-kubernetes.md). ## Which Java does Migrax use? Migrax uses `JAVA_HOME` when it is set, otherwise the `java` on your `PATH`. To run it with a different Java for one terminal session, set `JAVA_HOME` there: === "Windows (PowerShell)" ```powershell $env:JAVA_HOME = "C:\Program Files\Eclipse Adoptium\jdk-25.0.4.101-hotspot" ``` === "macOS and Linux" ```bash export JAVA_HOME=/path/to/jdk-25 ``` Extra JVM options go in `MIGRAX_JAVA_OPTS`. ## Updating and uninstalling Run the installer of the new version again to update. To uninstall, delete the install folder (`%LOCALAPPDATA%\migrax` on Windows, `~/.local/share/migrax` and the `~/.local/bin/migrax` link on macOS and Linux) and remove its `bin` folder from your user `PATH`. Next: [Quick start](quickstart.md). --- Source: https://migrax.org/getting-started/quickstart/ # Quick start This takes about five minutes. You need a service with JPA entities, a `pom.xml` or `build.gradle`, and a database it can reach. Run everything from the service folder, the one with `pom.xml` or `build.gradle`. ## 1. Let Migrax look at your project ```console $ migrax init ``` `init` creates the migration folder and shows what Migrax detected: entity package, framework, naming, database and dialect. It changes nothing else and is safe to run again. ## 2. Check everything is ready ```console $ migrax doctor [ok] Java 21.0.5 [ok] Build tool: maven [ok] Entity package: com.example.shop [info] Framework: Spring Boot [info] Naming: spring (must match Hibernate; ...) [ok] Project compiled and dependencies resolved [ok] Hibernate 6.6.13.Final: entities are read from Hibernate's own mapping [ok] Entities: 4 table(s) under com.example.shop (Hibernate 6.6.13.Final mapping) [ok] Database URL: jdbc:postgresql://localhost:5432/shop [ok] Connected: PostgreSQL 16.4 All checks passed. ``` Anything that fails comes with the fix. Warnings explain settings worth changing, for example when Hibernate is allowed to create tables itself. !!! warning "Let Migrax own the schema" If your configuration lets Hibernate create or update tables at startup (`spring.jpa.hibernate.ddl-auto=update`, `quarkus.hibernate-orm.schema-management.strategy=update` and similar), set it to `none` or `validate`. `doctor` shows the exact line to change. ## 3. Generate the first migration ```console $ migrax generate Created src/main/resources/db/migration/0001_initial.sql: - create table customers - create table orders ... Rollback script written to src/main/resources/db/migration/rollback. Review the SQL, then run 'migrax migrate'. ``` The first `generate` compares your entities with the **current database**: - an empty database gets `0001_initial.sql` with every table; - a database that already has tables gets `0001_baseline.sql` with the tables it has, recorded as applied there without running it, and a second migration with only the differences. An empty database (a new environment, or `migrax verify`) is built from both. Open the file and read it. Migrations are plain SQL, and you can edit them before they run. ## 4. Apply it ```console $ migrax migrate Applying migration '0001_initial.sql'... Successfully applied migration '0001_initial.sql'. $ migrax status [X] 0001_initial.sql applied 2026-10-08 11:02:05 1 applied. ``` ## 5. Commit Commit these with your code: - the migration files and the `rollback/` folder in `src/main/resources/db/migration`; - `.migrax/snapshot.json` and `.migrax/history/`. Everything else in `.migrax/` is a local cache, and Migrax tells git to ignore it. ## From now on Every time you change an entity: ```console $ migrax generate # writes 0002_..., with a rollback script, linted $ migrax migrate ``` Next, learn [how Migrax works](../guides/how-it-works.md), or set up your framework: [Spring Boot](../frameworks/spring-boot.md), [Quarkus](../frameworks/quarkus.md), [Micronaut](../frameworks/micronaut.md) or [Helidon](../frameworks/helidon.md). --- Source: https://migrax.org/guides/how-it-works/ # How Migrax works ## The pieces ```mermaid flowchart LR E[Entities] -->|Hibernate's own mapping| M[Schema model] S[.migrax/snapshot.json] --> D{Diff} M --> D D --> G[0003_add_....sql
+ rollback script] G -->|migrax migrate| DB[(Database)] DB --> H[migrax_history] ``` **Schema model.** Migrax builds a model of the tables your entities need. When your project has Hibernate 5.4 or newer, Migrax asks **your own Hibernate version** for its mapping, without connecting to a database, so names, types, join tables, element collections, inheritance and sequences are exactly what your application expects. Without Hibernate it scans the `jakarta.persistence` / `javax.persistence` annotations instead. **Snapshot.** `.migrax/snapshot.json` records the schema your migrations produce. `generate` compares the entities with the snapshot and writes only the difference. The snapshot also records the dialect and [naming strategy](../reference/naming.md), so the names stay stable even if your configuration changes later. The very first `generate` has no snapshot, so it compares with the live database instead. When that database already has tables, it also writes them to `0001_baseline.sql` and records that migration as applied there without running it, so an empty database can still be built from the migrations alone. The baseline has no rollback script: undoing it would drop every table. **Migration files.** Migrations live in `src/main/resources/db/migration` and are numbered: `0001_initial.sql`, `0002_add_customers_phone.sql`, ... The name describes the change. Next to each generated migration, `rollback/.sql` undoes it. **History.** `migrax migrate` records every applied migration in the `migrax_history` table, with a SHA-256 checksum of the file. Failed attempts are recorded in `migrax_failures`. ## What `generate` does 1. Compiles the project if sources changed, and asks Maven or Gradle (preferring `./mvnw` / `./gradlew`) for the runtime classpath. The classpath is cached in `.migrax/classpath.txt` and refreshed when the build file changes. 2. Reads the entities into a schema model. 3. Compares it with the snapshot and works out the operations: create and drop tables, add, drop, rename and alter columns, keys, indexes, unique constraints and sequences. 4. Asks about likely renames, and refuses drops unless you pass `--allow-destructive` (see [Changing entities](changing-entities.md)). 5. Writes the migration and its rollback script, lints the SQL, and updates the snapshot. `migrax plan` shows the same SQL without writing anything. ## What `migrate` does 1. Takes a database lock, so two instances starting at the same time don't both migrate. 2. Checks the history: an applied file that changed or disappeared stops the run. 3. Applies pending migrations in order, each in its own transaction with its history record. 4. Runs repeatable `R__*.sql` migrations that are new or changed, and the callbacks. If a migration fails, its transaction is rolled back (where the database supports transactional DDL) and the failure is recorded. `migrate` then refuses to continue until you have looked at it; see [Rollbacks and recovery](rollbacks-and-recovery.md). ## Locking | Database | Lock | |---|---| | PostgreSQL | `pg_advisory_lock` | | CockroachDB | A row in `migrax_lock` (CockroachDB has no session locks) | | MySQL, MariaDB | `GET_LOCK` | | SQL Server | `sp_getapplock` | | Oracle | `DBMS_LOCK` (needs `EXECUTE` on `DBMS_LOCK`) | | H2 | Single-process database, no lock needed | | SQLite | An operating-system lock on `.migrax-lock` next to the database file | --- Source: https://migrax.org/guides/changing-entities/ # Changing entities The everyday loop is short: ```console $ migrax generate $ migrax migrate ``` This page covers the cases where Migrax needs a decision from you. ## Preview first ```console $ migrax plan 2 change(s) for postgresql: 1. add column orders.notes ALTER TABLE orders ADD COLUMN notes varchar(500); 2. alter column tags.label ALTER TABLE tags ALTER COLUMN label TYPE varchar(100); ``` `plan --impact` adds how many rows each affected table has, read from the database, so you can spot changes that will take long on large tables. ## Renamed a field or entity? To a database, a rename looks like "one column disappeared, another appeared". Dropping and re-adding would lose the data, so Migrax looks for likely renames: - the same name in another style, like `created_at` and `createdAt`, even when the type changed too; - otherwise a new column of the same type in the same table. In a terminal it asks: ```console $ migrax generate Did you rename customers.email to customers.contact_email? [y/N] y Created src/main/resources/db/migration/0004_rename_customers_email.sql: - rename column customers.email to contact_email ``` In scripts and CI, where nothing can be asked, pass the answer: ```console $ migrax generate --rename customers.email=contact_email $ migrax generate --rename-table client=customer ``` Several column renames are separated by commas. A rename with a type change becomes a `RENAME` followed by an `ALTER`. !!! note "Non-interactive runs" When input isn't a terminal, Migrax prints each possible rename with the exact `--rename` option to use, and does not assume a rename. ## Drops and narrowing changes Dropping a table or column, or making a column smaller, deletes data. Migrax refuses until you confirm with `--allow-destructive`: ```console $ migrax generate 3 change(s) for mysql: ... 3. drop column test_table.number_of_items [DESTRUCTIVE] ALTER TABLE test_table DROP COLUMN number_of_items; error[MXE108]: 1 destructive change(s) detected (marked [DESTRUCTIVE] above). Review them, then run 'migrax generate --allow-destructive'. If a column or table was renamed, pass --rename table.old=new or --rename-table old=new instead. ``` For a running production system, drop in two releases (expand and contract): first deploy code that no longer uses the column, then drop it in a later release. ## Naming the migration The name is generated from the first changes, for example `0005_add_orders_notes_and_more.sql`. Choose your own with `--name`; the next number is added for you: ```console $ migrax generate --name split_customer_name Created src/main/resources/db/migration/0005_split_customer_name.sql ``` ## Editing generated SQL Generated migrations are yours: edit them before they run anywhere, for example to backfill data in the same release. Never edit a migration after it has been applied: Migrax keeps a checksum of every applied file and stops when one changes. Write a new migration instead. ## Hand-written migrations ```console $ migrax new backfill_full_names Created src/main/resources/db/migration/0006_backfill_full_names.sql ``` `new` creates the next numbered file and an empty rollback script, for changes Migrax can't derive from entities: data migrations, views, grants, partitions. `--java` creates a Java class instead; see [Advanced migrations](advanced-migrations.md). --- Source: https://migrax.org/guides/rollbacks-and-recovery/ # Rollbacks and recovery ## Rolling back Every generated migration has a rollback script in `rollback/.sql`. To undo the newest migration: ```console $ migrax rollback --yes Rolling back 1 migration(s), newest first: 0006_create_categories_seq_and_more.sql Rolled back migration '0006_create_categories_seq_and_more.sql'. ``` | Option | Effect | |---|---| | `--steps 3` | Roll back the three newest migrations | | `--to 0004_add_index.sql` | Roll back everything after `0004_add_index.sql` | | `--dry-run` | List what would be rolled back | | `--yes` | Required: confirms the change. Back up first. | The rolled-back migrations become pending again, so `migrax migrate` re-applies them. !!! warning "Structure, not data" A rollback restores the **structure**: re-creating a dropped column brings the column back, not its values. The header of each rollback script says what cannot be restored: ```sql -- Rollback for 0006_create_categories_seq_and_more.sql. 'migrax rollback' runs it. -- Note: deletes data written to categories since the migration ran. ``` Hand-written migrations from `migrax new` get an empty rollback script for you to fill in. Java migrations roll back through `JavaMigration.rollback(Connection)`. `migrax verify` proves the rollback scripts work: it rolls every migration back on a throwaway database and applies them again ([Production safety](production-safety.md)). ## When a migration fails A failed migration is recorded in `migrax_failures`, and `migrate` stops until you decide what happened: ```console $ migrax status [X] 0005_fix_naming.sql applied 2026-10-08 11:04:45 [!] 0006_create_categories_seq_and_more.sql FAILED: SQLSyntaxErrorException: Table 'categories_seq' already exists 6 applied, 1 failed. Some migrations need attention. See 'migrax help repair'. ``` The message is the database's own error. Look at the database, then tell Migrax which case you are in: === "The statements took effect" Every statement in the migration is in the database (for example, you finished it by hand): ```console $ migrax repair 0006_create_categories_seq_and_more.sql --action applied --yes ``` === "You restored the database" You undid the partial changes, or nothing was applied; run it again: ```console $ migrax repair 0006_create_categories_seq_and_more.sql --action retry --yes $ migrax migrate ``` Keep a record of every repair. Never edit an applied migration or delete history rows by hand. ### Migrations that are safe to re-run A migration that starts with `-- migrax:resume-safe` can be re-run after a failure with `migrax migrate --resume`, without a repair. Only mark migrations whose statements can run twice (for example `CREATE TABLE IF NOT EXISTS`, idempotent updates). ## A migration file went missing `status` reports applied migrations whose file is gone, and `migrate` stops: ```console $ migrax migrate error[MXE105]: Applied migration file(s) are missing from the configured migration location: 0001_initial.sql. Restore the files from version control; applied migrations must not be deleted. If you deleted them on purpose, remove them from the history with: migrax repair 0001_initial.sql --action forget --yes ``` **Deleted by accident?** Restore the file from git (`git show :`), unchanged. This is the right fix in almost every project: other environments and new databases still need it. **Deleted on purpose**, for example to start a test database over? Remove the migrations from the history: ```console $ migrax repair 0001_initial.sql 0002_add_email.sql --action forget --yes Removed 0001_initial.sql from the migration history. Removed 0002_add_email.sql from the migration history. The database keeps the changes these migrations made. Run 'migrax status' to check the history. ``` The tables and columns those migrations created stay in the database; only the record goes. Migrax refuses to forget a migration whose file still exists, because `migrate` would run it again. !!! tip "Starting over with the current database as the baseline" To make the current database the starting point, forget the deleted migrations, delete `.migrax/snapshot.json` and the `.migrax/history` folder, and run `migrax generate`: with no snapshot, it compares the entities with the database. It writes the current tables to `0001_baseline.sql` (recorded as applied, not run) and what is really different to a second migration. Review that migration before running `migrax migrate`. From then on the migrations start at the current database. On other databases that still have the old history, the baseline would try to create tables that exist, so for shared environments restore the deleted files instead. ## Starting a development database over `migrax clean` drops every table, view and sequence in the database, the migration history included, so `migrax migrate` can build it again from the migrations: ```console $ migrax clean --dry-run Would drop from jdbc:postgresql://localhost:5432/shop: table customers table migrax_history ... $ migrax clean --yes Dropped 5 table(s), 2 sequence(s) from jdbc:postgresql://localhost:5432/shop. Run 'migrax migrate' to rebuild the schema from the migrations. $ migrax migrate ``` All data is gone afterwards, so use it only on development and test databases. On servers where it must never run, set `MIGRAX_CLEAN_DISABLED=true`: `clean` then refuses. --- Source: https://migrax.org/guides/team-workflow/ # Working in a team ## What to commit | Commit | Ignore (Migrax does it for you) | |---|---| | `src/main/resources/db/migration/*.sql` | `.migrax/classpath.txt`, `.migrax/classpath.hash` | | `src/main/resources/db/migration/rollback/` | `.migrax/build.stamp` and other caches | | `.migrax/snapshot.json` | | | `.migrax/history/` | | The snapshot is written with one table, column, index and key per line, so git can merge changes two people made to different tables. ## Two branches, same migration number Alice and Bob both branch from `0007`. Each generates a migration: both get `0008`. After the merge, `generate` refuses to continue and `check` fails: ```console $ migrax check Migrations share numbers: [[0008_add_orders_notes.sql, 0008_create_invoices.sql]]. Run 'migrax merge'. ``` ```console $ migrax merge Renamed 0008_create_invoices.sql to 0009_create_invoices.sql. Next: run 'migrax check' (no changes expected) and 'migrax verify' to prove the merged migrations produce the entity schema. ``` The migration added later moves to the next free number, together with its rollback script. `merge` also rebuilds a snapshot that has git conflict markers. Migrax never renames a migration that the configured database has already applied. If both files with the same number are already applied, they are settled history: they run in file name order and are left as they are. ## Too many migration files? After a while, squash old migrations into one: ```console $ migrax squash --to 0040_add_audit_columns.sql ``` The squashed file replaces `0001` to `0040`. Databases that already applied those record the squashed file without running it; new databases run only the squashed file. `--optimize` writes just the resulting schema and drops data statements. Delete the old files once every environment has run `migrate`. ## Reviewing migrations Treat a migration like code: - read the SQL in the pull request (the [GitHub Action](ci.md) posts it as a comment); - check the lint findings; - let CI run `migrax check` and `migrax verify`. --- Source: https://migrax.org/guides/ci/ # Continuous integration Three commands make a good CI gate: | Command | Fails when | Exit code | |---|---|---| | `migrax check` | an entity changed without a migration, or two migrations share a number | 2 / 1 | | `migrax lint --strict` | a pending migration would lock tables, fail on data, or break running instances | 1 | | `migrax verify` | the migrations don't produce a schema Hibernate accepts, or a rollback script fails | 1 | ## GitHub Actions The Migrax repository is itself a GitHub Action. It runs `check`, lints new migrations and posts the planned SQL as a pull request comment: ```yaml title=".github/workflows/migrations.yml" name: Migrations on: pull_request permissions: contents: read pull-requests: write # for the comment jobs: migrax: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: fsmutimeer/migrax@v0.4.0 with: working-directory: . # folder with pom.xml or build.gradle fail-on-lint-warnings: 'false' ``` | Input | Default | Meaning | |---|---|---| | `working-directory` | `.` | Service folder | | `java-version` | `21` | Java used to build Migrax and your service | | `fail-on-lint-warnings` | `false` | Fail on lint warnings, not only errors | | `comment` | `true` | Post or update a pull request comment with the results | | `github-token` | `${{ github.token }}` | Token used to comment | Outputs: `changes` (entity changes without a migration) and `findings` (lint findings in changed migrations). ## Any CI system ```bash migrax check --no-input migrax lint --strict migrax verify # H2 in memory, or Docker for other engines ``` `verify` starts a throwaway database: in-memory H2 for H2 projects, or a container of your engine when Docker is available. Without Docker, point it at an empty scratch database: ```bash migrax verify --url jdbc:postgresql://ci-db:5432/scratch ``` The database user and password come from the same place as usual (environment variables or application configuration) unless you pass `--user` / `--password`. ## Machine-readable output `plan`, `status`, `check`, `lint`, `drift` and `verify` accept `--json`. Progress messages go to standard error, so standard output stays valid JSON: ```console $ migrax check --json {"ok":false,"changes":["add column orders.notes"],"conflicts":[]} ``` --- Source: https://migrax.org/guides/production-safety/ # Production safety Migrax is built around one idea: a migration should be proven before it runs in production. ## Lint every migration Every generated migration is linted as it is written, and `migrax lint` checks the pending ones (or `--all`, or the files you name). Findings explain the risk and the safer alternative: ```console $ migrax lint src/main/resources/db/migration/0007_add_orders_notes_and_more.sql, statement 3: warning MX007 ALTER TABLE test_table DROP COLUMN numberOfItems This deletes data, and running application instances that still use it will fail. Fix: Deploy code that no longer uses it first, then drop it in a later release (expand/contract). 1 file(s) checked, 1 finding(s). ``` See all [lint rules](../reference/lint-rules.md). To accept a finding deliberately, put `-- migrax:lint-ignore MX007` on the line before the statement. ### Non-blocking changes on PostgreSQL `migrax generate --safe` moves index and constraint creation on existing tables into a second, non-transactional migration that uses `CREATE INDEX CONCURRENTLY`, `NOT VALID` and `VALIDATE CONSTRAINT`, so writes keep flowing while they build. ## Verify before you ship ```console $ migrax verify Applied 7 migration(s) to a fresh postgresql database. Schema matches the entities (Hibernate 6.6.13.Final schema validation). Rollback scripts: ok (7 rolled back and re-applied). Verified. ``` `verify`: 1. starts a throwaway database: in-memory H2, a Docker container of your engine, or the empty database you pass with `--url`; 2. applies every migration from scratch; 3. starts **your** Hibernate version with schema validation against the result (without Hibernate, it compares the schema with the entities); 4. rolls the migrations back and applies them again, proving the rollback scripts work. When older migrations have no rollback script, it checks the newest ones that do. Run it in CI on every pull request. ## Detect manual changes ```console $ migrax drift 2 difference(s) between the database and the snapshot: - products.name: expected varchar(255) but the database column is shorter (varchar(100)) - products.hotfix: column exists in the database but not in the expected schema Someone changed the database outside migrations. Write a migration for intended changes ('migrax new '), or revert them. ``` `drift` reads the live database and compares it with what the migrations produce (the snapshot), or with the entities (`--entities`). It reports missing and extra tables and columns, keys, unique constraints, nullability, type changes and narrowed columns. It exits with code 2 when it finds drift, so it can run on a schedule. ## Hibernate must not change the schema When Hibernate is allowed to create or update tables at startup, the database drifts away from your migrations, and migrations later fail with "already exists". `create` and `drop-and-create` even delete all data on every start. `migrax doctor` finds these settings and shows the exact line to change: ```console [warn] Hibernate will change your database schema by itself Found: quarkus.hibernate-orm.schema-management.strategy = update in src/main/resources/application.properties, line 15 Why: Hibernate adds tables and columns itself when the application starts. ... Fix: change quarkus.hibernate-orm.schema-management.strategy=update to quarkus.hibernate-orm.schema-management.strategy=none (or validate: Hibernate then checks the schema at startup without changing it) ``` ## A release checklist - [x] Migration generated in development and reviewed in the pull request - [x] CI: `check`, `lint --strict`, `verify` - [x] Backup taken before the production run - [x] `migrax migrate` (or startup migration) as part of the release - [x] `migrax status` and `migrax drift` afterwards --- Source: https://migrax.org/guides/advanced-migrations/ # Advanced migrations ## Java migrations Some data changes are easier in Java than in SQL. Create one with: ```console $ migrax new backfill_full_names --java ``` ```java title="src/main/java/db/migration/V0006__BackfillFullNames.java" package db.migration; import org.migrax.api.JavaMigration; import java.sql.Connection; public class V0006__BackfillFullNames implements JavaMigration { @Override public void migrate(Connection connection) throws Exception { try (var statement = connection.createStatement()) { statement.executeUpdate( "UPDATE customer SET full_name = first_name || ' ' || last_name"); } } @Override public void rollback(Connection connection) throws Exception { // optional; without it, 'migrax rollback' refuses to roll this migration back } } ``` - The class name gives the version and order, like a file name: `V0006__BackfillFullNames` runs after `0005_....sql`. - Classes live in the `db.migration` package (change it with `migrax.java-package`). - The migration runs in the same transaction as its history record. Don't commit or close the connection. - Add `org.migrax:migrax` as a `provided` (Maven) or `compileOnly` (Gradle) dependency so the class compiles. With the Spring Boot starter, `JavaMigration` beans are picked up too. ## Repeatable migrations Files named `R__.sql` run after the versioned migrations, and again whenever their content changes. Use them for views, functions, stored procedures and grants: ```sql title="R__active_customers_view.sql" CREATE OR REPLACE VIEW active_customers AS SELECT id, email FROM customers WHERE active = true; ``` ## Callbacks SQL files with these names run around migrations: | File | Runs | |---|---| | `beforeMigrate.sql` | once, before any migration | | `beforeEachMigrate.sql` | before each migration | | `afterEachMigrate.sql` | after each migration | | `afterMigrate.sql` | once, after all migrations | ## Placeholders `${name}` in a migration is replaced at run time: ```sql GRANT SELECT ON customers TO ${reporting_role}; ``` Set values in application config (`migrax.placeholders.reporting_role=analyst`), as a system property (`-Dmigrax.placeholders.reporting_role=analyst`) or an environment variable (`MIGRAX_PLACEHOLDERS_REPORTING_ROLE=analyst`). ## Directives Directives are comment lines in a migration: | Directive | Effect | |---|---| | `-- migrax:no-transaction` | Run without a transaction, for statements like `CREATE INDEX CONCURRENTLY` | | `-- migrax:resume-safe` | Allow `migrate --resume` to re-run it after a failure | | `-- migrax:lint-ignore MX001` | Silence a lint rule for the next statement | ## Several schemas (multi-tenant) Run the same migrations once per schema: ```console $ migrax migrate --schemas tenant_a,tenant_b,tenant_c ``` Or set `migrax.schemas=tenant_a,tenant_b` in application config. `status` and `rollback` accept the same option, and the startup integrations read `migrax.schemas`. --- Source: https://migrax.org/guides/switching-tools/ # Switching from Flyway or Liquibase A project that already manages its database with Flyway or Liquibase can move to Migrax with one command, without running anything again on the database. `migrax import` reads the other tool's history and records it in Migrax's own history (`migrax_history`). ## From Flyway Flyway's files already live where Migrax looks for migrations (`src/main/resources/db/migration`), and their names work unchanged: `V1__init.sql`, `V2__add_email.sql` and repeatable `R__views.sql` files are ordered by their version. ```console $ migrax import flyway Recorded 12 migration(s) as applied. Next: disable Flyway (spring.flyway.enabled=false), then run 'migrax generate'; its first run uses the current database as the baseline. ``` What it does: - Every migration that Flyway applied successfully (`success` is true in `flyway_schema_history`) is recorded as applied, so `migrax migrate` doesn't run it again. - Java migrations Flyway ran are recorded by their class name. - A Flyway baseline (`baselineVersion`) is respected: files up to that version are recorded as applied too. - Failed Flyway migrations are not recorded; `migrax migrate` runs them like new files. - A file Flyway applied that is no longer in the folder is skipped, with a note. If the history table has another name, pass it with `--table` (for example `--table my_schema_history`). ## From Liquibase Liquibase's changelogs are XML, YAML or SQL that Migrax doesn't run. Instead, the import writes the database's current schema as one SQL migration, a baseline, and records it as applied: ```console $ migrax import liquibase Wrote 0001_liquibase_baseline.sql with 9 table(s) from the current schema. Recorded 1 migration(s) as applied. Next: disable Liquibase (spring.liquibase.enabled=false), then run 'migrax generate'; its first run uses the current database as the baseline. ``` The baseline has the tables, keys, indexes and the sequences your entities use, so a new environment or `migrax verify` builds the whole schema from it. The database the import ran on already has all of it and never runs the file. Review its column types before you commit it; `--name` chooses another file name. ## Afterwards 1. Remove the other tool, or switch it off (`spring.flyway.enabled=false`, `spring.liquibase.enabled=false`, or your framework's setting), so that only Migrax migrates at startup. 2. Run `migrax generate`. With no snapshot yet, it compares the entities with the database and writes only what is really different; usually it finds nothing and saves the snapshot. 3. Run `migrax verify` to check that the migrations build the schema your entities expect. The other tool's history tables (`flyway_schema_history`, `DATABASECHANGELOG`, `DATABASECHANGELOGLOCK`) stay in the database; Migrax ignores them. Drop them once you no longer need them. The import is safe to run again: migrations already in Migrax's history are not recorded twice. --- Source: https://migrax.org/guides/containers-and-kubernetes/ # Containers and Kubernetes In container platforms, migrations usually run as their own step before the new version of the application starts: a Kubernetes Job, an init container, or a deploy hook. Migrax has an official container image for this, and it runs with just the migration files; no Java project or build is needed. !!! note "Available from Migrax 0.3.0" The image is published with Migrax 0.3.0 and newer. ## The container image ``` ghcr.io/fsmutimeer/migrax:0.4.0 ``` It has Java, Migrax and these JDBC drivers: PostgreSQL (also for CockroachDB), MariaDB (also for MySQL servers), SQL Server, SQLite and H2. It runs as a non-root user, for `amd64` and `arm64`. Migrations are read from `/migrations`. ```console $ docker run --rm -v "$PWD/src/main/resources/db/migration:/migrations:ro" \ -e MIGRAX_DATABASE_URL=jdbc:postgresql://db:5432/shop \ -e MIGRAX_DATABASE_USER=shop -e MIGRAX_DATABASE_PASSWORD_FILE=/run/secrets/db-password \ -v "$PWD/db-password:/run/secrets/db-password:ro" \ ghcr.io/fsmutimeer/migrax:0.4.0 migrate Applying migration '0001_initial.sql'... Successfully applied migration '0001_initial.sql'. Applied 1 migration(s); 1 total in /migrations. ``` `migrate`, `status`, `rollback`, `repair`, `clean`, `lint` and `drift` work in the image. `generate`, `check` and `verify` read your entities, so they run where your project is built (your machine or CI), not in the image. ### Your migrations in an image Usually the migrations are baked into an image built with each version of the application: ```dockerfile FROM ghcr.io/fsmutimeer/migrax:0.4.0 COPY src/main/resources/db/migration/ /migrations/ # The snapshot lets 'migrax drift' compare the database with what the migrations produce. COPY .migrax/snapshot.json /workspace/.migrax/snapshot.json ``` ### Other drivers MySQL Connector/J and Oracle's driver aren't included, because their licenses differ from Migrax's. Add them in your image: ```dockerfile FROM ghcr.io/fsmutimeer/migrax:0.4.0 ADD --chmod=644 https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/9.1.0/mysql-connector-j-9.1.0.jar /opt/migrax/drivers/ ``` Every jar in `/opt/migrax/drivers` (or in the folder `MIGRAX_DRIVERS` names) is available to Migrax. Outside the image the same works with `--classpath ` or a `drivers` folder next to Migrax's `lib` folder. ## Database logins in containers Kubernetes and Docker mount secrets as files. Point Migrax at them: | Variable | Contains | |---|---| | `MIGRAX_DATABASE_URL` or `MIGRAX_DATABASE_URL_FILE` | the JDBC URL | | `MIGRAX_DATABASE_USER` or `MIGRAX_DATABASE_USER_FILE` | the user | | `MIGRAX_DATABASE_PASSWORD` or `MIGRAX_DATABASE_PASSWORD_FILE` | the password | A `*_FILE` variable names a file; its content is used without the last line break. When both forms are set, the plain variable wins. `--password-file ` does the same on the command line. ### Logins with cloud identities Cloud databases can accept short-lived tokens instead of passwords (AWS RDS with IAM, Google Cloud SQL, Azure Database with Microsoft Entra ID). That works through the provider's JDBC plugin: put its jar in the drivers folder (or a derived image) and use the URL form its documentation gives. Migrax passes the URL and user through unchanged. !!! warning "Not tested by the Migrax build" The Migrax build has no cloud accounts, so these logins are not tested by it. Check them in a test environment with `migrax doctor` before you rely on them. ## Running in Kubernetes The manifests below are in the repository under [`examples/kubernetes`](https://github.com/fsmutimeer/migrax/tree/main/examples/kubernetes). Each was run on a local Kubernetes cluster (kind) against PostgreSQL. They read the database login from a Secret named `shop-db` with the keys `url`, `user` and `password` (`examples/kubernetes/secret.yaml`). ### A Job before the rollout (recommended) Run the Job, wait for it, then roll out the application. A failed migration stops the deployment and needs a person: `backoffLimit: 0` keeps Kubernetes from retrying it. ```yaml apiVersion: batch/v1 kind: Job metadata: name: shop-migrate spec: backoffLimit: 0 ttlSecondsAfterFinished: 3600 template: spec: restartPolicy: Never containers: - name: migrate image: registry.example.com/shop-migrations:1.0 args: ["migrate", "--lock-timeout", "2m"] env: - name: MIGRAX_DATABASE_URL valueFrom: {secretKeyRef: {name: shop-db, key: url}} - name: MIGRAX_DATABASE_USER valueFrom: {secretKeyRef: {name: shop-db, key: user}} - name: MIGRAX_DATABASE_PASSWORD_FILE value: /run/secrets/shop-db/password volumeMounts: - {name: db-secret, mountPath: /run/secrets/shop-db, readOnly: true} volumes: - name: db-secret secret: secretName: shop-db items: [{key: password, path: password}] ``` ```console $ kubectl apply -f job.yaml $ kubectl wait --for=condition=complete job/shop-migrate --timeout=5m ``` ### With Helm or Argo CD The same Job runs as a hook before each release. For Helm, add to its `metadata`: ```yaml annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-delete-policy": before-hook-creation ``` For Argo CD, the annotation is `argocd.argoproj.io/hook: PreSync`. ### An init container in each pod Simpler to set up: every pod migrates before the application starts. Pods that start together wait for each other with `--lock-timeout`; the first one applies the migrations and the others find nothing to do. From the test with three replicas: ```console == pod/shop-7bf9684b9c-sxnq9 Applying migration '0001_initial.sql'... Successfully applied migration '0001_initial.sql'. Applied 1 migration(s); 1 total in /migrations. == pod/shop-7bf9684b9c-5n79b Another Migrax process holds the migration lock; waiting up to 5m... Nothing to migrate: all 1 migration(s) are applied. ``` The manifest is `examples/kubernetes/init-container.yaml`. Give the lock timeout more time than your longest migration takes. Without `--lock-timeout`, the second pod fails at once with "Another Migrax process is applying migrations" and Kubernetes restarts it. ### Detecting manual changes every night `migrax drift` exits with code 2 when the database differs from what the migrations produce, so a CronJob fails and your cluster's alerting for failed jobs tells you (`examples/kubernetes/drift-cronjob.yaml`): ```console 1 difference(s) between the database and the snapshot: - customers.hotfix: column exists in the database but not in the expected schema Someone changed the database outside migrations. Write a migration for intended changes ('migrax new '), or revert them. ``` ## Startup integrations in several instances The Spring Boot, Quarkus, Micronaut and Helidon integrations migrate when the application starts. When several instances start at the same time, set `migrax.lock-timeout` (for example `2m`) so they wait for each other instead of failing. --- Source: https://migrax.org/guides/ai-assistants/ # Using Migrax with AI assistants AI coding assistants can use Migrax directly: they read what changed, preview the SQL with its risks, and explain it to you, while you stay the one who changes the database. ## The MCP server `migrax mcp` is a [Model Context Protocol](https://modelcontextprotocol.io) server. Assistants that support MCP, such as Claude Code, Codex CLI, Gemini CLI, GitHub Copilot in VS Code and Cursor, start it and call Migrax's commands as tools, getting their [JSON results](../reference/errors-and-json.md). | Tool | What it does | |---|---| | `status` | Which migrations are applied, pending, failed or changed | | `check` | Whether entities changed without a migration | | `plan` | The SQL `generate` would write, with lint findings and table sizes | | `lint` | Locking and risky statements in migrations | | `drift` | Manual changes in the database | | `doctor` | Setup problems and how to fix them | | `verify` | Migrations applied to a throwaway database and validated (slow) | | `show_migration` | One migration file's SQL, or its rollback script | | `generate` | Writes a migration; only with `migrax mcp --allow-generate` | The server never changes a database: `migrate`, `rollback`, `repair` and `clean` are not offered, so an assistant can't apply or undo migrations on its own. With `--allow-generate` it can write migration files, which you review in version control before running `migrax migrate` yourself. ### Claude Code Run this in your service folder: ```console $ claude mcp add migrax -- migrax mcp ``` On Windows, `migrax` is a `.cmd` script, so start it through `cmd`: ```console > claude mcp add migrax -- cmd /c migrax mcp ``` To share it with your team, add `--scope project`: Claude Code then writes the setting to `.mcp.json` in the project, which you commit. To let the assistant write migrations, end the command with `migrax mcp --allow-generate`. ### Codex CLI Run this in your service folder: ```console $ codex mcp add migrax -- migrax mcp ``` Codex writes the server to `~/.codex/config.toml`, so it is available in every folder you start Codex in. To set it up for one project only, put the same lines in `.codex/config.toml` in the project (Codex reads it in projects you trust): ```toml [mcp_servers.migrax] command = "migrax" args = ["mcp"] ``` Codex finds `migrax.cmd` on Windows by itself, so the command is the same there. ### Gemini CLI Run this in your service folder: ```console $ gemini mcp add migrax migrax mcp ``` Gemini writes the server to `.gemini/settings.json` in the project; add `--scope user` to use it in every folder. `gemini mcp list` shows whether it connects. Gemini starts MCP servers only in folders you trust, so trust the folder when it asks. Like Codex, it finds `migrax.cmd` on Windows by itself. ### GitHub Copilot in VS Code Create `.vscode/mcp.json` in your project: ```json { "servers": { "migrax": { "type": "stdio", "command": "migrax", "args": ["mcp"] } } } ``` VS Code starts the server in the workspace folder. Note that the top-level key is `servers` here, not `mcpServers` as in the other clients. ### Cursor Create `.cursor/mcp.json` in your project, or `~/.cursor/mcp.json` to use Migrax in every project: ```json { "mcpServers": { "migrax": { "type": "stdio", "command": "migrax", "args": ["mcp", "--dir", "${workspaceFolder}"] } } } ``` `${workspaceFolder}` is the folder you opened in Cursor, so the server always reads that service. ### Other MCP clients Most other clients (Claude Desktop, Windsurf, Cline, ...) read the same JSON as Cursor: ```json { "mcpServers": { "migrax": { "command": "migrax", "args": ["mcp", "--dir", "/path/to/service"] } } } ``` The server runs in the folder the client starts it in; `--dir` points it at your service instead. It uses the same database settings as the command line (application config, `MIGRAX_DATABASE_URL`, ...). ### Windows: start Migrax through `cmd` On Windows, `migrax` is a `.cmd` script. Some clients start programs directly and can't run scripts, so they report that `migrax` was not found even though it works in a terminal. In JSON files, start it through `cmd` instead: ```json "command": "cmd", "args": ["/c", "migrax", "mcp"] ``` This works in every client. Codex CLI and Gemini CLI don't need it. ### ChatGPT The ChatGPT app connects only to MCP servers on the internet, and `migrax mcp` runs on your own machine next to your code and database. To use Migrax with OpenAI's models, use Codex CLI (above). ## Instructions for your project's assistant Assistants follow the instructions in files such as `AGENTS.md` or `CLAUDE.md`. This block tells them how to work with Migrax; copy it into yours: ```markdown ## Database migrations (Migrax) - The schema is managed by Migrax. Never change the database by hand or with Hibernate's schema generation (`ddl-auto`, `schema-management.strategy`): change the entities, then run `migrax plan` to preview and `migrax generate` to write the migration. - Read the plan's lint findings before generating. Explain locking statements, NOT NULL columns on existing tables and anything marked [DESTRUCTIVE]. - A renamed field or table must be passed as a rename (`--rename table.old=new`, `--rename-table old=new`); otherwise its data is dropped. Ask when unsure. - Never edit a migration that is already applied anywhere; write a new one. - Do not run `migrax migrate`, `rollback`, `repair` or `clean` against shared or production databases. Tell the person what to run. - Use `--json` for machine-readable output; errors have stable codes (MXE...). ``` ## Documentation for assistants The documentation is also published as plain text for AI assistants: - [llms.txt](https://migrax.org/llms.txt): an index of every page, with a one-line summary each; - [llms-full.txt](https://migrax.org/llms-full.txt): every page in one file. Point an assistant at `llms-full.txt` when it needs to answer questions about Migrax. --- Source: https://migrax.org/frameworks/ # Frameworks and build tools Migrax doesn't depend on any framework. It reads your entities and talks to the database over JDBC, so the command line works the same everywhere. What differs per framework is: - **where the database settings are**, which Migrax reads for you; - **how Hibernate names tables and columns**, which Migrax must match exactly; - **how migrations can run at startup**, through a small integration library. Migrax recognizes the framework from your build file and configuration. `migrax doctor` shows what it detected. | Framework | Database settings | Naming | Startup integration | |---|---|---|---| | [Spring Boot](spring-boot.md) | `spring.datasource.*` | `spring` | `migrax-spring-boot-starter` | | [Quarkus](quarkus.md) | `quarkus.datasource.*` | `jpa` | `migrax-quarkus` | | [Micronaut](micronaut.md) | `datasources.default.*` | `micronaut` with Micronaut Data, else `jpa` | `migrax-micronaut` | | [Helidon](helidon.md) | `javax.sql.DataSource..*`, `db.connection.*` (SE) | `jpa` | `migrax-helidon` (MP) | | [Jakarta EE, plain Hibernate](jakarta-ee.md) | `persistence.xml` | `jpa` | run the CLI in your deployment | Build tools: [Maven plugin](maven.md) and [Gradle plugin](gradle.md). !!! info "Getting the integration libraries" Migrax 0.4.0 is not published to Maven Central yet. Install the libraries into your local Maven repository from the source: ```bash git clone https://github.com/fsmutimeer/migrax.git && cd migrax mvn install # core library and Maven plugin mvn install -f integrations/pom.xml # framework integrations and Gradle plugin ``` !!! note "Upgrading from 0.3 or older" Since 0.4.0, Migrax is published as `org.migrax` instead of `io.migrax`: the groupId, the Gradle plugin ID (`id("org.migrax")`), the Java packages (`org.migrax.api.JavaMigration`) and the logger name. Replace `io.migrax` with `org.migrax` in your build files, Java migrations and logging configuration. During the upgrade, let only one instance run migrations against a database at a time: 0.3 and 0.4 use differently named migration locks. --- Source: https://migrax.org/frameworks/spring-boot/ # Spring Boot ## Command line Nothing to configure. Migrax reads the database settings from `application.properties` or `application.yml`, including the active profile: ```properties spring.datasource.url=jdbc:postgresql://localhost:5432/shop spring.datasource.username=shop spring.datasource.password=${DB_PASSWORD} ``` `SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_USERNAME` and `SPRING_DATASOURCE_PASSWORD` environment variables work too, and so does `spring.datasource.hikari.jdbc-url`. **Naming:** Spring Boot converts names to snake_case (`fullName` → `full_name`) and names join tables `_`. Migrax uses the same rules (`spring` naming), or a custom naming strategy from `spring.jpa.hibernate.naming.*`. **Hibernate settings:** anything under `spring.jpa.properties.*` is passed to Hibernate when Migrax reads the mapping. Set Hibernate so it doesn't change the schema itself: ```properties spring.jpa.hibernate.ddl-auto=validate ``` ## Migrate at startup Add the starter: === "Maven" ```xml org.migrax migrax-spring-boot-starter 0.4.0 ``` === "Gradle" ```kotlin implementation("org.migrax:migrax-spring-boot-starter:0.4.0") ``` Migrations are applied when the application starts, **before** JPA starts, so `ddl-auto=validate` checks the migrated schema. `JavaMigration` beans are picked up. | Setting | Default | Meaning | |---|---|---| | `migrax.enabled` | `true` | Apply migrations at startup | | `migrax.locations` | `classpath:db/migration` | Migration folder | | `migrax.java-package` | `db.migration` | Package of `JavaMigration` classes | | `migrax.resume` | `false` | Re-run a failed migration marked `-- migrax:resume-safe` | | `migrax.lock-timeout` | `0` | How long to wait while another instance migrates, for example `2m` when several start together | | `migrax.placeholders.` | | Values for `${name}` placeholders | | `migrax.schemas` | | Schemas to migrate one after another | ### Development mode ```properties title="application-dev.properties" migrax.dev.generate=true ``` With `migrax.dev.generate=true`, the application generates a migration for changed entities on every restart, then applies it. Drops still need `migrax.dev.allow-destructive=true`. Use it only in development, and review what it wrote before you commit. ## Health check With Spring Boot Actuator in the application, Migrax adds a `migrax` health component: UP when every migration is applied and none failed, was edited or is missing, DOWN otherwise, with the counts as details: ```json "migrax": {"status": "UP", "details": {"applied": 3, "pending": 0, "problems": 0}} ``` Turn it off with `management.health.migrax.enabled=false`. --- Source: https://migrax.org/frameworks/quarkus/ # Quarkus ## Command line Migrax reads `application.properties` / `application.yaml` with the active profile: ```properties quarkus.datasource.db-kind=postgresql quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/shop quarkus.datasource.username=shop quarkus.datasource.password=${DB_PASSWORD} ``` `QUARKUS_DATASOURCE_JDBC_URL` and the other Quarkus environment variables work too. **Naming:** Quarkus keeps Hibernate's default naming, so columns are named exactly like fields (`createdAt`). Migrax uses `jpa` naming, or the strategy in `quarkus.hibernate-orm.physical-naming-strategy`. Tell Hibernate not to change the schema: ```properties quarkus.hibernate-orm.schema-management.strategy=none ``` !!! warning "Not `update`" With `update`, Hibernate creates new tables when the application starts, before migrations run, and the migration then fails with "already exists". `migrax doctor` warns about it. ## Migrate at startup ```xml org.migrax migrax-quarkus 0.4.0 ``` Migrations from `db/migration` are applied when the application starts (JVM mode). | Setting | Default | Meaning | |---|---|---| | `migrax.enabled` | `true` | Apply migrations at startup | | `migrax.locations` | `db/migration` | Migration folder on the classpath | | `migrax.java-package` | `db.migration` | Package of `JavaMigration` classes | | `migrax.resume` | `false` | Re-run a failed migration marked `-- migrax:resume-safe` | | `migrax.lock-timeout` | `0` | How long to wait while another instance migrates, for example `2m` when several start together | | `migrax.placeholders.` | | Values for `${name}` placeholders | | `migrax.schemas` | | Schemas to migrate one after another | !!! note "Keep `strategy=none` with the integration" Quarkus starts Hibernate before the integration runs. With `validate`, a pending migration would stop the application from starting, because Hibernate checks the schema first. Use `none`, and validate in CI with `migrax verify` instead. ## Readiness check With `quarkus-smallrye-health` in the application, Migrax adds a `migrax` readiness check: UP when every migration is applied and none failed, was edited or is missing, DOWN otherwise, with the counts as data (`/q/health/ready`). Without the health extension, Quarkus prints a warning while building that it can't index `org.eclipse.microprofile.health.HealthCheck`; it is harmless, and the check is simply not used. --- Source: https://migrax.org/frameworks/micronaut/ # Micronaut Micronaut 4 and 5 are supported. ## Command line Migrax reads `application.yml` / `application.properties` and the environments in `MICRONAUT_ENVIRONMENTS`: ```yaml datasources: default: url: jdbc:postgresql://localhost:5432/shop username: shop password: ${DB_PASSWORD} jpa: default: properties: hibernate: hbm2ddl: auto: none ``` `DATASOURCES_DEFAULT_URL`, `DATASOURCES_DEFAULT_USERNAME` and `DATASOURCES_DEFAULT_PASSWORD` work too. Without a `default` data source, Migrax uses the first one. Hibernate settings under `jpa.default.properties.*` are passed to Hibernate. !!! warning "Change the starter's `update`" Projects created from the Micronaut starter set `hbm2ddl.auto: update`. Set it to `none` or `validate`; `migrax doctor` shows the exact line. ## Naming | Project uses | Naming | Example: `homepageURL` | |---|---|---| | Micronaut Data (`micronaut-data-hibernate-jpa`) | `micronaut` | `homepage_url` | | Micronaut Hibernate JPA without Micronaut Data | `jpa` | `homepageURL` | Micronaut Data converts names to snake_case with its own rule, which differs from other snake_case rules for names like `myURL` (`my_url`) or `productSKU` (`product_sku`). It also uses one shared `hibernate_sequence` for generated ids. Migrax matches both, and when Micronaut Data is on the classpath it uses Micronaut's own naming class. ## Migrate at startup ```xml org.migrax migrax-micronaut 0.4.0 ``` Migrations run as soon as Micronaut creates the data source, **before** Hibernate starts, so `hbm2ddl.auto: validate` checks the migrated schema. | Setting | Default | Meaning | |---|---|---| | `migrax.enabled` | `true` | Apply migrations at startup | | `migrax.datasource` | `default` | Data source to migrate | | `migrax.locations` | `db/migration` | Migration folder on the classpath | | `migrax.java-package` | `db.migration` | Package of `JavaMigration` classes | | `migrax.resume` | `false` | Re-run a failed migration marked `-- migrax:resume-safe` | | `migrax.lock-timeout` | `0` | How long to wait while another instance migrates, for example `2m` when several start together | | `migrax.placeholders.` | | Values for `${name}` placeholders | | `migrax.schemas` | | Schemas to migrate one after another | ## Health check With `micronaut-management` in the application, Migrax adds a `migrax` health indicator: UP when every migration is applied and none failed, was edited or is missing, DOWN otherwise, with the counts as details (`/health`). --- Source: https://migrax.org/frameworks/helidon/ # Helidon ## Helidon MP ### Command line Migrax reads `META-INF/microprofile-config.properties` (and the `mp.config.profile`) together with `META-INF/persistence.xml`: ```properties title="META-INF/microprofile-config.properties" javax.sql.DataSource.shop.dataSourceClassName=org.postgresql.ds.PGSimpleDataSource javax.sql.DataSource.shop.dataSource.url=jdbc:postgresql://localhost:5432/shop javax.sql.DataSource.shop.dataSource.user=shop javax.sql.DataSource.shop.dataSource.password=secret ``` ```xml title="META-INF/persistence.xml" shop ``` When several data sources are configured, Migrax uses the one `persistence.xml` names. The `jdbcUrl`, `username` and `password` keys of other connection pools work too, and so do the MicroProfile environment variables, such as `JAVAX_SQL_DATASOURCE_SHOP_DATASOURCE_URL`. `hibernate.*` properties from `persistence.xml` are passed to Hibernate. **Naming:** Helidon keeps Hibernate's default naming (`jpa`). ### Migrate at startup ```xml org.migrax migrax-helidon 0.4.0 ``` Migrations run when the application starts, before your own beans and before JPA is first used, so `hibernate.hbm2ddl.auto=validate` works. Settings go in `microprofile-config.properties`: | Setting | Default | Meaning | |---|---|---| | `migrax.enabled` | `true` | Apply migrations at startup | | `migrax.datasource` | the one `persistence.xml` names, or the only one | Data source to migrate | | `migrax.locations` | `db/migration` | Migration folder on the classpath | | `migrax.java-package` | `db.migration` | Package of `JavaMigration` classes | | `migrax.resume` | `false` | Re-run a failed migration marked `-- migrax:resume-safe` | | `migrax.lock-timeout` | `0` | How long to wait while another instance migrates, for example `2m` when several start together | | `migrax.placeholders.` | | Values for `${name}` placeholders | | `migrax.schemas` | | Schemas to migrate one after another | ### Readiness check With `helidon-microprofile-health` in the application, Migrax adds a `migrax` readiness check: UP when every migration is applied and none failed, was edited or is missing, DOWN otherwise, with the counts as data (`/health/ready`). ## Helidon SE Helidon SE uses its database client rather than JPA, so there are no entities to generate migrations from. Write migrations with `migrax new` and apply them with `migrax migrate` in your deployment. Migrax reads the connection from `application.yaml`: ```yaml db: connection: url: jdbc:postgresql://localhost:5432/shop username: shop password: secret ``` --- Source: https://migrax.org/frameworks/jakarta-ee/ # Jakarta EE and plain Hibernate Any project with JPA entities works, with or without an application framework. ## Database settings Migrax reads the first persistence unit in `src/main/resources/META-INF/persistence.xml`: ```xml ``` `javax.persistence.jdbc.*` and `hibernate.connection.*` work too. On an application server, the data source is usually defined in the server's own configuration, which Migrax doesn't read: set the connection with environment variables or options instead. ```bash export MIGRAX_DATABASE_URL=jdbc:postgresql://db:5432/shop export MIGRAX_DATABASE_USER=shop export MIGRAX_DATABASE_PASSWORD=secret migrax migrate ``` ## Naming Plain Hibernate keeps names as written (`jpa` naming). A physical or implicit naming strategy in `persistence.xml` (`hibernate.physical_naming_strategy`) is used when Migrax reads the mapping. ## Running migrations Run `migrax migrate` as a step of your deployment, before the new version starts. !!! note "Other JPA providers" Migrax reads the mapping through Hibernate. With another JPA provider it scans the annotations and applies Hibernate's naming rules; check the generated names, and use `migrax drift --entities` to compare. --- Source: https://migrax.org/frameworks/maven/ # Maven plugin Everything the command line does is also available as Maven goals, which is handy in CI or when you prefer not to install the CLI. ## Setup ```xml title="pom.xml" org.migrax migrax 0.4.0 ``` Install it into your local Maven repository first (see [Getting the integration libraries](index.md)). Goals compile the project and use its runtime classpath, so no other configuration is needed. ## Goals ```bash mvn migrax:generate mvn migrax:migrate mvn migrax:status ``` | Goal | Same as | |---|---| | `migrax:generate` | `migrax generate` | | `migrax:migrate` | `migrax migrate` | | `migrax:status` | `migrax status` | | `migrax:plan` | `migrax plan` | | `migrax:check` | `migrax check` | | `migrax:lint` | `migrax lint` | | `migrax:verify` | `migrax verify` | | `migrax:drift` | `migrax drift` | | `migrax:rollback` | `migrax rollback` | | `migrax:repair` | `migrax repair` | | `migrax:clean` | `migrax clean` | | `migrax:new` | `migrax new` | | `migrax:squash` | `migrax squash` | | `migrax:merge` | `migrax merge` | | `migrax:import` | `migrax import` (`-Dmigrax.from=flyway` or `liquibase`) | ## Options Options are `-Dmigrax.*` properties (or `` elements of the same name): | Property | CLI option | Goals | |---|---|---| | `migrax.allowDestructive=true` | `--allow-destructive` | generate | | `migrax.renames=customers.email=contact_email` | `--rename` | generate | | `migrax.renameTables=client=customer` | `--rename-table` | generate | | `migrax.name=split_name` | `--name` | generate, new | | `migrax.safe=true` | `--safe` | generate | | `migrax.dryRun=true` | `--dry-run` | migrate, rollback, clean | | `migrax.resume=true` | `--resume` | migrate | | `migrax.lockTimeout=2m` | `--lock-timeout` | migrate, rollback, repair, clean, generate | | `migrax.steps=2`, `migrax.to=` | `--steps`, `--to` | rollback | | `migrax.confirm=true` | `--yes` | rollback, repair, clean | | `migrax.action=applied` | `--action` | repair | | `migrax.impact=true` | `--impact` | plan | | `migrax.all=true`, `migrax.strict=true` | `--all`, `--strict` | lint | | `migrax.verifyUrl=`, `migrax.skipRollbacks=true` | `--url`, `--skip-rollbacks` | verify | | `migrax.entities=true` | `--entities` | drift | | `migrax.java=true` | `--java` | new | | `migrax.url`, `migrax.user`, `migrax.password` | `--url`, `--user`, `--password` | all | | `migrax.package`, `migrax.naming`, `migrax.dialect`, `migrax.locations`, `migrax.schemas` | same names | all | Hints in the output use Maven syntax, for example: ```console $ mvn migrax:generate [ERROR] Review them, then run 'mvn migrax:generate -Dmigrax.allowDestructive=true'. ``` --- Source: https://migrax.org/frameworks/gradle/ # Gradle plugin ## Setup ```kotlin title="settings.gradle.kts" pluginManagement { repositories { mavenLocal() gradlePluginPortal() } } ``` ```kotlin title="build.gradle.kts" plugins { java id("org.migrax") version "0.4.0" } migrax { packageName.set("com.example.shop") // optional; defaults to the project group } ``` Install the plugin into your local Maven repository first (see [Getting the integration libraries](index.md)). ## Tasks | Task | Same as | |---|---| | `migraxGenerate` | `migrax generate` | | `migraxMigrate` | `migrax migrate` | | `migraxStatus` | `migrax status` | | `migraxPlan` | `migrax plan` | | `migraxCheck` | `migrax check` | | `migraxLint` | `migrax lint` | | `migraxVerify` | `migrax verify` | | `migraxDrift` | `migrax drift` | | `migraxRollback` | `migrax rollback` | | `migraxRepair` | `migrax repair` | | `migraxNew` | `migrax new` | | `migraxSquash` | `migrax squash` | | `migraxMerge` | `migrax merge` | Pass CLI options with `-Pmigrax.args`: ```bash gradle migraxGenerate -Pmigrax.args="--allow-destructive" gradle migraxRollback -Pmigrax.args="--steps 2 --yes" ``` ## The `migrax` extension | Property | Meaning | |---|---| | `packageName` | Entity package | | `url`, `user`, `password` | Database connection (default: application config) | | `locations` | Migration folder, e.g. `classpath:db/migration` | | `naming` | `spring`, `jpa`, `jpa-snake` or `micronaut` | | `dialect` | `postgresql`, `mysql`, `mariadb`, `sqlserver`, `oracle` or `h2` | | `args` | Extra arguments for every task, e.g. `--verbose` | | `version` | Migrax version used to run the tasks | --- Source: https://migrax.org/reference/cli/ # CLI commands Run `migrax help ` for the same information in the terminal. Run every command from the service folder (the one with `pom.xml` or `build.gradle`), or pass `--dir `. ## Options for every command | Option | Meaning | |---|---| | `--dir ` | Project folder (default: current folder) | | `--no-build` | Do not run Maven/Gradle; use the classes and dependencies from the last build | | `--refresh` | Re-resolve dependencies even if cached | | `--json` | Machine-readable output (`plan`, `status`, `check`, `lint`, `drift`, `verify`) | | `--verbose`, `-v` | Show debug output, Hibernate's own log and stack traces | | `--no-input` | Never ask questions (the default when input is not a terminal) | ## Exit codes | Code | Meaning | |---|---| | `0` | Success | | `1` | Error, failed check, or failed migration | | `2` | Differences found: `check` (entity changes without a migration), `drift` (database differs) | ## Getting started ### init ``` migrax init ``` Creates the .migrax folder and the migration folder, then shows the entity package, database, dialect and naming Migrax detected. Safe to run more than once. | Option | Meaning | |---|---| | `--package ` | Entity package (default: MIGRAX_PACKAGE or pom groupId) | | `--locations ` | Migration folder, e.g. filesystem:db/sql | | `--naming ` | spring, jpa, jpa-snake or micronaut (default: detected) | | `--dir ` | Project folder (default: current folder) | ### doctor ``` migrax doctor ``` Runs every check Migrax needs and explains how to fix anything that fails. Exits with code 1 when a check fails. | Option | Meaning | |---|---| | `--json` | Machine-readable output | | `--package ` | Entity package (default: MIGRAX_PACKAGE or pom groupId) | | `--url ` | Database URL (default: application config) | | `--user ` | Database user | | `--password ` | Database password (prefer env vars) | | `--password-file ` | Read the database password from a file (mounted secret) | | `--classpath ` | Extra classpath; skips Maven/Gradle resolution | | `--no-build` | Do not run Maven/Gradle; use compiled classes | | `--extractor ` | auto, hibernate or annotations (default: auto) | | `--dir ` | Project folder (default: current folder) | ### mcp ``` migrax mcp [--allow-generate] ``` Runs a Model Context Protocol server on stdin/stdout, so AI assistants such as Claude Code can call status, check, plan, lint, drift, doctor, verify and show_migration as tools and read their JSON results. It never changes a database: migrate, rollback, repair and clean are not offered. `--allow-generate` also offers generate, which writes migration files for the person to review. Claude Code: `claude mcp add migrax -- migrax mcp`. See [Using Migrax with AI assistants](../guides/ai-assistants.md). | Option | Meaning | |---|---| | `--allow-generate` | Also offer the generate tool (writes migration files) | | `--dir ` | Project folder (default: current folder) | ### import ``` migrax import flyway|liquibase ``` flyway: records every migration in flyway_schema_history as applied, so Migrax continues where Flyway stopped; `V*__` and `R__` files keep working. liquibase: writes a baseline migration with the current schema and records it. Afterwards run `migrax generate` to start managing changes, and remove Flyway or Liquibase from the project. See [Switching from Flyway or Liquibase](../guides/switching-tools.md). | Option | Meaning | |---|---| | `--table ` | Flyway history table (default: flyway_schema_history) | | `--name ` | Migration file name (default: next number + description) | | `--schema ` | Database schema to read | | `--url ` | Database URL (default: application config) | | `--user ` | Database user | | `--password ` | Database password (prefer env vars) | | `--password-file ` | Read the database password from a file (mounted secret) | | `--locations ` | Migration folder, e.g. filesystem:db/sql | | `--dialect ` | postgresql, cockroachdb, mysql, mariadb, sqlserver, oracle, h2, sqlite | | `--classpath ` | Extra classpath; skips Maven/Gradle resolution | | `--no-build` | Do not run Maven/Gradle; use compiled classes | | `--dir ` | Project folder (default: current folder) | ## Everyday ### generate *Alias:* `makemigrations` ``` migrax generate [--name ] [--allow-destructive] [--safe] ``` Compiles the project if needed, compares the entities with `.migrax/snapshot.json` and writes a numbered SQL file plus a rollback script (`rollback/`). On the first run, with no snapshot, the current database schema is the baseline: its tables are written to `0001_baseline.sql`, which that database records as applied without running it. When a column or table seems renamed, Migrax asks (or pass `--rename` / `--rename-table`) so the data is kept. Drops need `--allow-destructive`. `--safe` (PostgreSQL, CockroachDB) builds indexes and constraints on existing tables without blocking writes, in a second migration. Always review the generated SQL; Migrax lints it for you. | Option | Meaning | |---|---| | `--name ` | Migration file name (default: next number + description) | | `--allow-destructive` | Allow drops and narrowing type changes | | `--safe` | Non-blocking indexes and constraints (PostgreSQL, CockroachDB) | | `--rename t.old=new` | Treat a column change as a rename (comma-separated) | | `--rename-table old=new` | Treat a table change as a rename | | `--no-input` | Never ask questions | | `--lock-timeout