# Refinery for Rust: SQL migrations with versioned history

> Refinery is an MIT-licensed Rust toolkit for applying SQL or Rust-defined migrations through supported database drivers or its CLI. It records applied versions and checksums, supports contiguous and non-contiguous numbering, and leaves rollback migration design to the application.

**rust-db/refinery** — Powerful SQL migration toolkit for Rust.

- Repository: https://github.com/rust-db/refinery
- Stars: 1,712 · Forks: 169
- Language: Rust
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/rust-db-refinery

## What Refinery changes in a Rust database deployment

Refinery addresses the problem of applying database schema changes in a predictable order while keeping a record of what has already run. It is aimed at Rust applications that use SQL migration files, Rust modules that generate migration SQL, or both. The supported database integrations listed in the README are `postgres`, `tokio-postgres`, `mysql`, `mysql_async`, `rusqlite`, and `tiberius`. The library can also work with an unsupported connection type when the application supplies a `Config`, but a supported driver feature is still required for `Runner::run` or `Runner::run_async`.

The central design choice is that migrations belong to the application rather than to a loose directory discovered at deployment time. Migrations can be embedded with `embed_migrations`, or the CLI can read migration files from a configured path. That gives teams a choice between shipping SQL alongside the application and baking migrations into the binary. The README states that the crate ignores new migration files until the source code is rebuilt. This prevents an application from accidentally seeing a newly added file during deployment, but it also means a migration cannot be introduced independently of an application build.

Refinery supports two naming conventions. Files beginning with `V` are treated as strictly versioned, while files beginning with `U` are treated as non-contiguous versions. Both formats require a version and name after the prefix, separated by a double underscore. The distinction affects how a team manages merge order and deployed migration numbers, rather than changing the SQL execution mechanism.

## Migration files, Rust modules, and history checks

Migration files must use names such as `V1__create_users.sql`, `U11__update_cars_table.sql`, or an equivalent `.rs` module name. A Rust migration module must expose a function named `migration` that returns a `String`. Refinery works especially well with Barrel for generating or organizing migrations, but the README also permits ordinary `.sql` files and other Rust crates for schema generation.

When a runner executes, Refinery creates a table that stores applied migration versions and metadata. It compares the recorded history with the migrations available to the runner, looking for divergent and missing migrations. Those checks are configured through `set_abort_divergent` and `set_abort_missing`. The README does not define the exact checksum algorithm or every metadata column, so those details should be confirmed in the API documentation before depending on them.

By default, each migration runs in its own transaction. Calling `set_grouped(true)` changes the execution model so that all migrations are wrapped in one transaction. This choice affects failure handling. With individual transactions, an earlier migration can remain committed if a later migration fails. With grouped execution, the shared transaction can preserve all-or-nothing behavior, but the database and migration contents must be compatible with that transaction scope.

Version numbers normally use `i32`. Enabling the `int8-versions` feature changes that restriction to `i64`. The README warns that this feature must be enabled before a database starts using Refinery. It also warns that converting an existing `refinery_schema_history` table to `int8` versions will break checksums for previously applied migrations. This is an important adoption constraint for teams that already maintain migration history.

## Contiguous and non-contiguous numbering for competing releases

The `V` prefix is suited to teams whose migration versions remain adjacent and whose development order matches deployment order. If `V1` is applied, the next expected contiguous migration should be `V2`. Refinery can therefore identify a version that has not been applied when the numbering has moved forward unexpectedly.

The `U` prefix provides more flexibility for branches and pull requests that are reviewed or merged at different times. The README describes a case where one developer waits to merge `U11__update_cars_table.sql`, while another merges and deploys `U12__create_model_tags.sql`. With the non-contiguous convention, the lower-numbered migration is not blocked by the newer one. This is useful when several migration branches are in flight, but the application and deployment process still need a clear ordering policy.

The choice is not a substitute for migration review. Refinery records versions and checksums, while the SQL still determines the schema effect and compatibility implications. A migration that changes data or introduces a constraint may need deployment coordination across multiple application instances. The README does not describe locking behavior, concurrent runner coordination, or application compatibility checks, so teams should not infer those guarantees from the history-table description alone.

## Installing Refinery and running the first migration

The README instructs users to add Refinery to `Cargo.toml` with the selected driver feature. The following dependency example is copied from the README, so the version shown there is the documented example rather than a claim about the latest release.

```toml
refinery = { version = "0.8", features = ["rusqlite"] }
```

For an embedded SQLite migration, the README defines a module using `embed_migrations!`, opens an in-memory database, and runs the generated migrations runner. The migration path must point to a directory that contains files following Refinery's naming rules.

```rust
use rusqlite::Connection;

mod embedded {
    use refinery::embed_migrations;
    embed_migrations!("./tests/sql_migrations");
}

fn main() {
    let mut conn = Connection::open_in_memory().unwrap();
    embedded::migrations::runner().run(&mut conn).unwrap();
}
```

