Skip to main content

Environment Promotion for Declarative Schema Migrations

Overview

Environment promotion ensures that schema changes are tested and validated in lower environments, such as development and staging, before being applied to production. The Environment Promotion and Compliance guide covers this workflow for versioned migrations. This guide shows how to implement the same workflow for declarative schema migrations, where Atlas plans and applies changes by diffing the desired schema state against the target database.

The promotion model is the same as in the versioned workflow: the desired state is stored in the Atlas Registry and read by every environment, and deployments report the state (migration metadata) of each database back to the registry. Promotion is then enforced by reading the database state of lower environments from the registry:

  1. Push once: As part of continuous delivery, every merge to the main branch pushes the schema to the Atlas Registry with atlas schema push, along with its pre-approved migration plans created with atlas schema plan, creating a new schema version in the registry.
  2. Validate in staging: Staging reads the pushed version from the registry and applies it, with tests and validations running against it.
  3. Promote to production: Production reads the schema version currently deployed to the staging database from the registry using the cloud_databases data source, and applies the exact same version. Pre-execution checks reject any deployment that does not target it.

This guarantees that what reaches production is byte-for-byte the schema state that was validated in staging, satisfying the environment segregation and controlled change management requirements of compliance frameworks like SOC 2 and ISO/IEC 27002. For a detailed discussion of these frameworks, see the compliance context section of the versioned guide.

Implementing Environment Promotion in Atlas

Versioned Workflows

This guide covers environment promotion for the declarative schema migrations workflow. If you manage your schema with versioned migrations, see Environment Promotion and Compliance.

Atlas supports enforcing environment promotion workflows using Data Sources, Pre-Execution Checks, and the Atlas Registry. These capabilities allow teams to define workflows where production deployments depend on the state of lower environments. For example, a production deployment can be configured to only proceed if the deployed schema version matches the one applied in the Staging or Development environment. Similarly, attempts to promote a version that was not deployed and verified in Staging can be automatically blocked.

The guide below demonstrates how to configure an environment promotion workflow using the cloud_databases data source and pre-execution checks.

Setting Up Continuous Delivery

In the declarative workflow, the schema is synced to the Atlas Registry as part of continuous integration: every merge to the main branch pushes the desired schema state with atlas schema push, along with pre-approved migration plans created with atlas schema plan. Each push creates a new schema version, which can be set explicitly with the --version flag. If not set, it defaults to the current timestamp in YYYYMMDDHHMMSS format (UTC) to match the format used for versioned migrations.

Continuous delivery then deploys the pushed version to lower environments, such as Staging, where tests and validations run against it. To set up this pipeline, follow the CI/CD setup guide, or choose your CI platform to get started:

Example: Promotion from Staging to Prod

The following example assumes deployments to the staging database have been reported to the Atlas Registry. Note that this reporting is disabled by default. To enable it, follow the instructions here.

The following configuration defines an environment promotion workflow from staging to production, ensuring production only advances to a version verified in staging. The cloud_databases data source retrieves the schema version currently deployed to the staging database from the Atlas Registry, and sets it as the desired state of production. This ensures that production can only be promoted to the version currently deployed in staging.

Why is this necessary? By default, an unpinned atlas://myapp URL resolves to the latest version in the registry. If a newer version was pushed but not yet validated in staging, this could lead to untested schema changes being applied directly to production.

atlas.hcl
data "cloud_databases" "staging" {
repo = "myapp"
env = "staging"
}

env "production" {
url = getenv("PROD_URL")
schema {
# Promote the schema version from the `staging` environment.
src = "atlas://myapp?version=${data.cloud_databases.staging.targets[0].current_version}"
}
}
How it works
  • The cloud_databases data source retrieves the current schema version of the staging database from the Atlas Registry.
  • The desired state of production is pinned to that version, so the planned migration always targets the version validated in staging.
  • This prevents direct production changes and enforces a progressive deployment workflow.

Example: Pre-Execution Check for Promotion

Another option to ensure controlled promotion is to use a pre-execution check that verifies the target version before applying the planned migration to production. Checks have access to self.planned_migration.target_version, the registry-resolved version of the desired state: the pinned version for atlas://myapp?version= (or ?tag=) URLs, the version that latest resolves to for unpinned URLs, and an empty string for non-registry states. If the planned version differs from the one deployed in staging, the deployment is blocked with an informative error message.

