Seed Data as Code: Lookup Tables and Reference Data
A large part of an application's behavior lives in rows your code reads at runtime: country codes,
currencies, status types, plan tiers, feature flags. These rows are part of your application contract,
but they almost always live outside it: bootstrapped by ad-hoc INSERT scripts and drifting between
environments.
Atlas applies the same shift-left workflow to data that it applies to schema. Declare the desired rows
in HCL data blocks or plain SQL INSERT statements alongside the tables that hold them, and Atlas plans
the exact INSERT / UPDATE / DELETE needed to bring any database to that state in the same pull
request as your DDL.
Choose a Sync Mode
Different tables have different lifecycles. A countries table is mostly static. A feature_flags table
is edited often. A plan_tiers table must match the desired state exactly.
For each table, Atlas compares the rows you declared to the rows in the live database (matched by primary
key), and generates the DML needed to bring them in line. The sync mode is a per-environment policy that
controls which kinds of DML Atlas is allowed to emit: just INSERTs for new rows, INSERTs plus UPDATEs
for changed rows, or full INSERT / UPDATE / DELETE reconciliation including removal of rows that are
no longer declared.
INSERT
Add missing rows. Never modify or delete existing data. Use for one-time bootstraps and append-only seed data where production rows must not be touched.
UPSERT
Add missing rows and update existing ones by primary key. Never delete. Use for lookup tables where rows can change (renames, new attributes) but old rows should be preserved.
SYNC
Full reconciliation: insert, update, and delete to match the desired state exactly. Use for tables you fully own as code, where any row not in the schema is wrong.
Get Started by Database
The guides below use PostgreSQL and MySQL as concrete examples, but the data block, sync modes, and env
configuration work across every database Atlas supports, including Spanner, Snowflake, and the rest. Each example walks through configuring atlas.hcl, declaring rows in HCL or SQL alongside the
schema, and applying changes through INSERT, UPSERT, and SYNC modes.
PostgreSQL Seed and Lookup Data
Declare countries, currencies, status types, plan tiers, and feature flag rows alongside the tables that hold them. Atlas computes the INSERT / UPDATE / DELETE statements across all three sync modes.
MySQL Seed and Lookup Data
Manage MySQL reference data as code. Atlas reconciles by primary key, with skip_diff for columns with non-deterministic defaults like CURRENT_TIMESTAMP so dev provisioning doesn't trigger false updates.
Defining Data
Data can be authored as HCL data blocks or plain SQL INSERT statements alongside the tables they populate. The references below cover both syntaxes, the env-level data block that enables synchronization, and the rules for skipping columns from the diff.
HCL Data Syntax
Declare rows in HCL alongside the table they belong to. Covers row syntax, table references, and data requirements (primary key, generated columns, NOT NULL).
SQL Data Syntax
Author seed data as plain SQL INSERT INTO statements, organized in separate files and pulled in with atlas:import.
Env-Level Data Block
Configure data sync in your atlas.hcl: choose a mode, scope tables with include / exclude, and set max_rows for safety.
Skipping Columns from Diff
Ignore columns with non-deterministic defaults like now() or CURRENT_TIMESTAMP, so dev provisioning doesn't trigger false UPDATE statements on every plan.
Dynamic Data Sources
Both HCL and SQL support dynamic data definitions, so your seed data is never limited to static, hand-written rows:
- HCL: define computed resources, inject values through input variables, and read external datasets (CSV, JSON, HTTP responses, or the output of any external program) through data sources.
- SQL: the same data sources are available, with template directories providing the templating layer that turns a compact definition into a full dataset.
Static HCL or SQL
Declare rows inline in your schema files. The simplest, most reviewable form: rows live next to the table they belong to.
Imported Files
Split data into separate SQL files (e.g. countries.sql, currencies.sql) and pull them in with atlas:import. Keep big lookup sets out of the main schema file.
Template Directory
Generate schema files dynamically with Go templates. Loop over a CSV or YAML to emit hundreds of rows from a small definition.
Composite Schema
Compose seed data from multiple sources at plan time: a base SQL file plus an env-specific override plus a templated dataset, all merged into one desired state.
External Program
Run any program that produces SQL or HCL on stdout. Pull rows from a CMS, an admin UI export, an internal API, or anything else that speaks the command line.
HTTP API
Fetch the desired data state directly from an HTTP endpoint. Centralize your reference data behind one service and let every environment pull from it.