Core Commands: update, rollback, status, history, validate, diff
Liquibase has over 50 commands. In practice, you will use fewer than ten of them for 95% of your work. This article covers those ten commands — what each one does, when to reach for it, and what the output tells you. Every example builds on the ecommerce database and users table from Article 4.
The commands are organized by what you’re trying to accomplish: inspect, apply, undo, and verify.
Before Any Command: The Safe Workflow
Every command in this article follows one rule: preview before you apply. Liquibase provides a *SQL variant of every destructive command that prints the SQL it would execute without touching the database. Develop the habit of using them — especially on staging and production.
validate → status → updateSQL → update
For rollbacks:
rollbackSQL --tag=v1.2.0 → rollback --tag=v1.2.0
Group 1: Inspection Commands
These commands read the database and changelog but make no changes.
validate
validate checks that the changelog is syntactically correct and that no deployed changeset has been modified since it was applied.
liquibase validate
Run this first — before status, before update, before anything. It catches:
- YAML/XML syntax errors
- References to non-existent include files
- MD5 checksum mismatches (modified deployed changesets)
- Duplicate changeset IDs
Sample output — clean:
Liquibase command 'validate' was executed successfully.
No validation errors found.
Sample output — checksum mismatch:
ERROR: Validation failed for changeset
db/changelog/migrations/2026/05/001-create-users-table.yaml::001::abhay:
'checkSum' changed:
Was: 9:7d2f14a8b3c...
Is: 9:1a9e83bc21d...
This means changeset 001 was modified after it was applied to this database. Do not edit the changeset to “fix” the hash — that defeats the audit trail. The correct resolution is to revert the edit in the changelog file and make a new changeset for the intended change.
status
status lists all changesets in the changelog that have not yet been applied to the target database.
liquibase status
# More verbose — shows the reason each changeset is pending
liquibase status --verbose
Sample output:
2 changesets have not been applied to lb_user@localhost/ecommerce
db/changelog/migrations/2026/05/002-create-products-table.yaml::001::abhay
db/changelog/migrations/2026/05/002-create-products-table.yaml::002::abhay
Liquibase command 'status' was executed successfully.
When to use it:
- After pulling new code from the team repo — find out what migrations need to run
- Before a deployment — confirm the pending set matches what you expect
- In CI — fail the pipeline early if there are unexpected pending changesets
A result of 0 changesets have not been applied means the database matches the changelog exactly.
history
history shows every changeset that has been applied to the database, drawn from the DATABASECHANGELOG table.
liquibase history
Sample output:
Liquibase History for jdbc:mysql://localhost:3306/ecommerce
+---------------+------------------+---------------------------+----------+--------+
| Deployment ID | Author | Changeset Path | ID | Tag |
+---------------+------------------+---------------------------+----------+--------+
| 7243819023 | abhay | ...001-create-users-... | 001 | |
| 7243819023 | abhay | ...001-create-users-... | 002 | |
| 7243819023 | abhay | ...001-create-users-... | 003 | |
| 7243819045 | abhay | ...001-create-users-... | 004 | v1.0.0 |
+---------------+------------------+---------------------------+----------+--------+
The Deployment ID groups changesets that ran in the same update invocation. If three changesets ran together in one liquibase update call, they share a deployment ID — you can use that ID with rollbackOneUpdate (Pro feature) to undo exactly that deployment.
The Tag column shows any tags applied via liquibase tag. Changesets marked with a tag are rollback targets.
When to use it:
- Audit who applied what and when
- Find the deployment ID for a specific release
- Verify that a rollback actually removed the expected changesets
diff
diff compares two databases and reports schema differences. The most common use is comparing your local development database against staging to find drift.
liquibase diff \
--url=jdbc:mysql://localhost:3306/ecommerce \
--username=lb_user \
--password=lb_pass \
--reference-url=jdbc:mysql://staging-host:3306/ecommerce \
--reference-username=lb_user \
--reference-password=lb_pass
Sample output:
Diff Results:
Reference Database: lb_user @ jdbc:mysql://staging-host:3306/ecommerce
Comparison Database: lb_user @ jdbc:mysql://localhost:3306/ecommerce
Missing Table(s): NONE
Unexpected Table(s): NONE
Changed Table(s):
users
Missing Column(s): phone_number
Missing Index(s): idx_users_phone
This tells you: staging has phone_number column and its index that your local database is missing. Someone applied a migration to staging that isn’t in your local database (or your local hasn’t been updated yet).
diff-changelog goes one step further — it generates a changelog file from the diff output:
liquibase diff-changelog \
--changelog-file=db/changelog/migrations/drift-fix.yaml \
--url=jdbc:mysql://localhost:3306/ecommerce \
--reference-url=jdbc:mysql://staging-host:3306/ecommerce \
--reference-username=lb_user \
--reference-password=lb_pass
The generated file is a starting point, not production-ready — always inspect and clean it up before committing.
Group 2: Apply Commands
These commands change the database.
tag
tag marks the current state of DATABASECHANGELOG with a label. It updates the TAG column on the last row of the table. No schema change is made.
liquibase tag v1.0.0
The single most important habit in Liquibase: run tag immediately before every update on any shared environment (staging, production). This gives you a safe rollback target if the deployment goes wrong.
# Safe deployment sequence
liquibase tag v1.1.0
liquibase updateSQL # Review the SQL
liquibase update # Apply
If update fails partway through or the new release has a bug:
liquibase rollback --tag=v1.1.0 # Return to pre-deployment state
Tags are stored in the database, not in the changelog — so the same tag name can be reused across databases (dev, staging, prod each maintain their own DATABASECHANGELOG).
You can also tag as part of a changeset using tagDatabase:
- changeSet:
id: "010"
author: abhay
comment: Tag release v1.1.0
changes:
- tagDatabase:
tag: v1.1.0
This embeds the tag in the changelog itself, so it is applied atomically with the changesets in that release rather than as a manual pre-deployment step. Both approaches are valid; the in-changelog approach is more reproducible in CI.
update
update applies all pending changesets (those not in DATABASECHANGELOG) in the order they appear in the changelog.
liquibase update
Always run updateSQL first:
liquibase updateSQL
# Review output
liquibase update
Variants:
# Apply only the next N changesets
liquibase updateCount 3
# Apply changesets up to (and including) a tagged changeset
liquibase updateToTag v1.1.0
# Preview SQL for the above variants
liquibase updateCountSQL 3
liquibase updateToTagSQL v1.1.0
What Liquibase does during update:
- Acquires the
DATABASECHANGELOGLOCK(setsLOCKED = 1) - Reads
DATABASECHANGELOGto find which changesets have already run - Applies each pending changeset in changelog order
- Inserts a tracking row into
DATABASECHANGELOGafter each successful changeset - Releases the lock (
LOCKED = 0)
If the process dies mid-run, the lock may remain set. Release it with liquibase releaseLocks — but only after confirming no other Liquibase process is active.
updateTestingRollback
updateTestingRollback applies all pending changesets, immediately rolls them all back, then re-applies them. It is a CI validation tool — it proves that your rollback blocks work before you need them in production.
liquibase updateTestingRollback
Run this in your CI pipeline on every PR that adds new changesets. If rollback fails here (missing rollback block, bad SQL), it fails in a safe environment rather than during an incident.
Group 3: Undo Commands
rollback
rollback reverts all changesets applied after a given tag. It runs the rollback block of each reverted changeset in reverse order.
# Syntax
liquibase rollback --tag=<tag>
# Example: revert everything applied after the v1.0.0 tag
liquibase rollback --tag=v1.0.0
Always preview first:
liquibase rollbackSQL --tag=v1.0.0
# Review the DROP TABLE, DROP INDEX statements
liquibase rollback --tag=v1.0.0
The rollback command requires every reverted changeset to have an explicit rollback block (or be using a change type with automatic rollback support, like createTable → dropTable). If a changeset has no rollback and no automatic rollback, the command fails.
Changesets that cannot be automatically rolled back (e.g., dropTable, sql, data changes) must have an explicit rollback block. If you omitted rollback when writing the changeset, your options are:
- Add the rollback block now and run
clearCheckSums(risky — resets all checksums) - Write a new forward changeset that undoes the effect manually
- Restore from a database backup
Option 3 is why tagging before deployment and having database snapshots are non-negotiable in production.
rollbackCount
rollbackCount reverts the last N changesets, regardless of tags.
# Preview
liquibase rollbackCountSQL 2
# Apply
liquibase rollbackCount 2
When to use it: Useful during development when you want to undo your last few changesets without having set a tag. Not recommended for production — use tag-based rollback there because it is explicit about which state you’re returning to.
rollbackToDate
rollbackToDate reverts all changesets applied after a given date/time.
# Preview
liquibase rollbackToDateSQL "2026-05-03 14:00:00"
# Apply
liquibase rollbackToDate "2026-05-03 14:00:00"
When to use it: When you know the time of a bad deployment but didn’t set a tag. This is the “I forgot to tag” escape hatch. The timestamp is matched against the DATEEXECUTED column in DATABASECHANGELOG.
Date formats accepted: YYYY-MM-DD, YYYY-MM-DD HH:MM:SS, YYYY-MM-DD'T'HH:MM:SS.
Group 4: Utility Commands
changelogSync and changelogSyncSQL
changelogSync marks all changesets in the changelog as executed in DATABASECHANGELOG without actually running them against the database.
liquibase changelogSyncSQL # Preview
liquibase changelogSync # Apply
Primary use case: You are adopting Liquibase on an existing database that was built without it. The schema already matches your changelog, but DATABASECHANGELOG is empty. Running update would try to create tables that already exist and fail. Running changelogSync inserts the tracking rows as if all changesets had run, letting you start tracking new changes from that baseline.
Other use case: A hotfix was applied directly to production (SQL run manually during an incident). You’ve captured the equivalent changeset in the changelog. changelogSync marks it as applied so update skips it.
# Sync only up to a specific tag (for partial sync)
liquibase changelogSyncToTag --tag=baseline-2026-05-01
releaseLocks
releaseLocks sets LOCKED = 0 in DATABASECHANGELOGLOCK. It is used when Liquibase crashed mid-run and left the lock set, blocking all subsequent runs.
liquibase releaseLocks
Important: Before running this, confirm that no Liquibase process is currently running. Releasing the lock while an active process holds it allows two Liquibase instances to modify the schema simultaneously — the exact race condition the lock is designed to prevent.
-- Check in MySQL whether a process is actually holding the lock
SELECT LOCKED, LOCKGRANTED, LOCKEDBY FROM DATABASECHANGELOGLOCK;
If LOCKED = 1 and LOCKGRANTED is more than a few minutes ago with no active Liquibase process, it is safe to release.
clearCheckSums
clearCheckSums deletes all MD5SUM values from DATABASECHANGELOG. On the next update or validate, Liquibase recomputes and stores fresh checksums.
liquibase clearCheckSums
When to use it: A whitespace-only or comment-only change was made to a deployed changeset and triggered a checksum mismatch. If the change is genuinely cosmetic and the SQL produced is identical, clearCheckSums lets you reset without reverting the changelog edit.
When not to use it: If the changeset content was substantively changed (different SQL), clearCheckSums hides the problem instead of fixing it. Create a new changeset.
Command Quick Reference
| Command | Changes DB? | Use When |
|---|---|---|
validate | No | First thing, always |
status | No | Check what’s pending |
history | No | Audit what ran |
diff | No | Find drift between environments |
updateSQL | No | Preview SQL before applying |
tag | No* | Before every deployment |
update | Yes | Apply pending changesets |
updateCount N | Yes | Apply next N changesets only |
updateTestingRollback | Yes (then reverts) | CI validation of rollback blocks |
rollbackSQL | No | Preview rollback SQL |
rollback --tag | Yes | Undo a tagged deployment |
rollbackCount N | Yes | Undo last N changesets |
rollbackToDate | Yes | Undo since a timestamp |
changelogSync | No* | Adopt Liquibase on existing DB |
releaseLocks | No* | Unstick a crashed run |
clearCheckSums | No* | Fix cosmetic checksum mismatch |
*Writes to DATABASECHANGELOG/DATABASECHANGELOGLOCK only — no schema changes.
Putting It All Together: The Daily Workflow
Developer — running migrations locally after a git pull:
liquibase validate # Catch changelog errors
liquibase status # See what's new
liquibase updateSQL # Skim the SQL
liquibase update # Apply
Before deploying to staging or production:
liquibase tag v1.2.0 # Bookmark current state
liquibase updateSQL # Review SQL one more time
liquibase update # Deploy
liquibase status # Confirm 0 pending
After a bad deployment — roll back:
liquibase rollbackSQL --tag=v1.2.0 # Review what will be undone
liquibase rollback --tag=v1.2.0 # Undo
liquibase history # Confirm the reverted changesets are gone
In CI — validate that rollback works before merging:
liquibase validate
liquibase updateTestingRollback
Common Mistakes
Running update without tag on production: Without a tag, your only rollback options are rollbackCount (fragile — you have to know exactly how many changesets ran) or rollbackToDate (requires knowing the exact timestamp). Tag before every deployment. It costs one command and saves hours.
Using clearCheckSums to silence a legitimate mismatch: If a deployed changeset was modified in a way that changes the generated SQL, clearCheckSums tells Liquibase to trust the new version. Now the database and changelog are silently out of sync. Always investigate a checksum mismatch before clearing it.
Running releaseLocks while a process is active: This defeats the concurrency control that DATABASECHANGELOGLOCK provides. Two simultaneous update runs can produce duplicate changesets, corrupted state, or partial migrations. Always check LOCKEDBY and LOCKGRANTED before releasing.
Best Practices
validatefirst, always — it is free and catches problems before they hit the databasetagbefore every non-devupdate— tag-based rollback is the only rollback you want to use under pressure- Use the
SQLpreview variant (updateSQL,rollbackSQL) before applying on any shared environment updateTestingRollbackin CI — proves rollback works before the PR is merged, not during an incidenthistoryafter every deployment — confirm the expected changesets ran and the tag is attached- Never use
clearCheckSumsto hide a real change — it defeats the immutability guarantee that makes Liquibase trustworthy
What You’ve Learned
validatechecks changelog syntax and changeset immutability — run it firststatuslists pending changesets;historylists applied changesetsdifffinds schema drift between two databases without modifying eithertagmarks a rollback point — always run it beforeupdateon shared environmentsupdateapplies pending changesets idempotently;updateTestingRollbackproves rollback worksrollback --tagis the safe, explicit undo mechanism;rollbackCountandrollbackToDateare escape hatcheschangelogSyncbootstraps Liquibase tracking on an existing database without re-applying schemareleaseLocksandclearCheckSumsare last-resort tools — understand why they’re needed before using them
Next: Article 6 — Spring Boot Integration: Zero-Config Setup and Full Properties Reference — how Spring Boot auto-runs Liquibase at startup, spring.liquibase.* properties, disabling Hibernate ddl-auto, and environment-specific configuration.