atlas.hcl
data "cloud_databases" "staging" {
repo = "myapp"
env = "staging"
}

locals {
# `try` guards against the case where no staging deployment was reported to the registry yet.
staging_version = try(data.cloud_databases.staging.targets[0].current_version, null)
}

env "production" {
url = getenv("PROD_URL")
schema {
src = "atlas://myapp"
}
check "schema_apply" {
deny "version_mismatch" {
condition = self.planned_migration.target_version != local.staging_version
message = <<-MSG
Production can only be promoted to the version validated in staging.
Current staging version: ${local.staging_version}, planned production version: ${self.planned_migration.target_version}
MSG
}
}
}

Running atlas schema apply --env production will fail if the planned version differs from the one deployed in staging:

Executing pre-execution check (1 check in total):

-- check at atlas.hcl:16 (1 rule):
-> check deny rule "version_mismatch"
Error: "schema apply" was blocked by pre-execution check (deny rule: "version_mismatch" at line: 17):
Production can only be promoted to the version validated in staging.
Current staging version: 20260610120000, planned production version: 20260611080000

-------------------------------------------

https://<your-tenant>.atlasgo.cloud/migrations/304942681163
How it works
  • The pre-execution check retrieves the current schema version of the staging database from the Atlas Registry.
  • After the migration plan is computed, but before any statement is executed, it compares the registry-resolved target version of the desired state with the staging version.
  • If the versions differ, the deployment is blocked with a clear error message. Checks also run on --dry-run, so the gate can be verified without touching the database.

Auditing and Migration History

Atlas automatically maintains a detailed audit trail of all database migrations, ensuring full visibility and accountability across environments. This audit log is essential for meeting compliance requirements in frameworks like SOC 2, ISO/IEC 27002, PCI DSS, and HIPAA. It provides complete traceability, from migration planning, where pre-approved plans are linked to their source pull requests, through review, approval, and execution, so every change can be verified and audited with confidence.

In the declarative workflow, each reported schema apply records the registry version it deployed as the deployment's target version, allowing auditors to verify that production only ever received versions that were previously validated in lower environments.

The Deployment Trace view offers a clear, end-to-end record of how each migration was deployed across environments. You can track when and where each version was applied, which databases were affected, and whether all executions completed successfully. Every action is linked to its source pull request and CI run, allowing teams to easily trace who approved, merged, and deployed every change.

Atlas deployment trace view showing migration audit history

Best Practices

  1. Push once, promote everywhere: Push the schema to the registry exactly once per change (typically on merge to the main branch) and deploy it to staging. Avoid re-pushing per environment, as this creates distinct versions and defeats the promotion model.

  2. Read the promoted version from the registry: Use the cloud_databases data source to pin production's desired state to the version currently deployed in staging, rather than an unpinned atlas://myapp that silently tracks latest.

  3. Gate production with pre-execution checks: Deny deployments whose target_version is empty or differs from the version deployed in staging. Keep the checks even when src is pinned - they protect against future configuration changes.

  4. Pre-plan production changes: Use atlas schema plan in CI to review and approve the migration for the pinned schema transition. Because the desired state is pinned to a version, the pre-approved plan resolves deterministically, ensuring production executes exactly the reviewed SQL.

  5. Report deployments to Atlas Registry: Enable deployment reporting so every apply, including its target version and outcome, is recorded. This is what allows the cloud_databases data source to read the database state of lower environments, and provides audit evidence for compliance frameworks.

Summary

Environment promotion for declarative schema migrations works like the versioned workflow: the desired state is read from the Atlas Registry, and the registry tracks the schema version deployed to each database. A schema version is pushed once, validated in staging, and production reads the staging state from the registry to deploy the exact same version. By combining atlas schema push, the cloud_databases data source, and check "schema_apply" pre-execution checks, teams can:

  • Guarantee that production only receives schema versions validated in lower environments.
  • Block unpinned or mismatched deployments before any SQL is executed.
  • Maintain a complete, auditable history of which version was deployed where.

For the versioned migrations variant of this workflow, see Environment Promotion and Compliance.