Preventing Test Data Leaks with Liquibase Contexts
Learn how to use Liquibase Contexts to separate environment-specific data from core schema changes, preventing test data from leaking into production databases.
03 Jul 2026, 14:45 UTC

The Danger of the All-or-Nothing Changelog
A common friction point in database migrations is the need for environment-specific data. You need test users, mock records, and seed data in development and QA environments to make the application functional. However, if that same seed script runs against your production database, you risk polluting real customer data or creating security vulnerabilities.
The solution is not to maintain separate changelog files for every environment—which leads to configuration drift—but to use Liquibase Contexts. Contexts allow you to tag specific changesets so they only execute when a matching environment flag is passed during deployment.
How Contexts Filter Execution
A context is a label applied to a changeSet. When Liquibase runs the update command, it checks the provided context expression. If a changeset has a context assigned that does not match the current execution flag, Liquibase skips it entirely without marking it as executed in the DATABASECHANGELOG table.
This differs from Labels. While labels are used to group related changes (like "billing-module" or "user-auth") across different files, contexts are designed specifically for environment-based filtering (like "dev", "test", or "prod").
Practical Implementation: Seed Data vs. Schema
In a professional pipeline, your changelog should be a mix of global schema changes (which apply everywhere) and contextual data changes. Below is an example using the XML format.
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/liquibase">
<!-- This runs in ALL environments -->
<changeSet id="1" author="engineer">
<createTable tableName="app_users">
<column name="id" type="int">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="username" type="varchar(50)"/>
</createTable>
</changeSet>
<!-- This runs ONLY in dev or test -->
<changeSet id="2" author="engineer" context="dev, test">
<insert tableName="app_users">
<column name="id" value="101"/>
<column name="username" value="test_user_alpha"/>
</insert>
</changeSet>
<!-- This runs ONLY in production -->
<changeSet id="3" author="engineer" context="prod">
<sql>
CREATE INDEX idx_prod_performance ON app_users(username);
</sql>
</changeSet>
</databaseChangeLog>
Executing the Deployment
Run these commands from your terminal or CI/CD runner with the appropriate database permissions.
For Development:
liquibase update -Dliquibase.contexts=dev
Expected Result: Changeset 1 and 2 are applied. Changeset 3 is ignored.
For Production:
liquibase update -Dliquibase.contexts=prod
Expected Result: Changeset 1 and 3 are applied. Changeset 2 is ignored.
The Risk of Opaque Pipelines
While contexts are powerful, they introduce a layer of "invisible" logic. If a developer forgets to include the -Dliquibase.contexts=prod flag during a production release, Liquibase will only execute changesets that have no context assigned. This can lead to a dangerous scenario where critical production indexes or security patches are silently skipped, leaving the database in an inconsistent state.
To mitigate this, always verify the pending changes before applying them in production using the update-sql command. This outputs the raw SQL that Liquibase intends to run based on the provided contexts, allowing for a final manual audit.
Verification and Rollback
To verify which changesets were applied, query your DATABASECHANGELOG table. Note that the contexts column will show the tags assigned to the changeset, but it does not explicitly record which flag was used to trigger the run.
Rollback Note: If you need to undo a contextual change, you must provide the same context flag during the rollback command to ensure Liquibase targets the correct changesets:
liquibase rollbackCount 1 -Dliquibase.contexts=dev
Limitations
Over-reliance on complex context logic can make the deployment pipeline opaque and difficult to debug. Incorrectly configured contexts may lead to critical schema changes being skipped in production if the context flag is omitted.
Practical Check
Before a production deployment, run liquibase update-sql -Dliquibase.contexts=prod and review the generated SQL file to confirm that seed-data changesets are absent and only intended production changesets appear.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.