Using Liquibase Contexts to Control Environment‑Specific Schema Changes
Learn how to use Liquibase changelog contexts to apply environment‑specific changesets without duplicating files, with a concrete YAML example and practical verification steps.
16 Sept 2026, 10:37 UTC

Problem: Same changelog, different environments
Many teams keep a single Liquibase changelog that is applied to development, test, and production databases. Yet some changesets—such as loading demo data, creating test‑only indexes, or inserting feature‑flag rows—should only run in certain environments. Duplicating the changelog or maintaining separate files creates drift and makes it easy to miss a change when promoting code.
Thesis: Contexts let you gate changesets while keeping one changelog
Liquibase’s context attribute lets you tag a changeset with one or more labels. At runtime you activate a set of contexts (via environment variable or CLI flag). Only changesets whose contexts match the activated set are executed. This gives you a single source of truth and explicit control over where each changeset runs.
Defining contexts in a changelog
You can add a context element inside any changeset regardless of the changelog format. Below is a YAML example that shows two changesets: one that creates a lookup table (runs everywhere) and one that inserts sample data (runs only when the demo context is active).
# db/changelog.yaml
databaseChangeLog:
- changeSet:
id: 1-create-lookup-table
author: dev-team
changes:
- createTable:
tableName: lookup_status
columns:
- column:
name: id
type: int
autoIncrement: true
constraints:
primaryKey: true
nullable: false
- column:
name: code
type: varchar(10)
- column:
name: description
type: varchar(50)
- changeSet:
id: 2-insert-demo-data
author: dev-team
context: demo # <-- this changeset runs only when 'demo' is activated
changes:
- insert:
tableName: lookup_status
columns:
- column:
name: code
value: "DEMO"
- column:
name: description
value: "Demo record for UI testing"
The same syntax works in XML, JSON, or SQL formatted changelogs. In SQL you must place the context inside a special comment block, e.g., -- liquibase: context demo; forgetting the syntax causes the changeset to run in all environments.
Activating contexts at runtime
Contexts are supplied when you invoke liquibase update. You can set them via the LIQUIBASE_CONTEXTS environment variable or the --contexts command‑line option. Multiple contexts are comma‑separated.
# Run with the demo context active (e.g., in a local dev sandbox)
liquibase --changeLogFile=db/changelog.yaml update --contexts=demo
# Run without any contexts (e.g., in CI for production)
LIQUIBASE_CONTEXTS= liquibase --changeLogFile=db/changelog.yaml update
When the demo context is active, both changesets are applied. When no contexts are supplied, only the first changeset (the lookup table) runs; the demo‑data changeset is skipped because its context does not match the activation set.
Worked example: verifying the behavior
- Create a fresh Liquibase project with the YAML changelog shown above.
- Point Liquibase at an empty test database (e.g., an H2 instance).
- Execute
liquibase update --contexts=demo. Check the output log: you should see two executed changesets, with the second one listingcontext: demoin theDATABASECHANGELOGtable. - Run
liquibase update(no contexts). The log should show only the first changeset applied; the second changeset will be absent from the log and not appear inDATABASECHANGELOG. - Inspect the
DATABASECHANGELOGtable directly: theCONTEXTScolumn for the first row will be empty, while the second row (if present) will containdemo.
These steps confirm that the context mechanism is working as intended without needing to inspect any invented output.
Trade‑off: readability vs. flexibility
Over‑using contexts can make a changelog harder to follow. A file littered with many context tags forces readers to mentally track which sets are active in each environment. Moreover, if a context name is misspelled or the case does not match exactly between the changeset definition and the activation value, Liquibase silently skips the changeset—no error is raised, which can lead to missing schema objects in production.
To mitigate these risks:
- Limit contexts to a small, well‑documented set (e.g.,
demo,test,prod‑only). - Adopt a naming convention (all‑lowercase, no spaces) and enforce it via a linter or CI check.
- Add a brief comment above each context‑gated changeset explaining why it is environment‑specific.
- Include context activation instructions in your team’s contribution guide and in the README of the Liquibase module.
Actionable closing
Start small: pick one changeset that should only run in a non‑production environment (such as a data‑load or a test‑only index) and add a context attribute. Test the activation locally with --contexts=<your‑context> and verify the DATABASECHANGELOG table as described above. Document the chosen context names and the activation pattern in your project’s contributing documentation. This approach gives you a single changelog file while keeping environment‑specific changes explicit and auditable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.