PostgreSQL migration tool on Node.js.
This SQL migration tool is designed to facilitate seamless database schema evolution. It operates based on a directory containing migration scripts and a defined order of execution.
- Migration Files: Each migration consists of two files:
{name}.do.sqlfor applying the migration and{name}.undo.sqlfor reverting it. - Order File: An
orderfile lists migration names in the sequence they should be applied. It also supports@baselineand@shrinkdirectives.
- Applying New Migrations: When a new migration is added to the directory and listed in the
orderfile, the tool executes the corresponding.do.sqlfile. - Reverting Migrations: If a migration is removed from the order file (in the feature zone), the tool executes the stored
.undo.sqland deletes the history row. - Modifying Migrations: If a feature-zone migration is altered, the tool reverts from the end of the list to the change point, then reapplies.
- Baseline zone: Names listed before
@baselineare matched by name only (hash changes do not trigger undo/redo). - Shrink: Names listed under
@shrinkare deleted fromschema_versionswithout running undo. Clear the@shrinksection after a successful--exec.
Columns:
- name
- do_sql — executed migration
- undo_sql — undo migration SQL
- do_hash — SHA-256 of do SQL
- undo_hash — SHA-256 of undo SQL
- exec_date — execution date
skip— already appliedshrink— remove history row without undo (@shrink)remove— run undo and delete history rowadd— run do and insert history rowadopt— insert history row without running do (new baseline on a non-empty DB)change— reserved (inspect emits remove+add instead)
Example order file:
init_2026_03_06_120000
@baseline
feature_1
feature_2
@shrink
init
feature_old_1
feature_old_2Example migrations dir:
init_2026_03_06_120000.do.sql
init_2026_03_06_120000.undo.sql
feature_1.do.sql
feature_1.undo.sql
feature_2.do.sql
feature_2.undo.sql
orderWhen the migration list grows too large, squash history into a new init snapshot:
npx do-migrate baseline
npx do-migrate --exec
# then remove the @shrink section from order (optional cleanup)Or in one step:
npx do-migrate baseline --execThis will:
- Dump the live schema with
pg_dump(requires PostgreSQL client tools) - Create
init_{timestamp}.do.sql/.undo.sql - Rewrite
ordertoinit_{timestamp}+@baselineand move previous migrations into@shrink - With
--exec: shrink old history rows and adopt the new init (no do on a live DB)
Fresh databases with the new order will add (execute) the baseline dump, then apply later features.
To undo a feature migration, remove it from the list after @baseline and do not put it in @shrink.
To drop a migration from history without undo (folded into baseline), list it under @shrink.
npm install do-migrateOr use Docker. The baseline command needs pg_dump available.
View action plan:
npx do-migrate [options]Execute:
npx do-migrate --exec [options]Create baseline snapshot:
npx do-migrate baseline [--name <name>] [--exec] [options]Options:
- --exec — Execute planned actions
- --host <host> — Database hostname (default: "localhost", env: MIGRATOR_DB_HOST)
- --port <port> — Database port (default: 5432, env: MIGRATOR_DB_PORT)
- --user <username> — Database user (env: MIGRATOR_DB_USER)
- --password <password> — Database password (env: MIGRATOR_DB_PASSWORD)
- --database <name> — Database name (env: MIGRATOR_DB_DATABASE)
- --schema-table <name> — Migrator sync table name (default: "schema_versions", env: MIGRATOR_TABLE_NAME)
- --schema-name <name> — Database schema name (default: "public", env: MIGRATOR_DB_SCHEMA_NAME)
- --path <path> — Path to migrations dir (env: MIGRATOR_FILES_PATH)
- -V, --verbose — Show skipped migrations in output
- -v, --version — Output the current version
- -h, --help — Display help
Env variables:
MIGRATOR_DB_HOST— Database host (default: localhost)MIGRATOR_DB_PORT— Database port (default: 5432)MIGRATOR_DB_USER— Database user (default: postgres)MIGRATOR_DB_PASSWORD— Database password (default empty)MIGRATOR_DB_DATABASE— Database name (default: postgres)MIGRATOR_DB_SCHEMA_NAME— Database schema name (default: public)MIGRATOR_DB_SSL— SSL enable flag (true/false, default: false)MIGRATOR_DB_SSL_CHECK— SSL rejectUnauthorized (true/false, default: false)MIGRATOR_DB_SSL_CA— SSL CA (pem string)MIGRATOR_FILES_PATH— Path to migration dir (default: ./migrations). Legacy alias:MITRATOR_FILES_PATHMIGRATOR_ORDER_FILE— Order file name (default: order). Legacy alias:MITRATOR_ORDER_FILEMIGRATOR_TABLE_NAME— Sync table name (default: schema_versions)
import Migrator, { Config } from 'do-migrate';
const migrator = new Migrator(config);
await migrator.inspect();
await migrator.migrate((action) => console.log(action));
await migrator.baseline(); // rewrite files; then migrate() to apply
await migrator.close();Inspect:
docker run --rm -e MIGRATOR_DB_HOST=... -v ./migrations:/migrator/migrations nim579/do-migrateExecute:
docker run --rm -e MIGRATOR_DB_HOST=... -v ./migrations:/migrator/migrations nim579/do-migrate --execDocker Compose:
services:
migrator:
image: nim579/do-migrate:2
command: ["--exec"]
environment:
- MIGRATOR_DB_HOST
- MIGRATOR_DB_PORT
- MIGRATOR_DB_USER
- MIGRATOR_DB_PASSWORD
- MIGRATOR_DB_DATABASE
volumes:
- ./migrations:/migrator/migrations