Managing YugabyteDB Distributed Schemas (Versioned)
With YugabyteDB's distributed schema model, every table is split into tablets that are spread across the nodes of the cluster. The schema decides how that split happens out of three options:
| Decision | Attribute | What it controls |
|---|---|---|
| Colocation | colocation | Whether a table shares one tablet with its neighbours, or gets its own |
| Pre-splitting | split_into, split_at_values | How many tablets a table starts with, and where the boundaries are |
| Sharding | sharding = HASH | RANGE | Whether a key or index column is hash-distributed or kept in sorted order |
The decision is fixed when the object is created. YugabyteDB has no ALTER TABLE ... SET COLOCATION
and no way to re-declare split points in place, so getting these wrong means recreating the table later.
By managing your YugabyteDB schema as code, you review the distribution layout in a pull request
instead of discovering the decision in production.
This guide covers the versioned workflow. For the declarative approach, see the declarative distribution guide.
YugabyteDB support is available only to Atlas Pro users. To use this feature, run:
atlas login
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.
YugabyteDB support is only available on v1.3.1 or later. Check your version by running atlas version.
Update to the latest version by reinstalling.
- An Atlas Pro account (run
atlas loginto authenticate)
Start a Colocated Database
Begin by running a local single-node cluster:
docker run --rm -d --name atlas-yb \
-p 5433:5433 \
yugabytedb/yugabyte:latest \
bin/yugabyted start --background=false
YugabyteDB takes a few seconds to begin running. Wait until you see that it is ready:
docker exec atlas-yb bin/yugabyted status | grep 'YSQL Status'
| YSQL Status: Ready |
Now create the target database with colocation enabled:
docker exec atlas-yb bash -lc \
'bin/ysqlsh -h "$(hostname -i)" -U yugabyte -d yugabyte -c "CREATE DATABASE shop WITH COLOCATION = true"'
Confirm the flag took effect:
docker exec atlas-yb bash -lc \
'bin/ysqlsh -h "$(hostname -i)" -U yugabyte -d shop -tAc "select current_database(), yb_is_database_colocated()"'
shop|t
CREATE DATABASEYugabyteDB propagates catalog changes asynchronously across nodes, so a brand-new database can be briefly invisible to a new connection. Retry the command once.
Configuring Atlas
Set up your Atlas configuration file with an environment that points to your database URL
(url), desired schema file (schema.src), migrations directory (migration.dir), and
dev database (dev).
The dev database is an ephemeral database where Atlas normalizes your desired state before diffing it against
your database when planning a migration. If colocation is used in your YugabyteDB schema, then the dev
database must be colocated, as well. A dedicated docker "ysql" block creates one:
docker "ysql" "dev" {
image = "yugabytedb/yugabyte:latest"
database = "dev"
colocation = true
}
env "local" {
url = getenv("DATABASE_URL")
dev = docker.ysql.dev.url
schema {
src = "file://schema.hcl"
}
migration {
dir = "file://migrations"
}
}
If your schema uses only pre-splitting and index sharding, the plain shorthand works and no docker block
is needed:
env "local" {
url = getenv("DATABASE_URL")
dev = "docker://ysql/latest"
schema {
src = "file://schema.hcl"
}
migration {
dir = "file://migrations"
}
}
If the dev database is not colocated, colocation = false is already its default, so Atlas normalizes
the attribute away and loses your opt-out. The result is a spurious DROP TABLE / CREATE TABLE on every
apply. Always pair a colocated target with a colocated dev database.
Point the target at the shop database you just created:
export DATABASE_URL="ysql://yugabyte@localhost:5433/shop?search_path=public&sslmode=disable"
Define Desired Schema
The schema file is the target that Atlas diffs against. Let's model a small shop that uses all three distribution levers: colocation, pre-splitting, and index sharding.
Colocated tables
In a colocated database every table is colocated by default, sharing a single tablet with its neighbors. That is the right choice for small, low-throughput tables, because a join between two colocated tables is served from one node with no network hop.
Colocation may not be the right choice for two kinds of tables:
- Tables with real write throughput. A single tablet is a single Raft leader, so it is a bottleneck that cannot be scaled out.
- Lookup tables used in many joins. These are read from every node. Sharding them by primary key lets each node read the range it needs, instead of funnelling every lookup through the one node that owns the colocation tablet.
Opt a table out with colocation = false:
schema "public" {
}
// Small, low-throughput lookup table: colocated (the database default).
table "currencies" {
schema = schema.public
column "code" {
null = false
type = character_varying(3)
}
column "name" {
null = false
type = character_varying
}
primary_key {
columns = [column.code]
}
}
// Lookup table used in many joins: opt out and shard by primary key.
table "products" {
schema = schema.public
colocation = false
column "id" {
null = false
type = bigint
}
column "sku" {
null = false
type = character_varying
}
column "price" {
null = false
type = numeric(10,2)
}
primary_key {
columns = [column.id]
}
}
Note what is not written: currencies says nothing about colocation. Atlas treats an undeclared colocation
as unspecified, so the table simply follows the database default. Only the opt-out is explicit.
colocation = false needs a colocated databaseThe attribute describes an opt-out from a database-level default. Against a non-colocated database there is no default to opt out of, making the attribute meaningless. Atlas does not read it back in this case, so the diff never converges. Use it only with a colocated target.