Reshape migrations
Zero-downtime database schema migrations, managed automatically.
Zero-downtime schema migrations using Reshape, built into Specific.
Reshape uses a 3-phase approach so old and new application code can run against the database at the same time:
- Start - creates the new schema version with views and triggers. Both old and new schemas are available simultaneously.
- Rollout - new application code is deployed and uses the new schema via
search_path. - Complete - the old schema and migration artifacts are removed.
Enabling Reshape
Add a reshape block to a postgres definition in specific.hcl:
postgres "main" {
reshape {
enabled = true
}
}
With a custom migrations directory (the default is migrations/, relative to specific.hcl):
postgres "main" {
reshape {
enabled = true
migrations_dir = "db/migrations"
}
}
Writing migrations
Migrations are TOML files composed of actions (create_table, add_column, alter_column, add_index, custom, and more), processed in lexical order (001_first.toml before 002_second.toml):
[[actions]]
type = "create_table"
name = "users"
primary_key = ["id"]
[[actions.columns]]
name = "id"
type = "INTEGER"
generated = "ALWAYS AS IDENTITY"
[[actions.columns]]
name = "email"
type = "TEXT"
nullable = false
The full list of actions, with every option and examples, is bundled with the CLI: run specific docs postgres/reshape/actions, and specific docs postgres/reshape/actions/<action> for a specific one.
Automatic management in development
When specific dev starts with Reshape enabled, migrations are managed for you:
- All migrations except the last are completed - these become permanent schema changes.
- The last migration is started but not completed - so you can iterate on it.
- Completed migration files are made read-only - preventing accidental modification.
While specific dev is running:
- Modifying the last migration file aborts and restarts that migration. Services keep running - no restart needed, since the schema name doesn’t change.
- Adding a new migration file completes the previous migration (making it permanent) and starts the new one. Services are restarted to use the new schema.
The development workflow:
- Write a migration file in the migrations directory.
- Run
specific checkto validate. specific devapplies it automatically.- Iterate on the migration - run
specific checkafter every change. - When ready, add a new migration file to lock in the previous one.
If you need to modify a completed migration, change its file permissions first; completed files are read-only on purpose.
Connection strings
When Reshape is enabled, postgres.<name>.url automatically includes the correct search_path. No service configuration changes needed:
service "api" {
build = build.api
command = "node server.js"
env = {
DATABASE_URL = postgres.main.url # Automatically includes search_path
}
}
Manual migration commands
You can also control migrations directly:
# Validate migration files (works without specific dev running)
specific reshape check
# Start a migration (applies new schema while keeping the old one available)
specific reshape start
# Check migration status on a specific database
specific reshape status main
# Complete a migration (removes old schema)
specific reshape complete main
# Abort a migration (rolls back to old schema)
specific reshape abort main
Most actions need database access; if specific dev isn’t running, the database is started temporarily. check only validates file syntax and runs anytime.
Production
During specific deploy, migrations follow the same 3-phase flow, coordinated with the service rollout: pending migrations are started before the new code rolls out, and completed once the rollout succeeds. If the rollout fails, the migrations are aborted automatically and the database rolls back to the old schema.