콘텐츠로 이동

Liquibase Cheat Sheet

Overview

Liquibase is an open-source database schema change management tool. It tracks database changes using changelog files that describe changes (changesets) in XML, YAML, JSON, or raw SQL. Each changeset has a unique identifier (author + id) and is tracked in a DATABASECHANGELOG table, ensuring changes are applied exactly once and in order.

Liquibase supports over 50 database platforms including PostgreSQL, MySQL, Oracle, SQL Server, MariaDB, SQLite, H2, and many more. It provides rollback support, diff capabilities, pre-conditions for conditional execution, contexts and labels for environment-specific changes, and integration with CI/CD pipelines. Liquibase can generate changelogs from existing databases and compare schemas across environments.

Installation

# macOS via Homebrew
brew install liquibase

# Linux (Debian/Ubuntu)
curl -L https://github.com/liquibase/liquibase/releases/download/v4.30.0/liquibase-4.30.0.tar.gz | tar xz
sudo mv liquibase /opt/liquibase
sudo ln -s /opt/liquibase/liquibase /usr/local/bin/liquibase

# Docker
docker pull liquibase/liquibase

# Verify installation
liquibase --version

JDBC Drivers

# Drivers are included for common databases
# For additional databases, add the JDBC JAR to:
# $LIQUIBASE_HOME/lib/

# Or specify via CLI
liquibase --classpath=/path/to/driver.jar update

Core Commands

CommandDescription
liquibase updateApply pending changesets
liquibase rollbackCount 1Rollback the last N changesets
liquibase rollbackToDate 2025-01-01Rollback to a specific date
liquibase rollback v1.0Rollback to a tag
liquibase statusShow pending changesets
liquibase historyShow applied changesets
liquibase validateValidate changelog syntax
liquibase diffCompare two databases
liquibase diffChangeLogGenerate changelog from diff
liquibase generateChangeLogGenerate changelog from existing DB
liquibase tag v1.0Tag the current database state
liquibase dropAllDrop all database objects
liquibase clearCheckSumsClear stored checksums
liquibase updateSQLPreview SQL without executing
liquibase futureRollbackSQLPreview rollback SQL

Changelog Formats

YAML Format

# db/changelog/db.changelog-master.yaml
databaseChangeLog:
  - changeSet:
      id: 1
      author: alice
      changes:
        - createTable:
            tableName: users
            columns:
              - column:
                  name: id
                  type: int
                  autoIncrement: true
                  constraints:
                    primaryKey: true
                    nullable: false
              - column:
                  name: email
                  type: varchar(255)
                  constraints:
                    unique: true
                    nullable: false
              - column:
                  name: name
                  type: varchar(100)
              - column:
                  name: created_at
                  type: timestamp
                  defaultValueComputed: CURRENT_TIMESTAMP

  - changeSet:
      id: 2
      author: alice
      changes:
        - createTable:
            tableName: posts
            columns:
              - column:
                  name: id
                  type: int
                  autoIncrement: true
                  constraints:
                    primaryKey: true
              - column:
                  name: title
                  type: varchar(200)
              - column:
                  name: user_id
                  type: int
                  constraints:
                    foreignKeyName: fk_posts_user
                    references: users(id)
      rollback:
        - dropTable:
            tableName: posts

SQL Format

-- db/changelog/001-create-users.sql

-- changeset alice:1
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE,
    name VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- rollback DROP TABLE users;

-- changeset alice:2
CREATE INDEX idx_users_email ON users(email);
-- rollback DROP INDEX idx_users_email;

-- changeset alice:3
ALTER TABLE users ADD COLUMN role VARCHAR(20) DEFAULT 'user';
-- rollback ALTER TABLE users DROP COLUMN role;

XML Format

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
  http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

  <changeSet id="1" author="alice">
    <createTable tableName="users">
      <column name="id" type="int" autoIncrement="true">
        <constraints primaryKey="true" nullable="false"/>
      </column>
      <column name="email" type="varchar(255)">
        <constraints unique="true" nullable="false"/>
      </column>
    </createTable>
  </changeSet>

