.sql files against your configured datasources and tracks each applied migration in a winter_migrations table. Version state is determined by the relative file path — if a path has already been recorded in the tracking table, the file is skipped on subsequent runs. This gives you repeatable, idempotent deploys without requiring any migration-specific DSL.
Quick Start
Follow these steps to apply your first migration:1
Enable migrations in application.yml
Add
migrations.enabled: true to the datasource you want to migrate.application.yml
2
Create the migrations directory
Create a sub-folder named after your datasource under your migrations root.
3
Add a SQL migration file
Create
/migrations/defaultdb/001-init-schema.sql with your schema statements:001-init-schema.sql
4
Run the migrations
Execute the migration tool from the CLI or using the pre-built PHAR.
5
Verify the migration was applied
Query the
winter_migrations tracking table to confirm the file was recorded:Configuration
Standalone Datasource
Enable migrations on any datasource by settingmigrations.enabled: true. You can enable it on multiple datasources simultaneously — each datasource’s folder is migrated independently.
application.yml
Multi-Tenant Datasource
Migrations work with multi-tenant datasources too. The tool runs each SQL file against every tenant returned byTenantDataSourceProvider::getAllTenantIds().
application.yml
Native CLI Mode (useCli)
By default, the migration tool parses SQL files in PHP and executes each statement individually. For complex SQL files that contain transactions, PL/SQL or T-SQL blocks, stored procedures, triggers, or mixed DDL/DML, set useCli: true to hand the entire file to the database’s native command-line client instead.
application.yml
useCli: true is set, Winter Boot selects the appropriate CLI tool based on the DSN scheme:
Directory Structure
Organise migration files under a root directory with one sub-folder per datasource name. Files are executed in alphabetical order — use numeric or date-based prefixes to enforce a deterministic sequence.Each top-level sub-folder must exactly match the datasource
name in application.yml. The match is case-sensitive on Linux. SQL files must use the .sql extension. Sub-folders for release-based organisation are supported and scanned recursively.The winter_migrations Tracking Table
The framework automatically creates awinter_migrations table the first time it runs against a datasource. Each successfully executed migration is recorded by its relative path from the migrations root (e.g. defaultdb/001-init-schema.sql).
COUNT(*) WHERE migration_path = ?. Any file that already has a row is skipped entirely, making every run idempotent.
SQL File Format
Each file may contain one or more SQL statements. The parser supports both# and -- style line comments and ignores blank lines. Every statement must be terminated with a semicolon (;).
orders.sql
CLI Reference
Building the PHAR
Build a self-contained PHAR for use in Docker images or CI pipelines:Kubernetes Init Container
Run migrations as an init container so your schema is always up-to-date before the main application pod starts:pod.yaml
Migration Execution Flow
Understanding the exact sequence helps you predict behaviour and debug failures:1
Load configuration
Read
application.yml and collect all datasources with migrations.enabled: true.2
Locate SQL folder
For each datasource, resolve the SQL folder at
{sqlBasePath}/{datasource-name}/.3
Scan and sort
Recursively scan for
.sql files; sort alphabetically within each directory level.4
Enumerate tenants (multi-tenant only)
For multi-tenant datasources, retrieve all tenant IDs from
TenantDataSourceProvider::getAllTenantIds().5
Check tracking table
For each file (and each tenant, if multi-tenant): query
winter_migrations to check whether the file has already been applied.6
Execute new migrations
If not yet recorded, execute the file using PHP parser mode or native CLI mode (
useCli: true).7
Record success
Insert a row into
winter_migrations on successful execution.8
Halt on failure
Stop immediately on the first failure. No automatic rollback is performed — you must fix the failing statement and re-run.
Troubleshooting
"SQL folder not found" error
"SQL folder not found" error
The directory under
--sqlPath does not contain a sub-folder that exactly matches the datasource name. Verify your folder structure is {sqlPath}/{datasource-name}/ and confirm the datasource name in application.yml exactly matches the folder name — the comparison is case-sensitive on Linux.Migration not executing (file silently skipped)
Migration not executing (file silently skipped)
Check that If the file has already been recorded and you need to re-run it, delete its row from
migrations.enabled: true is present in application.yml for the target datasource, and that the file has a .sql extension. Then query the tracking table:winter_migrations. Use this with care in production environments.Native CLI tool not found (useCli: true)
Native CLI tool not found (useCli: true)
Install the appropriate client package and confirm the binary is on the system
PATH:Migration fails mid-way
Migration fails mid-way
Fix the failing SQL statement and manually roll back or compensate any partial changes. Re-run the migration tool — files already recorded in
winter_migrations are skipped, so only the failed (and unrecorded) file will be re-attempted.