The new atlas config validate command checks your atlas.hcl for structural errors, such as unknown or mis-nested blocks, missing or unknown attributes, mutually-exclusive options, and invalid enums, and reports them as file:line:column diagnostics. It works statically, at a parsing and semantic-checking level only, with no database and no runtime evaluation.
Starting with v1.2.3, you can check your atlas.hcl is structurally valid without evaluating it. Atlas already validates the config up front, before any command does real work, but it gets there by fully loading the file, which means you supply every input variable and Atlas evaluates data sources that a structural check never needs.
atlas config validate runs that same check on its own, with no variables to pass and no data sources to evaluate. That makes it a natural CI gate and a fast feedback loop for AI agents authoring config: an agent editing atlas.hcl can run it, read the bare file:line:column diagnostics, and correct the file in place.
Valid configs are silent
Point it at your config; it defaults to file://atlas.hcl. When everything checks out, the command prints nothing and exits 0:
env "local" {url = "mysql://root:pass@localhost:3306/example"dev = "docker://mysql/8/dev"migration {dir = "file://migrations"}}
$ atlas config validate$ echo $?0
Mistakes become diagnostics
Atlas reports any problem it finds compiler-style, with the exact location: an unknown or mis-nested block, a missing or unknown attribute, mutually-exclusive options, or an invalid enum value. Here, revision is not a valid attribute of the migration block:
env "local" {url = "mysql://root:pass@localhost:3306/example"dev = "docker://mysql/8/dev"migration {dir = "file://migrations"revision = "atlas_schema_revisions"}}
$ atlas config validateatlas.hcl:7,5-40: unknown attribute revision in block of type migration
Validate multiple files
Pass -f/--file more than once to validate every file that makes up an environment, for example a base config plus an override:
$ atlas config validate -f file://atlas.hcl -f file://atlas.dev.hcl
Static parsing and semantic checks
Validation runs entirely statically: it parses your config and checks its semantics, but it never opens a database, starts a dev-database, or evaluates runtime references such as getenv, runtimevar, or atlas:// URLs. The config below validates cleanly even when DATABASE_URL is unset and atlas://app is never fetched, so runtime inputs can never cause a false error:
env "prod" {src = "atlas://app"url = getenv("DATABASE_URL")dev = "docker://postgres/16/dev"}
What static checking won't catch
Because expressions are never evaluated, atlas config validate checks your config as written, not as it resolves at runtime. Conditions, variable substitutions, and data sources are parsed and type-checked, but they are not run, so a config whose shape depends on those values can pass validation and still behave differently once they are evaluated. In the example below, the ternary that picks a url and the data "external_schema" source that supplies src are both resolved only when you run a real command:
data "external_schema" "app" {program = ["./load-schema.sh"]}env "default" {# Parsed and type-checked, but not evaluated: the ternary and the# data source are resolved only when you run a real command.url = atlas.env == "prod" ? getenv("PROD_URL") : "docker://postgres/16/dev"src = data.external_schema.app.url}
That is the intended trade-off: validation stays fast, dependency-free, and safe to run anywhere, while the fully resolved picture, with variables substituted, conditions evaluated, and data sources executed, comes from the command you are actually configuring (schema apply, migrate diff, and friends). Use atlas config validate as the first gate, not the last.