</databaseChangeLog>

Configuration

liquibase.properties

# liquibase.properties
changeLogFile=db/changelog/db.changelog-master.yaml
url=jdbc:postgresql://localhost:5432/mydb
username=postgres
password=password
driver=org.postgresql.Driver

# Reference database (for diff operations)
referenceUrl=jdbc:postgresql://localhost:5432/mydb_staging
referenceUsername=postgres
referencePassword=password

# Output settings
outputDefaultCatalog=false
outputDefaultSchema=false
logLevel=info

Docker Compose Integration

services:
  liquibase:
    image: liquibase/liquibase
    depends_on:
      - db
    volumes:
      - ./db/changelog:/liquibase/changelog
    command: >
      --url=jdbc:postgresql://db:5432/mydb
      --username=postgres
      --password=password
      --changeLogFile=changelog/db.changelog-master.yaml
      update

Advanced Usage

Preconditions

- changeSet:
    id: 5
    author: alice
    preConditions:
      - onFail: MARK_RAN
      - not:
          - tableExists:
              tableName: users
    changes:
      - createTable:
          tableName: users
          columns:
            - column:
                name: id
                type: int

Contexts and Labels

# Apply changes based on environment
- changeSet:
    id: 10
    author: alice
    context: dev,test
    changes:
      - insert:
          tableName: users
          columns:
            - column: { name: email, value: "test@example.com" }

- changeSet:
    id: 11
    author: alice
    labels: "feature-1.2,JIRA-456"
    changes:
      - addColumn:
          tableName: users
          columns:
            - column: { name: phone, type: varchar(20) }
# Run with context
liquibase --contexts=dev update

# Run with labels
liquibase --label-filter="feature-1.2" update

Include and IncludeAll

# Master changelog that includes others
databaseChangeLog:
  - include:
      file: db/changelog/001-users.yaml
  - include:
      file: db/changelog/002-posts.yaml
  - includeAll:
      path: db/changelog/migrations/
      relativeToChangelogFile: true

Custom SQL Changes

- changeSet:
    id: 20
    author: alice
    changes:
      - sql:
          sql: >
            CREATE OR REPLACE FUNCTION update_timestamp()
            RETURNS TRIGGER AS $$
            BEGIN
              NEW.updated_at = CURRENT_TIMESTAMP;
              RETURN NEW;
            END;
            $$ LANGUAGE plpgsql;
      - sql:
          sql: >
            CREATE TRIGGER update_users_timestamp
            BEFORE UPDATE ON users
            FOR EACH ROW
            EXECUTE FUNCTION update_timestamp();
    rollback:
      - sql:
          sql: >
            DROP TRIGGER IF EXISTS update_users_timestamp ON users;
            DROP FUNCTION IF EXISTS update_timestamp();

CI/CD Integration

# GitHub Actions
name: Database Migration
on:
  push:
    branches: [main]
    paths: ['db/changelog/**']
jobs:
  migrate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Liquibase
        uses: liquibase/liquibase-github-action@v7
        with:
          operation: update
          classpath: db/changelog
          changeLogFile: db.changelog-master.yaml
          url: ${{ secrets.DB_URL }}
          username: ${{ secrets.DB_USER }}
          password: ${{ secrets.DB_PASS }}

Troubleshooting

IssueSolution
Checksum validation failedRun liquibase clearCheckSums (note: only if changeset was intentionally modified)
Lock stuck on DATABASECHANGELOGLOCKRun liquibase releaseLocks or manually update the lock table
Migration order wrongUse include with explicit file order in master changelog
Rollback not supportedAdd explicit rollback blocks to changesets; not all change types auto-generate rollbacks
SQL syntax errorUse updateSQL to preview before running; check database-specific syntax
Driver not foundAdd JDBC driver JAR to lib/ directory or specify --classpath
Context not filteringEnsure --contexts flag is passed; check changeset context attribute
Diff generating too many changesUse diffTypes to limit comparison scope
Large changelog slowSplit into multiple files using include and includeAll