ServiceProvider, VendorPublishCommand, and Migrator. Framework references point to v13.34.0.
Copy to the application, or load from the package?
publishesMigrations() only registers a source and destination as publishable. Booting the provider does not copy files or run any SQL.
loadMigrationsFrom(), on the other hand, registers a search path with the Migrator. A normal migrate run will include files in that path, but booting the provider alone does not run them.
If your design lets users adjust table names or columns before running, publishing is a good candidate. If the package owns the schema and does not expect users to edit the files, consider loading directly. The two provider examples below are alternatives.
Implementing the publish approach
Give the migrations a package-specific tag so users can publish them separately from other resources.Timestamp changes depend on configuration
The official documentation describes migration timestamps being updated to the current date and time on publish. However, the implementation ofServiceProvider::publishesMigrations() only adds the source to the set of timestamp-updated paths when database.migrations.update_date_on_publish is enabled. The fallback when reading this setting is false.
The default Laravel 13 application ships with the following in config/database.php. For applications carried over from older structures, check that the setting exists.
VendorPublishCommand then rewrites the date when the file matches the real path of a registered source and the destination name has the YYYY_MM_DD_HHMMSS_ format. Starting from the time the command began, it adds one second per file. If the name lacks this format, that step does not prepend a date.
Timestamp updates depend on both the package’s registration and the consuming application’s configuration. Do not change this setting globally from the package’s provider; document the requirement in your installation instructions. If the application uses config caching, the cache must also be rebuilt after changing the setting.
Republishing does not “add only what has not run”
vendor:publish does not check the database’s migration history. In addition, the copy logic in v13.34.0 checks for an existing file at the destination path before the timestamp is changed. Even when publishing a directory, it first checks whether the same relative path as the source exists at the destination, and only then rewrites the date.
As a result, if the first publish changed the dates and no file with the source’s original name exists in the application, publishing the same tag again can add files with different dates. Do not assume that omitting --force always prevents duplicates.
Whether a migration has run is decided by file name
Migrator::getMigrationName() returns the file’s base name without .php. The pending check compares this name against the migration history. It does not compare PHP contents or table names.
Implementing the direct loading approach
To run the package’s migrations as they are, register a search path. With this approach, do not also publish the same files.loadMigrationsFrom() calls path() when the Migrator is resolved. Migrator::path() deduplicates search paths, and getMigrationFiles() keys the found files by migration name and sorts them by that name.
When users update the package, new files are picked up by the next migrate. Do not rename existing files; add a new file for each new schema change. To avoid collisions with other packages, include the feature name, as in create_courier_deliveries_table. Files with the same name share the same key, so both are not run independently.
Delivering schema changes to existing users
For example, to add a tracking code to the deliveries table, add a new migration for the change instead of editing the publishedcreate_courier_deliveries_table. Editing an existing create migration does not apply the change for users who have already run it.
database/migrations/2026_10_02_000000_add_tracking_code_to_courier_deliveries_table.php
Checks before release
In addition to the package’s database tests, verify the publish and update procedures in a consuming application. Loading migrations directly in tests does not verify the publish approach, where file names change.- A first install on an empty database creates the required tables.
- Updating from a previous release’s database and migration history applies only the new changes.
- Repeating the same publish command and inspecting the file list confirms the update procedure does not create duplicates.
- The procedure accounts for date updates being enabled or disabled, and for edits to published files.
- Rollback of new migrations and their execution order relative to the application’s other migrations are verified.
Related pages
Migrations
Review the basics of schema definitions, migration history, and rollbacks.
Package Testing
Test package service providers and database behavior.
Package Config Merging and Caching
Review update procedures that account for user config and config caching.
Version Compatibility Management
Tie update procedures and compatibility changes to your release policy.