Skip to main content
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:
  1. Start - creates the new schema version with views and triggers. Both old and new schemas are available simultaneously.
  2. Rollout - new application code is deployed and uses the new schema via search_path.
  3. Complete - the old schema and migration artifacts are removed.

Enabling Reshape

Add a reshape block to a postgres definition in specific.hcl:
specific.hcl
postgres "main" {
  reshape {
    enabled = true
  }
}
With a custom migrations directory (the default is migrations/, relative to specific.hcl):
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):
migrations/001_create_users.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
Run specific check every time you create or edit a migration file. It validates both specific.hcl and all migration files, catching errors before they’re applied. Without it, invalid migrations silently fail to apply.
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:
  1. All migrations except the last are completed - these become permanent schema changes.
  2. The last migration is started but not completed - so you can iterate on it.
  3. 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:
  1. Write a migration file in the migrations directory.
  2. Run specific check to validate.
  3. specific dev applies it automatically.
  4. Iterate on the migration - run specific check after every change.
  5. 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:
specific.hcl
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.