Part 5 of 18

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:

  1. Acquires the DATABASECHANGELOGLOCK (sets LOCKED = 1)
  2. Reads DATABASECHANGELOG to find which changesets have already run
  3. Applies each pending changeset in changelog order
  4. Inserts a tracking row into DATABASECHANGELOG after each successful changeset
  5. 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 createTabledropTable). 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:

  1. Add the rollback block now and run clearCheckSums (risky — resets all checksums)
  2. Write a new forward changeset that undoes the effect manually
  3. 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

CommandChanges DB?Use When
validateNoFirst thing, always
statusNoCheck what’s pending
historyNoAudit what ran
diffNoFind drift between environments
updateSQLNoPreview SQL before applying
tagNo*Before every deployment
updateYesApply pending changesets
updateCount NYesApply next N changesets only
updateTestingRollbackYes (then reverts)CI validation of rollback blocks
rollbackSQLNoPreview rollback SQL
rollback --tagYesUndo a tagged deployment
rollbackCount NYesUndo last N changesets
rollbackToDateYesUndo since a timestamp
changelogSyncNo*Adopt Liquibase on existing DB
releaseLocksNo*Unstick a crashed run
clearCheckSumsNo*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

  • validate first, always — it is free and catches problems before they hit the database
  • tag before every non-dev update — tag-based rollback is the only rollback you want to use under pressure
  • Use the SQL preview variant (updateSQL, rollbackSQL) before applying on any shared environment
  • updateTestingRollback in CI — proves rollback works before the PR is merged, not during an incident
  • history after every deployment — confirm the expected changesets ran and the tag is attached
  • Never use clearCheckSums to hide a real change — it defeats the immutability guarantee that makes Liquibase trustworthy

What You’ve Learned

  • validate checks changelog syntax and changeset immutability — run it first
  • status lists pending changesets; history lists applied changesets
  • diff finds schema drift between two databases without modifying either
  • tag marks a rollback point — always run it before update on shared environments
  • update applies pending changesets idempotently; updateTestingRollback proves rollback works
  • rollback --tag is the safe, explicit undo mechanism; rollbackCount and rollbackToDate are escape hatches
  • changelogSync bootstraps Liquibase tracking on an existing database without re-applying schema
  • releaseLocks and clearCheckSums are 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.