Automatic YugabyteDB Schema Migrations with Atlas
YugabyteDB is a distributed SQL database with a PostgreSQL-compatible YSQL API.
Atlas supports YugabyteDB through the ysql:// driver, so you can inspect schemas, plan migrations, and manage schema
changes as code.
Enter: Atlas
Atlas helps developers manage their database schema as code. You provide the desired state of the schema, and Atlas plans the migration needed to move the database to that state.
In this guide, we will set up Atlas with YugabyteDB and walk through both declarative and versioned migration workflows.
Prerequisites
- Docker
- Atlas installed on your machine:
- macOS + Linux
- Homebrew
- Docker
- Windows
- CI
- Manual Installation
To download and install the latest release of the Atlas CLI, simply run the following in your terminal:
curl -sSf https://atlasgo.sh | sh
Get the latest release with Homebrew:
brew install ariga/tap/atlas
To pull the Atlas image and run it as a Docker container:
docker pull arigaio/atlas
docker run --rm arigaio/atlas --help
If the container needs access to the host network or a local directory, use the --net=host flag and mount the desired
directory:
docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas migrate apply \
--url "mysql://root:pass@:3306/test"
Download the latest release and move the atlas binary to a file location on your system PATH.
GitHub Actions
Use the setup-atlas action to install Atlas in your GitHub Actions workflow:
- uses: ariga/setup-atlas@v0
with:
cloud-token: ${{ secrets.ATLAS_CLOUD_TOKEN }}
Other CI Platforms
For other CI/CD platforms, use the installation script. See the CI/CD integrations for more details.
- An Atlas Pro account.
YugabyteDB support is available to Pro users. To use this feature, run atlas login.
Logging in to Atlas
To use YugabyteDB with Atlas, you'll need to log in to Atlas. If it's your first time, you will be prompted to create both an account and a workspace (organization):
- Via Web
- Via Token
- Via Environment Variable
atlas login
atlas login --token "ATLAS_TOKEN"
ATLAS_TOKEN="ATLAS_TOKEN" atlas login
Inspecting our Database
Let's start by running YugabyteDB locally:
docker run --rm -d --name atlas-yugabyte-demo \
-p 5433:5433 \
yugabytedb/yugabyte:latest \
bin/yugabyted start --background=false
YugabyteDB may take a few seconds to start. For this example, we will begin with a minimal database that contains a
users table:
CREATE TABLE "users" (
"id" bigint,
"name" varchar NOT NULL,
PRIMARY KEY ("id")
);
Create the table in the local YugabyteDB container:
docker exec atlas-yugabyte-demo bash -lc \
'bin/ysqlsh -h "$(hostname -i)" -U yugabyte -d yugabyte -c "CREATE TABLE \"users\" (\"id\" bigint, \"name\" varchar NOT NULL, PRIMARY KEY (\"id\"));"'
CREATE TABLE
Atlas uses the ysql:// scheme for YugabyteDB YSQL connections. Inspect the database and write the result to
schema.hcl:
atlas schema inspect \
-u "ysql://yugabyte@localhost:5433/yugabyte?search_path=public&sslmode=disable" > schema.hcl
Open schema.hcl to view the Atlas schema that describes the database:
table "users" {
schema = schema.public
column "id" {
null = false
type = bigint
}
column "name" {
null = false
type = character_varying
}
primary_key {
columns = [column.id]
}
}
schema "public" {
comment = "standard public schema"
}
The table block describes the users table. The schema field references the public schema defined below it.
For more details on inspecting databases, see the atlas schema inspect documentation.
Declarative Migrations
The declarative workflow starts with the desired state of the schema. Atlas compares that state to the target database and plans the SQL needed to reconcile the difference.
Let's add a repos table to schema.hcl:
table "users" {
schema = schema.public
column "id" {
null = false
type = bigint
}
column "name" {
null = false
type = character_varying
}
primary_key {
columns = [column.id]
}
}
table "repos" {
schema = schema.public
column "id" {
type = bigint
null = false
}
column "name" {
type = character_varying
null = false
}
column "owner_id" {
type = bigint
null = false
}
primary_key {
columns = [column.id]
}
foreign_key "fk_repo_owner" {
columns = [column.owner_id]
ref_columns = [table.users.column.id]
}
}
schema "public" {
comment = "standard public schema"
}
Now run atlas schema apply to preview the migration. The --dev-url flag points to a
Dev Database that Atlas uses to plan and validate the migration without touching the target:
atlas schema apply \
-u "ysql://yugabyte@localhost:5433/yugabyte?search_path=public&sslmode=disable" \
--to file://schema.hcl \
--dev-url "docker://ysql/latest" \
--dry-run
Atlas prints the plan, runs lint checks, and executes the dry-run:
Planning migration statements (1 in total):
-- create "repos" table:
-> CREATE TABLE "repos" (
"id" bigint NOT NULL,
"name" character varying NOT NULL,
"owner_id" bigint NOT NULL,
PRIMARY KEY ("id"),
CONSTRAINT "fk_repo_owner" FOREIGN KEY ("owner_id") REFERENCES "users" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION
);
-------------------------------------------
Analyzing planned statements (1 in total):
-- no diagnostics found
-------------------------
-- 1 schema change
-------------------------------------------
Running dry-run migration (1 statement in total):
-- create "repos" table
-> CREATE TABLE "repos" (
"id" bigint NOT NULL,
"name" character varying NOT NULL,
"owner_id" bigint NOT NULL,
PRIMARY KEY ("id"),
CONSTRAINT "fk_repo_owner" FOREIGN KEY ("owner_id") REFERENCES "users" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION
);
-- ok
-------------------------
-- 1 migration
-- 1 sql statement
Review the plan. When you are ready to apply it, run the same command without --dry-run.
Versioned Migrations
The versioned workflow stores schema changes as SQL files in a migration directory. The files can be reviewed, committed, and applied later.
To create the first migration from schema.hcl, run the following command with these flags:
--to- The URL of the desired state (yourschema.hclfile).--dev-url- A URL to a Dev Database that Atlas uses to compute the diff.
atlas migrate diff initial \
--to file://schema.hcl \
--dev-url "docker://ysql/latest"
Atlas creates a migrations directory with a migration file and an atlas.sum file:
-- Create "users" table
CREATE TABLE "public"."users" (
"id" bigint NOT NULL,
"name" character varying NOT NULL,
PRIMARY KEY ("id")
);
-- Create "repos" table
CREATE TABLE "public"."repos" (
"id" bigint NOT NULL,
"name" character varying NOT NULL,
"owner_id" bigint NOT NULL,
PRIMARY KEY ("id"),
CONSTRAINT "fk_repo_owner" FOREIGN KEY ("owner_id") REFERENCES "public"."users" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION
);
The migration file represents the current desired state, and atlas.sum protects the integrity of the migration
directory. To learn more, see migration directory integrity.
Next Steps
In this guide, we inspected a YugabyteDB schema, planned a declarative migration, and generated a versioned migration with Atlas. Here are a few good next steps:
GitHub Actions
Set up CI/CD with GitHub Actions
GitLab CI
Set up CI/CD with GitLab CI
Bitbucket Pipelines
Set up CI/CD with Bitbucket Pipelines
Declarative Migrations
Learn about the declarative workflow
Versioned Migrations
Learn about the versioned workflow
Modern Database CI/CD
A complete guide to database CI/CD