How to manage database migrations with Liquibase changelogs
Use Liquibase changelogs to define schema changes declaratively, apply them with update, roll back safely, and scope changes per environment with contexts and labels.
What and why
Liquibase tracks schema changes as discrete changesets in a changelog. Unlike pure SQL tools, it supports declarative formats, automatic rollbacks for many operations, and conditional execution by context. It suits teams that need cross-database portability and safe rollbacks.
Prerequisites
- A database with an account that can alter schema.
- The Liquibase CLI (or the Docker image) plus the JDBC driver for your database.
- A project directory for changelog files.
Steps
1. Install Liquibase
docker run --rm liquibase/liquibase --version
2. Create the master changelog
Create changelog.yaml:
databaseChangeLog:
- include:
file: changes/001-create-users.yaml
Configure connection details in liquibase.properties:
url: jdbc:postgresql://localhost:5432/app
username: app
password: ${DB_PASSWORD}
changeLogFile: changelog.yaml
3. Add a changeset
Create changes/001-create-users.yaml:
databaseChangeLog:
- changeSet:
id: 1
author: vibgrate
changes:
- createTable:
tableName: users
columns:
- column: { name: id, type: BIGINT, autoIncrement: true, constraints: { primaryKey: true } }
- column: { name: email, type: VARCHAR(255), constraints: { nullable: false, unique: true } }
Each changeset is identified by id + author + file path and runs exactly once.
4. Apply with update
liquibase update
Liquibase records applied changesets in DATABASECHANGELOG and locks with DATABASECHANGELOGLOCK to prevent concurrent runs.
5. Roll back a change
Undo the last applied changeset:
liquibase rollbackCount 1
For changes Liquibase cannot auto-reverse (like raw SQL), add a rollback block to the changeset.
6. Use contexts and labels
Tag changesets with context: prod or labels, then run liquibase update --contexts=prod to control what applies where. This keeps test-only seed data out of production.
Verification
Run liquibase status to list unapplied changesets; it should report none after update. Query DATABASECHANGELOG to confirm the expected rows, and inspect the schema for the new objects.
Next Steps
Generate a baseline changelog from an existing schema with generateChangeLog, add liquibase update to your deploy pipeline, and use diff to detect schema drift between environments.