Migrations
Payload exposes a full suite of migration controls available for your use. Migration commands are accessible via
the npm run payload
command in your project directory.
Ensure you have an npm script called "payload" in your package.json
file.
Migration file contents
Payload stores all created migrations in a folder that you can specify. By default, migrations are stored
in ./src/migrations
.
A migration file has two exports - an up
function, which is called when a migration is executed, and a down
function
that will be called if for some reason the migration fails to complete successfully. The up
function should contain
all changes that you attempt to make within the migration, and the down
should ideally revert any changes you make.
Here is an example migration file:
Using Transactions
When migrations are run, each migration is performed in a new transaction for you. All
you need to do is pass the req
object to any local API or direct database calls, such as
payload.db.updateMany()
, to make database changes inside the transaction. Assuming no errors were thrown, the transaction is committed
after your up
or down
function runs. If the migration errors at any point or fails to commit, it is caught and the
transaction gets aborted. This way no change is made to the database if the migration fails.
Using database directly with the transaction
Additionally, you can bypass Payload's layer entirely and perform operations directly on your underlying database within the active transaction:
MongoDB:
Postgres:
SQLite:
In SQLite, transactions are disabled by default. More.
Migrations Directory
Each DB adapter has an optional property migrationDir
where you can override where you want your migrations to be
stored/read. If this is not specified, Payload will check the default and possibly make a best effort to find your
migrations directory by searching in common locations ie. ./src/migrations
, ./dist/migrations
, ./migrations
, etc.
All database adapters should implement similar migration patterns, but there will be small differences based on the adapter and its specific needs. Below is a list of all migration commands that should be supported by your database adapter.
Commands
Migrate
The migrate
command will run any migrations that have not yet been run.
Create
Create a new migration file in the migrations directory. You can optionally name the migration that will be created. By default, migrations will be named using a timestamp.
Status
The migrate:status
command will check the status of migrations and output a table of which migrations have been run,
and which migrations have not yet run.
payload migrate:status
Down
Roll back the last batch of migrations.
Refresh
Roll back all migrations that have been run, and run them again.
Reset
Roll back all migrations.
Fresh
Drops all entities from the database and re-runs all migrations from scratch.
When to run migrations
Depending on which Database Adapter you use, your migration workflow might differ subtly.
In relational databases, migrations will be required for non-development database environments. But with MongoDB, you might only need to run migrations once in a while (or never even need them).
MongoDB
In MongoDB, you'll only ever really need to run migrations for times where you change your database shape, and you have lots of existing data that you'd like to transform from Shape A to Shape B.
In this case, you can create a migration by running pnpm payload migrate:create
, and then write the logic that you need to perform to migrate your documents to their new shape. You can then either run your migrations in CI before you build / deploy, or you can run them locally, against your production database, by using your production database connection string on your local computer and running the pnpm payload migrate
command.
Postgres
In relational databases like Postgres, migrations are a bit more important, because each time you add a new field or a new collection, you'll need to update the shape of your database to match your Payload Config (otherwise you'll see errors upon trying to read / write your data).
That means that Postgres users of Payload should become familiar with the entire migration workflow from top to bottom.
Here is an overview of a common workflow for working locally against a development database, creating migrations, and then running migrations against your production database before deploying.
1 - work locally using push mode
Payload uses Drizzle ORM's powerful push
mode to automatically sync data changes to your database for you while in development mode. By default, this is enabled and is the suggested workflow to using Postgres and Payload while doing local development.
You can disable this setting and solely use migrations to manage your local development database (pass push: false
to your Postgres adapter), but if you do disable it, you may see frequent errors while running development mode. This is because Payload will have updated to your new data shape, but your local database will not have updated.
For this reason, we suggest that you leave push
as its default setting and treat your local dev database as a sandbox.
For more information about push mode and prototyping in development, click here.
The typical workflow in Payload is to build out your Payload configs, install plugins, and make progress in development mode - allowing Drizzle to push your changes to your local database for you. Once you're finished, you can create a migration.
But importantly, you do not need to run migrations against your development database, because Drizzle will have already pushed your changes to your database for you.
2 - create a migration
Once you're done with working in your Payload Config, you can create a migration. It's best practice to try and complete a specific task or fully build out a feature before you create a migration.
But once you're ready, you can run pnpm payload migrate:create
, which will perform the following steps for you:
- We will look for any existing migrations, and automatically generate SQL changes necessary to convert your schema from its prior state to the new state of your Payload Config
- We will then create a new migration file in your
/migrations
folder that contains all the SQL necessary to be run
We won't immediately run this migration for you, however.
3 - set up your build process to run migrations
Generally, you want to run migrations before you build Payload for production. This typically happens in your CI pipeline and can usually be configured on platforms like Payload Cloud, Vercel, or Netlify by specifying your build script.
A common set of scripts in a package.json
, set up to run migrations in CI, might look like this:
In the example above, we've specified a ci
script which we can use as our "build script" in the platform that we are deploying to production with.
This will require that your build pipeline can connect to your database, and it will simply run the payload migrate
command prior to starting the build process. By calling payload migrate
, Payload will automatically execute any migrations in your /migrations
folder that have not yet been executed against your production database, in the order that they were created.
If it fails, the deployment will be rejected. But now, with your build script set up to run your migrations, you will be all set! Next time you deploy, your CI will execute the required migrations for you, and your database will be caught up with the shape that your Payload Config requires.
Running migrations in production
In certain cases, you might want to run migrations at runtime when the server starts. Running them during build time may be impossible due to not having access to your database connection while building or similar reasoning.
If you're using a long-running server or container where your Node server starts up one time and then stays initialized, you might prefer to run migrations on server startup instead of within your CI.
In order to run migrations at runtime, on initialization, you can pass your migrations to your database adapter under the prodMigrations
key as follows:
Passing your migrations as shown above will tell Payload, in production only, to execute any migrations that need to be run prior to completing the initialization of Payload. This is ideal for long-running services where Payload will only be initialized at startup.