The CLI example sets `DATABASE_URL`, changes into a `migrations` directory, and runs files from `./src`. The `-t 1` option is part of the README example, and its meaning is not defined in the README excerpt.

```bash
export DATABASE_URL="postgres://postgres:secret@localhost:5432/your-db"
pushd migrations
    # Runs ./src/V1__*.rs or ./src/V1__*.sql
    refinery migrate -e DATABASE_URL -p ./src -t 1
popd
```

A pool integration uses `run_async` on a client obtained from a `deadpool` or `bb8` connection. The README shows `dedicated_connection()` for `bb8`, but does not provide a complete pool setup, so the surrounding application configuration remains the reader's responsibility.

## Rollback behavior and operational boundaries

Refinery does not provide automatic rollback migrations. Its design is based on an earlier Flyway philosophy, and the README says that undoing a migration requires creating a new migration that describes the reverse operation. This is a deliberate workflow constraint. An application can therefore keep a forward-only migration history while retaining explicit control over corrective SQL.

That approach can be safer for destructive or data-changing operations, because an automatic reversal may not be possible or desirable. It also puts responsibility on the team to decide whether a new migration should restore data, remove a column, reverse an index, or perform a more selective repair. The README does not specify whether such a corrective migration is automatically linked to the original migration. It should be treated as a new versioned migration.

Refinery's migration execution also depends on the database driver feature selected for the runner. Supporting an unsupported driver through `Config` does not remove the feature requirement for the underlying supported driver. This distinction matters for projects evaluating SQLx: the README says SQLx can provide the connection context through `Config`, but a supported driver feature is still needed for the relevant runner method. The exact integration should be checked against the current API documentation.

The project is not archived. The repository's last push was on 2026-10-02, and the listed releases include `v0.10.0` from 2026-10-02, `v0.9.2` from 2026-06-10, and `v0.9.1` from 2026-04-15. The MIT license permits use and modification under its terms, but the repository's `LICENSE` file remains the authoritative text.

## Refinery compared with migration tooling built around SQLx

A relevant alternative approach is to use a migration system centered on SQLx. SQLx provides database integration for Rust and is explicitly named in the README as an unsupported driver for direct Refinery connection types. The practical difference is that Refinery offers its own migration runner, history table, naming rules, checksum validation, and migration execution controls. A SQLx-based workflow would instead keep SQLx as the database stack and use its migration facilities, but the README does not document SQLx's migration commands or comparison behavior.

That comparison should stay narrow because only Refinery's documented behavior is available here. Refinery is attractive when an application wants migrations embedded in the binary, `V` or `U` naming, and a history table managed through `Runner`. It is less suitable when automatic undo files are a requirement, because Refinery explicitly expects corrective migrations instead. It also adds a feature-selection decision: applications must enable the supported driver needed by the runner even when they use a different connection abstraction.

The workspace contains separate `refinery`, `refinery_cli`, `refinery_core`, and `refinery_macros` members, plus an `examples` member. The repository also includes a `Dockerfile` that builds `refinery_cli` with all features and places the binary at `/usr/local/bin/refinery`. The Dockerfile does not document how to supply `DATABASE_URL`, migrations, or application configuration at runtime, so container-based adoption requires separate operational setup.

## Conclusion

Adopt Refinery when a Rust service needs versioned SQL migrations with an application-managed migration history and you can follow its database-driver feature requirements. Do not choose it if you need automatic undo migrations, because the README says that rollback requires writing a new migration to reverse an earlier change. Verify first that your driver is supported, your files follow the exact naming format, and your database has not already used an incompatible version type.

## FAQ

### Which database drivers does Refinery support?

The README lists `postgres`, `tokio-postgres`, `mysql`, `mysql_async`, `rusqlite`, and `tiberius`. It also explains that an unsupported driver such as SQLx can be used through `Config`, while a supported driver feature is still required for the relevant runner method.

### How should I name a Refinery migration file?

Migration files must use `[U|V]{1}__{2}.sql` or `[U|V]{1}__{2}.rs`, where `{1}` is the version and `{2}` is the migration name. Use `V` for contiguous migrations or `U` for non-contiguous migrations.

### Does Refinery automatically roll back a migration?

No. The README says rollback or undo behavior requires generating a new migration that explicitly performs the reversal. Refinery keeps a history of applied migrations rather than automatically choosing an undo operation.

### Can Refinery migrations be embedded in a Rust binary?

Yes. The `embed_migrations!` macro embeds migrations from a specified directory, and the generated runner can be called with a supported connection. The README also notes that embedded migrations do not require additional migration files in the deployed application.

## Sources

- [Issues](https://github.com/rust-db/refinery/issues)
- [License: MIT](https://github.com/rust-db/refinery/blob/main/LICENSE)
- [README](https://github.com/rust-db/refinery/blob/main/README.md)
- [Releases](https://github.com/rust-db/refinery/releases)
- [rust-db/refinery on GitHub](https://github.com/rust-db/refinery)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rust-db-refinery
