Bolt is a command-line tool designed to simplify and streamline your database migration process.
It is distributed as a standalone binary, making it easy to run anywhere without having to setup any tooling, and it is language indepenent. It doesn't matter what programming language you're using, you can use Bolt for any project you have.
- Features
- Installation
- Tutorial
- How-to
- Reference
- Explanation
- How Are Migration Scripts Executed?
- How Does Bolt Know What Migrations Have Been Applied?
- How Are Migrations Applied?
- How Are Migrations Reverted?
- What are Migration Version Styles?
- Why can't I change between version styles?
- How is the migration message used?
- What restrictions are there on the custom migration table?
- Schema migrations are written in plain SQL.
- Migrations can be versioned sequentially or by creation timestamp.
- All migrations run in transactions by default.
- Supports up and down migrations and jumping to any particular schema version.
- Supports PostgreSQL, MySQL, SQL Server, and SQLite3.
$ go install github.com/eugenetriguba/bolt/cmd/bolt@latest
This will install Bolt to your $GOBIN
directory. Make sure that $GOBIN
is in your $PATH
.
$ brew tap eugenetriguba/homebrew-bolt
$ brew install bolt
If you visit the Releases page, pre-compiled binaries are created with every release for the following operating systems and architectures:
- linux/386
- linux/amd64
- linux/arm64
- windows/386
- windows/amd64
- darwin/amd64
- darwin/arm64
In this tutorial, we'll guide you through the basics of using Bolt by running through how you can create a migration, apply it, revert it, and see the status of your migrations.
Run the following command to create your first migration:
$ bolt new -m "my first migration"
Created migration 20240316145038 - my first migration.
Upon completion, a migrations directory will be created with the following structure:
migrations
└── 20240316145038_my_first_migration.sql
1 directory, 1 file
Bolt automatically creates the migrations directory and your migration scripts inside it.
Bolt utilizes plain SQL for migration scripts. Each migration directory contains a comment for the upgrade script portion with -- migrate:up
and a comment for the downgrade script with -- migrate:down
.
- Open up
20240316145038_my_first_migration.sql
You'll see the following template in the file for your migration:
-- migrate:up
-- migrate:down
- Write your migration script
-- migrate:up
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- migrate:down
DROP TABLE users;
We'll use Docker to run a PostgreSQL database container for this tutorial:
- Install Docker
If you don't have docker installed, view the installation instructions here.
- Startup the PostgreSQL container
$ docker run --name bolt-postgres -e POSTGRES_USER=bolt_user -e POSTGRES_PASSWORD=bolt_password -e POSTGRES_DB=bolt_tutorial_db -p 5432:5432 --detach postgres:16.1-alpine3.19
Bolt can be configured with a bolt.toml
file or environment variables. For simplicity, we'll use a bolt.toml
file in this tutorial.
- Create a
bolt.toml
file:
$ touch bolt.toml
- Add the following contents to the file:
[database]
host = "localhost"
port = "5432"
user = "bolt_user"
password = "bolt_password"
dbname = "bolt_tutorial_db"
driver = "postgresql"
Apply your migration, which will execute the upgrade.sql
script:
$ bolt up
Applying migration 20240316145038_my_first_migration..
Successfully applied migration 20240316145038_my_first_migration in 3.217875ms!
To view the status of your migration:
$ bolt status
Version Message Applied
20240316145038 my_first_migration X
This command displays the migration's version, name, and whether the migration was applied or not.
Connect to the database container to confirm the users table was created:
$ docker exec -it bolt-postgres psql -U bolt_user -d bolt_tutorial_db
bolt_tutorial_db=# \dt
List of relations
Schema | Name | Type | Owner
--------+-----------------+-------+-----------
public | bolt_migrations | table | bolt_user
public | users | table | bolt_user
(2 rows)
We can see that our users
table was created successfully. We also see another table that we didn't explicitly create called bolt_migrations
. This table is used by Bolt to keep track of which migrations have been applied to your database. You should not modify this table manually.
To revert your migration:
$ bolt down
Reverting migration 20240316145038_my_first_migration..
Successfully reverted migration 20240316145038_my_first_migration in 6.283667ms!
Reconnect to the database container and check that the users table has been removed:
$ docker exec -it bolt-postgres psql -U bolt_user -d bolt_tutorial_db
bolt_tutorial_db=# \dt
List of relations
Schema | Name | Type | Owner
--------+-----------------+-------+-----------
public | bolt_migrations | table | bolt_user
(1 row)
Congrats! You've learned the core features of Bolt. If you want learn more, see the How-to, Reference, or Explanation section.
In your migration script, add a transaction:false
option:
-- migrate:up transaction:false
-- migrate:down transaction:false
- PostgreSQL
- MySQL
- Microsoft SQL Server
- SQLite3
There are two ways to configure Bolt: via a bolt.toml
file or via environment variables. If you use both methods, the environment variables will always take precedence.
Bolt attempts to find a bolt.toml
file in your current working directory
or in any parent directory.
[migrations]
# The directory where your migration scripts are located.
# Defaults to "migrations". This directory is relative to
# the current working directory. You may also use an absolute
# path.
directory_path = "migrations"
# The migration versioning style you prefer. Supported options
# are "timestamp" and "sequential". Defaults to "timestamp".
#
# Note: It is not supported to change migration version styles
# i.e. you can't have a mix of sequential and timestamp migrations.
version_style = "timestamp"
# Connection parameters for the database Bolt will be
# applying migrations to. All connection parameters are
# required.
[database]
# The host to use to connect to your database.
host =
# The port to use to connect to your database.
port =
# The user to use to connect to your database.
user =
# The password to use to connect to your database.
password =
# The name of the database within your DBMS. If you're
# using sqlite3, this is the filesystem path to the db.
dbname =
# The name of the database driver to use to connect to
# the database. Either "postgresql", "mysql", "mssql", or "sqlite3".
driver =
# The name of the database table to create for managing
# the applied migration versions. Defaults to "bolt_migrations".
migrations_table = "bolt_migrations"
All configuration file settings have corresponding environment variables.
BOLT_MIGRATIONS_DIR_PATH
BOLT_MIGRATIONS_VERSION_STYLE
BOLT_DB_HOST
BOLT_DB_PORT
BOLT_DB_USER
BOLT_DB_PASSWORD
BOLT_DB_NAME
BOLT_DB_DRIVER
BOLT_DB_MIGRATIONS_TABLE
$ bolt help new
new [-message|-m]:
Create a new database migration
-m string
alias for -message (default "autogenerated")
-message string
Message to use for the migration (default "autogenerated")
$ bolt help up
up [-version|-v]:
Apply migrations against the database
-v string
alias for -version
-version string
The version to upgrade up and including to.
$ bolt help down
down [-version|-v]:
Downgrade migrations against the database
-v string
alias for -version
-version string
The version to downgrade down and including to.
$ bolt help status
status:
List the database migrations and their statuses
$ bolt help version
version:
Show the current version of Bolt
Bolt provides a way for you to customize how your migration scripts are executed if you need something different than the default behavior.
You can do this by adding onto the -- migrate:up
or -- migrate:down
comments with your own execution option. The options must be in the following format: -- migrate:up <option1> <option2> <...>
or -- migrate:down <option1> <option2> <...>
. The following options are available:
transaction:false
: Execute the migration script without a transaction. By default, every migration script will be attempted to be executed within a transaction, however, some SQL commands cannot be executed within a transaction so you'll need to opt out of that behavior in those cases.
The following version styles are supported:
- Timestamp
- Sequential
Bolt executes upgrade and downgrade migration scripts in a transaction. This ensures that if any errors occur during the execution of any migration script, the transaction will be rolled back and the migration will be marked as failed. Bolt will then exit with an error code and output what error has occurred to standard error. However, do note that some databases, like MySQL, commit certain DDL statements immediately even if you're in a transaction.
Bolt keeps track of which migrations have been applied to your database by creating a table called bolt_migrations
. This table contains a single column called version
which is the version of the migration that was applied. That version is compared to the versions you have locally.
When you run bolt up
, Bolt will look at your local migration scripts and compares the version part of the migration directory names to the versions that have been inserted into the bolt_migrations
table. Any versions that aren't in the table will be applied in order, starting with the oldest migration. Applying a migration entails executing the -- migrate:up
portion of the script in a transaction and inserting in the migration's version into the bolt_migrations
table.
When you run bolt down
, Bolt will look at the bolt_migrations
table and compare the versions to the versions of your local migration scripts. Any versions that are in the table but not in your local migration scripts will be reverted in order, starting with the newest migration. Reverting a migration entails executing the -- migrate:down
portion of the script in a transaction and removing the migration's version from the bolt_migrations
table.
Whenever you create a migration, it'll be prefixed with a "version". This is what is used by Bolt to keep track of what order to apply or revert migrations. Version styles are different supported options for what this prefix will be.
Bolt supports two different options for this:
- Sequential versions
- Timestamp versions
Sequential versions are incrementing integers. When you create a migration, it'll be prefixed with a number like "001", "002", and so on. Timestamp versions use the current time on migration creation as the prefix.
The main reason that switching back and forth between "sequential" and "timestamp" versions is not supported is because Bolt will no longer know how to properly sort your migrations in the right order (and therefore, apply or revert them in the right order). If you've been using sequential migrations, such as "001" and "002", and then move over to timestamp migrations, such as "20200101000000", Bolt won't know how to order these correctly when both exist. Furthermore, because the "version" is parsed and sorted according to the version style that is configured, the parsing will fail if a mix of version styles are used.
This means that if you wanted to switch version styles, you could do it manually by updating your local migrations to use the new version style and update the entries in bolt_migrations
to match, but there is no tooling support for doing this change.
All migrations are created on the local filesystem in the format <version>_<message>
. Bolt uses the <version>
part of the name for keeping track of the migrations and how they should be applied or reverted. The <message>
part is purely informational so you know what that migration is for.
While you can configure the database table that bolt uses for keeping track of applied migrations, there are restrictions on the name that can be used. It must only contain alphanumeric or underscore characters (e.g. my_custom_table
). Furthermore, it may contain one dot (.
) if you would like to specify a different schema (e.g. different_schema.my_custom_table
). However, do note that using a custom schema is only support with MSSQL and PostgreSQL since MySQL and SQlite3 both don't support schemas.