Database schema management CLI tool - Infrastructure as Code for database schemas
Strata is a modern database schema management tool that treats your database schema as code. Define your schemas in declarative YAML files, automatically generate migrations, and apply them with confidence across multiple environments.
- 📝 Schema as Code: Define database schemas in declarative YAML files
- 🔄 Automatic Migration Generation: Generate migration files from schema changes
- ✅ Schema Validation: Validate integrity, foreign keys, and naming conventions
- 🔍 Migration Status Tracking: Track applied and pending migrations
- ⬆️ Apply & Rollback: Apply migrations or rollback with confidence
- 📤 Schema Export: Export existing database schemas to code
- 🛡️ Destructive Change Guard: Prevent accidental data loss with explicit confirmation
- 🗄️ Multi-Database Support: PostgreSQL, MySQL, and SQLite
- 📊 Structured Output:
--format jsonfor CI/CD integration - 🔒 SSL/TLS Support: Configurable SSL modes for secure database connections
The easiest way to install Strata. Automatically downloads and installs the latest version.
# Install the latest version
curl -fsSL https://raw.githubusercontent.com/Lazialize/strata/main/install.sh | bash
# Install a specific version
curl -fsSL https://raw.githubusercontent.com/Lazialize/strata/main/install.sh | VERSION=v1.0.0 bash
# Specify custom installation directory
curl -fsSL https://raw.githubusercontent.com/Lazialize/strata/main/install.sh | INSTALL_DIR=/usr/local/bin bash# Clone the repository
git clone https://github.com/Lazialize/strata.git
cd strata
# Build and install
cargo build --release
cargo install --path .For detailed build instructions, cross-compilation, and platform-specific guides, see BUILDING.md.
# Initialize with SQLite
strata init --dialect sqlite
# Or with PostgreSQL
strata init --dialect postgresql
# Or with MySQL
strata init --dialect mysqlThis creates:
.strata.yaml- Configuration file with development, staging, and production environmentsschema/- Directory for schema definitionsmigrations/- Directory for generated migrations
Create a schema file in the schema/ directory (e.g., schema/users.yaml):
version: "1.0"
tables:
users:
columns:
- name: id
type:
kind: INTEGER
nullable: false
auto_increment: true
- name: email
type:
kind: VARCHAR
length: 255
nullable: false
- name: created_at
type:
kind: TIMESTAMP
with_time_zone: true
nullable: false
primary_key:
- id
indexes:
- name: idx_users_email
columns:
- email
unique: true# Generate migration with description
strata generate --description "create users table"
# Or with auto-generated description
strata generateThis creates a migration in migrations/ directory with:
up.sql- SQL to apply the migrationdown.sql- SQL to rollback the migration.meta.yaml- Migration metadata (version, checksum)
# Apply to development environment
strata apply
# Dry run to preview SQL
strata apply --dry-run
# Apply to production
strata apply --env production# Show migration status
strata status
# Check production status
strata status --env productionThese options apply to all commands:
-c, --config <PATH>- Path to configuration file (default:.strata.yaml)-v, --verbose- Enable verbose debug output--no-color- Disable colored output--format <FORMAT>- Output format:text(default) orjson
Initialize a new schema management project.
# Basic initialization
strata init --dialect sqlite
# Force re-initialization
strata init --forceOptions:
-d, --dialect <DIALECT>- Database dialect (postgresql, mysql, sqlite)-f, --force- Force initialization even if config exists
Generate migration files from schema changes.
# With description
strata generate --description "add user email column"
# Auto-generate description
strata generate
# Dry run to preview SQL and destructive changes
strata generate --dry-run
# Allow destructive changes (DROP, RENAME, etc.)
strata generate --allow-destructiveOptions:
-d, --description <DESCRIPTION>- Description for the migration--dry-run- Show SQL without creating files--allow-destructive- Allow destructive changes (DROP TABLE, DROP COLUMN, RENAME, ENUM removal)
Apply pending migrations to the database.
# Apply to development
strata apply
# Dry run (preview only)
strata apply --dry-run
# Apply to production with timeout
strata apply --env production --timeout 30
# Allow destructive changes
strata apply --allow-destructiveOptions:
--dry-run- Show SQL without executing-e, --env <ENV>- Target environment (default: development)--timeout <SECONDS>- Timeout for database operations--allow-destructive- Allow destructive changes (DROP TABLE, DROP COLUMN, RENAME, ENUM removal)
Rollback applied migrations.
# Rollback last migration
strata rollback
# Rollback last 3 migrations
strata rollback --steps 3
# Dry run to preview SQL
strata rollback --dry-run
# Rollback in production
strata rollback --env production --steps 1
# Allow destructive rollback
strata rollback --allow-destructiveOptions:
--steps <N>- Number of migrations to rollback--dry-run- Show SQL without executing--allow-destructive- Allow destructive changes (DROP TABLE, DROP COLUMN, etc.)-e, --env <ENV>- Target environment (default: development)
Run validate followed by generate --dry-run in a single command. If validation fails, generation is skipped.
# Check default schema directory
strata check
# Check specific directory
strata check --schema-dir ./custom-schema
# JSON output for CI/CD
strata check --format jsonOptions:
-s, --schema-dir <DIR>- Path to schema directory
Validate schema definition files.
# Validate default schema directory
strata validate
# Validate specific directory
strata validate --schema-dir ./custom-schemaOptions:
-s, --schema-dir <DIR>- Path to schema directory
Display migration status information.
# Show status for development
strata status
# Show status for production
strata status --env productionOptions:
-e, --env <ENV>- Target environment (default: development)
Export existing database schema to code.
# Export to default schema directory
strata export
# Export to custom directory
strata export --output ./exported-schema
# Export from production
strata export --env production --output ./prod-schema
# Overwrite existing files
strata export --force
# Split into per-table files
strata export --split
# Export specific tables only
strata export --tables users,posts
# Exclude specific tables
strata export --exclude-tables schema_migrationsOptions:
-o, --output <DIR>- Output directory for schema files-e, --env <ENV>- Target environment (default: development)--force- Overwrite existing files without confirmation--split- Output one YAML file per table instead of a single file--tables <TABLES>- Include only specified tables (comma-separated)--exclude-tables <TABLES>- Exclude specified tables (comma-separated)
The .strata.yaml configuration file defines database connections and project settings.
version: "1.0"
dialect: postgresql # postgresql, mysql, or sqlite
schema_dir: schema
migrations_dir: migrations
environments:
development:
host: localhost
port: 5432
database: myapp_dev
user: developer
password: devpass
timeout: 30
staging:
host: staging-db.example.com
port: 5432
database: myapp_staging
user: app_user
timeout: 30
ssl_mode: require
production:
host: db.example.com
port: 5432
database: myapp_prod
user: app_user
timeout: 60
ssl_mode: verify_full
max_connections: 10
min_connections: 2
idle_timeout: 300
options:
application_name: strataversion- Configuration file versiondialect- Database type (postgresql, mysql, sqlite)schema_dir- Directory for schema definition filesmigrations_dir- Directory for migration filesenvironments- Database connection settings per environmenthost- Database host (default:localhost)port- Database port (default: 5432 for PostgreSQL, 3306 for MySQL, none for SQLite)database- Database name (or file path for SQLite)user- Database user (optional for SQLite)password- Database password (optional for SQLite)timeout- Connection timeout in secondsssl_mode- SSL connection mode:disable,prefer,require,verify_ca,verify_fullmax_connections- Maximum connection pool size (default: 5)min_connections- Minimum connection pool sizeidle_timeout- Idle connection timeout in secondsoptions- Additional connection parameters (key-value pairs appended to connection string)
Database connection settings can be overridden with environment variables:
| Variable | Description |
|---|---|
DB_HOST |
Database host |
DB_PORT |
Database port |
DB_DATABASE |
Database name |
DB_USER |
Database user |
DB_PASSWORD |
Database password |
DB_TIMEOUT |
Connection timeout in seconds |
This allows you to keep sensitive credentials out of your configuration file:
DB_PASSWORD=secret strata apply --env productionStrata uses YAML for schema definitions. Each table is defined with its columns, indexes, and constraints.
For better development experience with IDE auto-completion, configure your editor to use the Strata YAML schema:
Install the YAML extension by Red Hat, then add the following to your .vscode/settings.json:
{
"yaml.schemas": {
"./resources/schemas/strata-schema.json": "schema/**/*.yaml"
}
}This enables:
- Auto-completion for column types (common and dialect-specific)
- Validation of schema structure
- Inline documentation for type properties
- Open Settings → Languages & Frameworks → Schemas and DTDs → JSON Schema Mappings
- Add a new mapping:
- Name:
Strata Schema - Schema file:
resources/schemas/strata-schema.json - Schema version:
JSON Schema version 2020-12 - File path pattern:
schema/**/*.yaml
- Name:
In addition to common column types that work across all databases, Strata supports dialect-specific types that leverage database-specific features:
SERIAL- Auto-incrementing integer (equivalent to INTEGER + SEQUENCE)BIGSERIAL- Auto-incrementing big integerSMALLSERIAL- Auto-incrementing small integerINT2- 2-byte integer (-32768 to 32767)INT4- 4-byte integerINT8- 8-byte integerVARBIT- Variable-length bit string (with optionallengthparameter)INET- IPv4 or IPv6 network addressCIDR- IPv4 or IPv6 network specificationARRAY- Array type (withelement_typeparameter)
Example:
- name: id
type:
kind: SERIAL
nullable: false
- name: ip_address
type:
kind: INET
nullable: true
- name: flags
type:
kind: VARBIT
length: 16
nullable: trueTINYINT- Very small integer (-128 to 127, or 0 to 255 if unsigned)MEDIUMINT- Medium-sized integerENUM- String object with a value chosen from a list (requiresvaluesparameter)SET- Set of string values (requiresvaluesparameter)YEAR- Year in 4-digit format
Example:
- name: age
type:
kind: TINYINT
unsigned: true
nullable: false
- name: status
type:
kind: ENUM
values: ["active", "inactive", "pending"]
nullable: false
- name: permissions
type:
kind: SET
values: ["read", "write", "execute"]
nullable: trueNote: Dialect-specific types are validated at database execution time. If you use a type that doesn't exist in your target database, you'll receive a clear error message from the database engine.
Example Schema Files:
- PostgreSQL:
example/postgres_specific_types.yml - MySQL:
example/mysql_specific_types.yml - SQLite:
example/sqlite_specific_types.yml
For more detailed documentation and examples, see example/DIALECT_SPECIFIC_TYPES.md.
Supported column types:
Numeric Types:
INTEGER- Integer numbersprecision: Optional bit size (e.g., 16, 32, 64)
DECIMAL- Fixed-point decimal numbersprecision: Total number of digits (required)scale: Number of decimal places (required)
FLOAT- Single-precision floating-point numbersDOUBLE- Double-precision floating-point numbers
String Types:
VARCHAR- Variable-length stringslength: Maximum length (required)
CHAR- Fixed-length stringslength: Fixed length (required, 1-255)
TEXT- Long text
Date/Time Types:
DATE- Date only (no time)TIME- Time only (no date)with_time_zone: Optional timezone support
TIMESTAMP- Date and timewith_time_zone: Optional timezone support
Other Types:
BOOLEAN- Boolean values (true/false)BLOB- Binary large objects (images, files, etc.)UUID- Universally unique identifierJSON- JSON dataJSONB- Binary JSON (PostgreSQL optimized, falls back to JSON on other databases)
Supported constraints:
PRIMARY_KEY- Primary key constraintcolumns: List of column names
FOREIGN_KEY- Foreign key constraintcolumns: List of column namesreferenced_table: Referenced table namereferenced_columns: Referenced column nameson_delete: Referential action on delete (optional):NO_ACTION,CASCADE,SET_NULL,SET_DEFAULT,RESTRICTon_update: Referential action on update (optional): same values ason_delete
UNIQUE- Unique constraintcolumns: List of column names
CHECK- Check constraintcolumns: List of columns involvedcheck_expression: SQL check expression (e.g.,"price > 0")
To rename a table or column, use the renamed_from field. Strata will generate ALTER TABLE RENAME or ALTER TABLE RENAME COLUMN instead of a destructive drop-and-create:
tables:
# Rename table: accounts -> users
users:
renamed_from: accounts
columns:
- name: full_name
renamed_from: name # Rename column: name -> full_name
type:
kind: VARCHAR
length: 255
nullable: falseNote: Remove
renamed_fromafter the migration has been applied. It is only used during migration generation.
Strata automatically maps column types to the appropriate native type for each database:
| Strata Type | PostgreSQL | MySQL | SQLite |
|---|---|---|---|
| INTEGER | INTEGER/SERIAL | INT | INTEGER |
| DECIMAL | NUMERIC(p,s) | DECIMAL(p,s) | TEXT |
| FLOAT | REAL | FLOAT | REAL |
| DOUBLE | DOUBLE PRECISION | DOUBLE | REAL |
| VARCHAR | VARCHAR(n) | VARCHAR(n) | TEXT |
| CHAR | CHAR(n) | CHAR(n) | TEXT |
| TEXT | TEXT | TEXT | TEXT |
| DATE | DATE | DATE | TEXT |
| TIME | TIME [WITH TZ] | TIME | TEXT |
| TIMESTAMP | TIMESTAMP [WITH TZ] | TIMESTAMP | TEXT |
| BOOLEAN | BOOLEAN | BOOLEAN | INTEGER |
| BLOB | BYTEA | BLOB | BLOB |
| UUID | UUID | CHAR(36) | TEXT |
| JSON | JSON | JSON | TEXT |
| JSONB | JSONB | JSON | TEXT |
Note: SQLite has limited native type support. Strata stores some types as TEXT to preserve precision (e.g., DECIMAL, DATE).
Here are examples of how to use each column type in your schema:
version: "1.0"
tables:
products:
columns:
# Numeric types
- name: id
type:
kind: INTEGER
nullable: false
auto_increment: true
- name: price
type:
kind: DECIMAL
precision: 10
scale: 2
nullable: false
- name: weight
type:
kind: FLOAT
nullable: true
- name: latitude
type:
kind: DOUBLE
nullable: true
# String types
- name: name
type:
kind: VARCHAR
length: 255
nullable: false
- name: country_code
type:
kind: CHAR
length: 2
nullable: false
- name: description
type:
kind: TEXT
nullable: true
# Date/Time types
- name: manufacturing_date
type:
kind: DATE
nullable: true
- name: opening_time
type:
kind: TIME
with_time_zone: false
nullable: true
- name: created_at
type:
kind: TIMESTAMP
with_time_zone: true
nullable: false
# Other types
- name: is_active
type:
kind: BOOLEAN
nullable: false
default_value: "true"
- name: thumbnail
type:
kind: BLOB
nullable: true
- name: external_id
type:
kind: UUID
nullable: false
- name: attributes
type:
kind: JSON
nullable: true
- name: metadata
type:
kind: JSONB
nullable: true
primary_key:
- idversion: "1.0"
tables:
posts:
columns:
- name: id
type:
kind: INTEGER
nullable: false
auto_increment: true
- name: title
type:
kind: VARCHAR
length: 200
nullable: false
- name: content
type:
kind: TEXT
nullable: true
- name: user_id
type:
kind: INTEGER
nullable: false
- name: published
type:
kind: BOOLEAN
nullable: false
default_value: "false"
- name: published_at
type:
kind: TIMESTAMP
with_time_zone: true
nullable: true
primary_key:
- id
indexes:
- name: idx_posts_user_id
columns:
- user_id
unique: false
- name: idx_posts_published_at
columns:
- published_at
unique: false
constraints:
- type: FOREIGN_KEY
columns:
- user_id
referenced_table: users
referenced_columns:
- id
on_delete: CASCADEMigration files are generated in the migrations/ directory with the following structure:
migrations/
20260122120000_create_users/
up.sql # SQL to apply the migration
down.sql # SQL to rollback the migration
.meta.yaml # Migration metadata
The .meta.yaml file contains:
version: "20260122120000"
description: create_users
dialect: postgresql
checksum: "abc123def456..." # SHA-256 hash of up.sqlMigrations with destructive changes include additional metadata:
version: "20260125120000"
description: drop_legacy_tables
dialect: postgresql
checksum: "def789..."
destructive_changes:
tables_dropped:
- legacy_users
columns_dropped:
- table: products
columns:
- old_fieldThe checksum ensures migration integrity - any modification to the migration after it's been applied will be detected.
- One table per file: Create separate YAML files for each table
- Meaningful names: Use descriptive names for tables, columns, and constraints
- Consistent naming: Follow naming conventions (e.g., snake_case)
- Always check before generating migrations:
strata check(validates and previews in one step) - Review generated SQL before applying:
strata apply --dry-run - Test in development before production
- Never modify applied migrations - create new ones instead
- Keep migrations small - one logical change per migration
- Use environment-specific configurations for different databases
- Store sensitive credentials in environment variables
- Test migrations in staging before production
- Maintain separate databases for each environment
- Commit schema files to version control
- Commit migration files to version control
- Don't commit
.strata.yamlif it contains sensitive data - Use
.gitignorefor database files and credentials
Strata includes a safety mechanism to prevent accidental data loss from destructive schema changes.
Destructive changes are schema modifications that can cause data loss or application compatibility issues:
| Change Type | Example | Risk |
|---|---|---|
| Table Drop | DROP TABLE users |
All data in the table is lost |
| Column Drop | ALTER TABLE users DROP COLUMN email |
Column data is lost |
| Column Rename | ALTER TABLE users RENAME COLUMN name TO full_name |
Application code may break |
| ENUM Drop | DROP TYPE status_enum |
References become invalid |
| ENUM Recreate | Removing values from an ENUM | Existing data may become invalid |
By default, Strata refuses to generate or apply migrations containing destructive changes:
$ strata generate
Error: Destructive changes detected
Tables to be dropped: users, posts
Columns to be dropped:
- products: legacy_field, unused_column
To proceed, choose one of the following:
1. Review changes: strata generate --dry-run
2. Allow destructive changes: strata generate --allow-destructive
3. Reconsider your schema changesTo proceed with destructive changes, use the --allow-destructive flag:
# Generate migration with destructive changes
strata generate --allow-destructive
# Apply migration with destructive changes
strata apply --allow-destructive-
Preview first: Use
--dry-runto see what will happenstrata generate --dry-run
-
Review the changes: Ensure you understand the impact
-
Generate with explicit permission:
strata generate --allow-destructive --description "drop legacy tables" -
Apply with explicit permission:
strata apply --allow-destructive
When you generate a migration with destructive changes, the metadata is recorded in .meta.yaml:
version: "20260125120000"
description: "drop legacy tables"
dialect: postgresql
checksum: "abc123..."
destructive_changes:
tables_dropped:
- legacy_users
columns_dropped:
- table: products
columns:
- old_fieldThis metadata is used by strata apply to enforce the safety guard, even if the migration was generated on another machine.
Migrations created before the destructive change guard feature (without destructive_changes field) are treated as potentially destructive and require --allow-destructive to apply:
$ strata apply
Error: Destructive migration detected
Migration: 20250101120000
⚠ Legacy migration format detected
To proceed, choose one of the following:
1. Review SQL: strata apply --dry-run
2. Allow destructive changes: strata apply --allow-destructive
3. Regenerate migration with current version of strataIf you see a checksum mismatch warning:
- Don't modify applied migrations - create a new migration instead
- Check if someone modified the migration file after it was applied
- If intentional, you may need to manually update the database migration history
If database connections timeout:
- Increase timeout in
.strata.yaml - Check network connectivity
- Verify database credentials
- Ensure database is running
If schema validation fails:
- Check error message for specific issues
- Verify all referenced tables exist
- Ensure foreign key columns match referenced columns
- Check naming conventions
# Clone repository
git clone https://github.com/Lazialize/strata.git
cd strata
# Build
cargo build
# Run tests
cargo test
# Build release version
cargo build --release# Run all tests
cargo test
# Run specific test
cargo test test_name
# Run with output
cargo test -- --nocaptureContributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under either of:
- MIT license (LICENSE or http://opensource.org/licenses/MIT)
at your option.
- Built with Rust
- CLI powered by clap
- Database access via SQLx
- YAML parsing with serde-saphyr
🤖 This project was created with the assistance of AI (Claude, Anthropic). The code, documentation, and architecture were developed through human-AI collaboration.
Made with ❤️ by the Strata Contributors