本文使用的 Docker 镜像
Liquibase 是什么
Liquibase 是 Liquibase 公司维护的数据库变更工具。它把建表、改字段、索引和初始数据写成 changelog,按 changeSet 的顺序执行,并在目标库里记下哪些变更已经跑过。社区版 Docker 镜像里有 Liquibase 命令行、Java 和包管理器 lpm,没有管理网页。
和把一段 SQL 直接交给数据库执行不同,Liquibase 会记住每个 changeSet 的执行结果。同一份 changelog 再跑一次时,已经完成的变更会跳过,只继续还没执行的部分。
Liquibase 能做什么
- 按 changelog 更新数据库:用 XML、YAML、JSON 或 SQL 描述变更,把表结构和初始数据应用到目标库。
- 改库前先看 SQL:先生成将要执行的语句,确认后再应用到数据库。
- 查看还没执行的变更:对照 changelog 和库里的执行记录,看还剩哪些 changeSet。
- 从已有数据库生成 changelog:把当前库结构写成文件。
- 按记录回退:changeSet 里写了回退步骤时,可以撤回去。
为什么要使用 Liquibase
多人往同一套库里交 SQL 时,测试库和生产库容易各改各的,也不好确认某段脚本有没有执行过。
Liquibase 把变更放进仓库,开发库和测试库用同一份 changelog,执行结果留在库里。适合管理应用表结构、又不想在每台机器上单独安装 Java 的人。
使用 Docker 部署 Liquibase
Liquibase 的 Docker 镜像是命令行工具。它执行完指定的变更命令就会退出,不是一直开着的网页服务。部署前准备 Docker,并准备一个它能连上的数据库。本文用 PostgreSQL 作为这次变更的目标库。
接下来用 Docker Compose 写好 changelog 和连接参数,装上 PostgreSQL 驱动,启动数据库并执行变更,再查询表里的数据。
环境要求
社区版 5.0.4 于 2026-08-20 发布。这次在 Ubuntu 24.04 上实测,主机名 ikuai-ubuntu2404。日志里的版本是 Liquibase 5.0.4,Java 是 21.0.11。镜像构建文件以 Eclipse Temurin 21(Ubuntu Noble)为底,容器用户是 liquibase,UID 和 GID 都是 1001。本机不用再装 Java。标签页列出 linux/amd64 和 linux/arm64。
Liquibase 本身不监听端口。PostgreSQL 发布在宿主机 5432,容器内也是 5432。changelog 放在工作目录里,挂进容器的 /liquibase/changelog。
| 项目 | 建议 |
|---|---|
| 系统 | Linux。macOS 把工作目录换成 ~/docker/liquibase |
| Docker | 已安装 Docker Engine 与 Compose V2(docker compose) |
| 架构 | amd64、arm64。命令使用多架构标签 5.0.4 |
| 磁盘 | Liquibase 镜像、带驱动的本地镜像,以及 PostgreSQL 数据卷 |
| 端口 | 宿主机 5432 → PostgreSQL 容器 5432。Liquibase 无监听端口 |
| 工作目录 | /www/wwwroot/liquibase |
5432 已被占用时,只改 Compose 里冒号左边的宿主机端口。容器之间仍用服务名 postgres 和容器端口 5432 通信,JDBC 地址不用跟着改。
bashdocker --version docker compose version
Linux 未装 Docker 可使用轩辕镜像一键安装脚本:
bashbash <(wget -qO- https://get.xuanyuan.cloud/docker.sh)
备用地址:
bashbash <(wget -qO- https://get.xuanyuan.me/docker.sh)
更多见 轩辕镜像使用手册。
标签怎么选
新安装使用 5.0.4。这是 2026-08-20 的 Liquibase 社区版。发布说明里写了数据库口令不再出现在报错和日志中,并包含 PostgreSQL、MySQL、MariaDB、Oracle 上的一批修复。
5.0 及以后的社区版在 liquibase/liquibase,许可证是 Functional Source License(FSL),可以用它管理自己的数据库变更。Docker 官方库里的 liquibase 镜像对应 4.x,4.x 仍是 Apache 2.0。有商业许可、要用策略检查等企业功能时,换 liquibase/liquibase-secure。
从 5.0 起,社区版镜像默认不带数据库驱动。连 PostgreSQL 之前,要在构建时用 lpm 把驱动装进镜像。
latest 和 5.0 目前与 5.0.4 是同一镜像,下次发布后会改指向。5.0.4-alpine 是同一版本的 Alpine 变体,底包和标准镜像不同。本文命令使用 5.0.4。
| 标签 | 说明 | 是否写入本文命令 |
|---|---|---|
5.0.4 | 2026-08-20 的社区版,amd64 与 arm64 | 是 |
5.0.4-alpine | 同一版本的 Alpine 变体 | 否 |
5.0、latest | 浮动标签,目前指向 5.0.4 | 否 |
5.0-alpine、latest-alpine、alpine | Alpine 浮动标签 | 否 |
4.x 数字标签 | 上一主版本,Apache 2.0 | 否 |
本文的 PostgreSQL 使用 17.10。上游 Compose 示例写的是 postgres:15-alpine,该标签会跟着 15 系列的新版本走。
完整标签见标签列表。5.0.4 的变更写在 v5.0.4 发布说明。
拉取镜像
用 轩辕镜像 加速拉取。
Liquibase:
bashdocker pull docker.xuanyuan.run/liquibase/liquibase:5.0.4
Ubuntu 24.04 上拉取完成:
textDigest: sha256:060f96efff4811ccf03957ff3160f21ea04139b89940640f8ad54a065cdc6906 Status: Downloaded newer image for docker.xuanyuan.run/liquibase/liquibase:5.0.4 docker.xuanyuan.run/liquibase/liquibase:5.0.4
PostgreSQL:
bashdocker pull docker.xuanyuan.run/library/postgres:17.10
textDigest: sha256:7958605b474b3d264a969cb3a123d6aa00ad1e1fe9da8a69984dabb704d93317 Status: Downloaded newer image for docker.xuanyuan.run/library/postgres:17.10 docker.xuanyuan.run/library/postgres:17.10
Docker Compose 部署
下面这份 Compose 让 PostgreSQL 保持运行,并让 Liquibase 执行一次 update。changelog 挂到容器内 /liquibase/changelog。连接参数写在 liquibase.properties。示例库名 liquibase_demo,用户 liquibase,口令 liquibase_password。
这是示例口令。5432 能被别人访问时,先把口令换成你自己的。liquibase.properties 里的 password、Compose 里的 POSTGRES_PASSWORD 和 LIQUIBASE_COMMAND_PASSWORD 要改成同一个值。
5.0 社区版镜像不含 PostgreSQL 驱动。构建阶段会执行 lpm add postgresql --global,这一步需要能访问外网。构建出来的本地镜像名是 liquibase-postgresql:5.0.4。
Liquibase 在 Compose 网络里通过服务名 postgres 连接数据库,JDBC 地址是 jdbc:postgresql://postgres:5432/liquibase_demo。这里要写服务名,不写 127.0.0.1。
| 宿主机 | 容器内 | 用途 |
|---|---|---|
| 5432 | 5432 | PostgreSQL。本机客户端连 服务器IP:5432 |
changelog/ | /liquibase/changelog | changelog 文件 |
liquibase.properties | /liquibase/liquibase.properties | 连接参数和 changelog 文件名 |
数据卷 postgres_data | /var/lib/postgresql/data | 数据库文件。删除容器不会删除这个卷 |
- 先看 5432 是否已有进程监听。有输出时,下一步把
compose.yaml里5432:5432冒号左边改成空闲端口。没有输出再继续:
bashsudo ss -lntp | grep -E ':5432' || true
这次没有输出,5432 没有进程监听。
- 创建工作目录。changelog 要让容器里的用户能读,不要把目录权限收成只有所有者可读:
bashsudo mkdir -p /www/wwwroot/liquibase/changelog sudo chown -R "$USER":"$USER" /www/wwwroot/liquibase cd /www/wwwroot/liquibase
- 写入 changelog。主文件用
include带上建表和插入数据两份变更,路径相对主文件本身:
bashcat > changelog/db.changelog-master.xml <<'EOF' <?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"> <include file="001-create-users-table.xml" relativeToChangelogFile="true"/> <include file="002-insert-sample-data.xml" relativeToChangelogFile="true"/> </databaseChangeLog> EOF cat > changelog/001-create-users-table.xml <<'EOF' <?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="docker-compose-example"> <createTable tableName="users"> <column name="id" type="SERIAL" autoIncrement="true"> <constraints primaryKey="true" nullable="false"/> </column> <column name="username" type="VARCHAR(50)"> <constraints nullable="false" unique="true"/> </column> <column name="email" type="VARCHAR(100)"> <constraints nullable="false" unique="true"/> </column> <column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP"> <constraints nullable="false"/> </column> </createTable> </changeSet> </databaseChangeLog> EOF cat > changelog/002-insert-sample-data.xml <<'EOF' <?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="2" author="docker-compose-example"> <insert tableName="users"> <column name="username" value="john_doe"/> <column name="email" value="john.doe@example.com"/> </insert> <insert tableName="users"> <column name="username" value="jane_smith"/> <column name="email" value="jane.smith@example.com"/> </insert> </changeSet> </databaseChangeLog> EOF
- 写入连接配置。镜像自带的配置打开了 headless,换了 defaults 文件后仍要保留,避免命令停下来等输入:
bashcat > liquibase.properties <<'EOF' liquibase.headless=true changeLogFile=db.changelog-master.xml url=jdbc:postgresql://postgres:5432/liquibase_demo username=liquibase password=liquibase_password driver=org.postgresql.Driver searchPath=/liquibase/changelog EOF
- 写入 Dockerfile。这一层在已经拉下的
5.0.4上安装 PostgreSQL 驱动:
bashcat > Dockerfile <<'EOF' FROM docker.xuanyuan.run/liquibase/liquibase:5.0.4 RUN lpm add postgresql --global EOF
- 写入
compose.yaml:
bashcat > compose.yaml <<'EOF' services: postgres: image: docker.xuanyuan.run/library/postgres:17.10 container_name: liquibase-postgres restart: unless-stopped environment: POSTGRES_DB: liquibase_demo POSTGRES_USER: liquibase POSTGRES_PASSWORD: liquibase_password ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U liquibase -d liquibase_demo"] interval: 10s timeout: 5s retries: 5 liquibase: build: context: . dockerfile: Dockerfile image: liquibase-postgresql:5.0.4 container_name: liquibase restart: "no" depends_on: postgres: condition: service_healthy volumes: - ./changelog:/liquibase/changelog - ./liquibase.properties:/liquibase/liquibase.properties environment: LIQUIBASE_COMMAND_URL: jdbc:postgresql://postgres:5432/liquibase_demo LIQUIBASE_COMMAND_USERNAME: liquibase LIQUIBASE_COMMAND_PASSWORD: liquibase_password command: ["--defaults-file=/liquibase/liquibase.properties", "update"] volumes: postgres_data: EOF
| 配置 | 说明 |
|---|---|
image: docker.xuanyuan.run/library/postgres:17.10 | PostgreSQL 具体版本 |
image: liquibase-postgresql:5.0.4 | 本地构建结果。基础镜像是 docker.xuanyuan.run/liquibase/liquibase:5.0.4 |
restart: "no" | Liquibase 跑完 update 就退出。不要设成一直重启 |
depends_on + service_healthy | 数据库接受连接之后再执行变更 |
- 构建并启动。构建时 lpm 会下载 PostgreSQL 驱动:
bashdocker compose up -d --build
这次 [2/2] RUN lpm add postgresql --global 完成。构建日志把本地镜像写成 docker.io/library/liquibase-postgresql:5.0.4,这是本机的构建结果,不是再到 Docker Hub 拉取。随后 PostgreSQL 变为 Healthy,Liquibase 容器启动:
text✔ Image liquibase-postgresql:5.0.4 Built ✔ Network liquibase_default Created ✔ Volume liquibase_postgres_data Created ✔ Container liquibase-postgres Healthy ✔ Container liquibase Started
- 查看容器状态:
bashdocker compose ps -a
这次查看时,Liquibase 仍在运行,PostgreSQL 已经是 healthy。Liquibase 没有端口映射:
textNAME IMAGE COMMAND SERVICE CREATED STATUS PORTS liquibase liquibase-postgresql:5.0.4 "/liquibase/docker-e…" liquibase 29 seconds ago Up 14 seconds liquibase-postgres docker.xuanyuan.run/library/postgres:17.10 "docker-entrypoint.s…" postgres 30 seconds ago Up 26 seconds (healthy) 0.0.0.0:5432->5432/tcp, [::]:5432->5432/tcp
Liquibase 是命令行,命令结束后容器会退出。刚启动就看状态,经常还是 Up。等下一步日志出现成功字样后,再看退出码。退出码为 0 时,表示这次命令结束时没有报错。
- 查看 Liquibase 的输出:
bashdocker compose logs liquibase
这次日志里的版本和执行结果是:
textStarting Liquibase at 08:16:28 using Java 21.0.11 (version 5.0.4 #11282 built at 2026-08-18 12:10:05 UTC) Liquibase Version: 5.0.4 Running Changeset: 001-create-users-table.xml::1::docker-compose-example Running Changeset: 002-insert-sample-data.xml::2::docker-compose-example UPDATE SUMMARY Run: 2 Previously run: 0 Filtered out: 0 ------------------------------- Total change sets: 2 Liquibase: Update has been successful. Rows affected: 2 Liquibase command 'update' was executed successfully.
Run 是这次新执行的 changeSet 数,Previously run 是库里已经记录过的数量。执行记录写在 DATABASECHANGELOG 里。
- 查询
users。第二条 changeSet 插入了john_doe和jane_smith:
bashdocker compose exec postgres psql -U liquibase -d liquibase_demo -c "SELECT id, username, email FROM users ORDER BY id;"
textid | username | email ----+------------+------------------------ 1 | john_doe | john.doe@example.com 2 | jane_smith | jane.smith@example.com (2 rows)
- 要再跑同一份 changelog,等这次命令结束后执行:
bashdocker compose up liquibase
再看新日志里的 Previously run。已经成功的 changeSet 会计入这一列,而不是再记一笔 Run。
不再需要这套服务时,在工作目录停止并删除容器。数据卷还在:
bashdocker compose down
数据库文件也不再需要时,再连数据卷一起删除:
bashdocker compose down -v
备选:docker run
没有 Compose 时,用下面的命令完成同一次变更。工作目录、changelog、liquibase.properties 和 Dockerfile 与上一节相同。
这一节和还在运行的 Compose 不要同时占用 5432。已经用 Compose 启动过时,先在工作目录执行 docker compose down。
properties 里的主机名是 postgres。数据库容器要加上这个网络别名,JDBC 地址才不用改。
- 创建网络:
bashdocker network create liquibase_net
- 启动 PostgreSQL:
bashdocker run -d --name liquibase-postgres --network liquibase_net --network-alias postgres --restart unless-stopped -e POSTGRES_DB=liquibase_demo -e POSTGRES_USER=liquibase -e POSTGRES_PASSWORD=liquibase_password -p 5432:5432 -v liquibase_postgres_data:/var/lib/postgresql/data --health-cmd "pg_isready -U liquibase -d liquibase_demo" --health-interval 10s --health-timeout 5s --health-retries 5 docker.xuanyuan.run/library/postgres:17.10
- 等到健康检查变成 healthy,再继续:
bashuntil [ "$(docker inspect --format '{{.State.Health.Status}}' liquibase-postgres)" = "healthy" ]; do sleep 2 done
- 构建带 PostgreSQL 驱动的镜像。这一步需要能访问外网:
bashcd /www/wwwroot/liquibase docker build -t liquibase-postgresql:5.0.4 .
- 执行 update。容器结束后会删除,变更留在数据库里:
bashdocker run --rm --name liquibase --network liquibase_net -v /www/wwwroot/liquibase/changelog:/liquibase/changelog -v /www/wwwroot/liquibase/liquibase.properties:/liquibase/liquibase.properties -e LIQUIBASE_COMMAND_URL="jdbc:postgresql://postgres:5432/liquibase_demo" -e LIQUIBASE_COMMAND_USERNAME="liquibase" -e LIQUIBASE_COMMAND_PASSWORD="liquibase_password" liquibase-postgresql:5.0.4 --defaults-file=/liquibase/liquibase.properties update
- 查询
users:
bashdocker exec liquibase-postgres psql -U liquibase -d liquibase_demo -c "SELECT id, username, email FROM users ORDER BY id;"
不再需要时,删除数据库容器和网络。数据卷单独再删:
bashdocker rm -f liquibase-postgres docker network rm liquibase_net
bashdocker volume rm liquibase_postgres_data
常见问题
浏览器打不开 Liquibase
社区版镜像没有网页,也不映射 HTTP 端口。docker compose ps 里只有 PostgreSQL 有 5432。变更是否成功,看 Liquibase 日志和数据库里的表。
容器还显示 Up,或马上变成退出
刚执行 docker compose ps 时,update 可能还在跑,状态是 Up。日志里出现 Liquibase command 'update' was executed successfully. 之后,容器会退出。退出码为 0 表示这次命令没有报错。PostgreSQL 应仍在运行。
退出码不是 0 时,用 docker compose logs liquibase 看报错。不要把 Liquibase 设成 restart: always,失败后会反复执行。
日志里找不到 PostgreSQL 驱动
5.0 社区版默认不带 JDBC 驱动。确认 Dockerfile 里有 RUN lpm add postgresql --global,并且 liquibase 服务用的是构建出来的 liquibase-postgresql:5.0.4。构建日志里如果是下载驱动失败,先确认这台机器能访问外网,再执行 docker compose up -d --build。
INSTALL_MYSQL=true 只安装 MySQL 驱动,不会装上 PostgreSQL 驱动。
想连 MySQL 或已有的数据库
本文的 PostgreSQL 只作演示库。连已有数据库时,改 liquibase.properties 里的 url、username、password 和 driver,并装上对应驱动。MySQL 驱动因许可未随镜像提供,可以在启动时设置 INSTALL_MYSQL=true。在 Compose 网络里,主机名写服务名,不写 127.0.0.1。
changelog 报找不到文件
changelog 目录要挂到 /liquibase/changelog。changeLogFile 写相对这个目录的文件名,例如 db.changelog-master.xml。include 使用 relativeToChangelogFile="true" 时,被包含的文件和主文件放在同一目录。5.0.4 在默认的安全解析下会拒绝带 DOCTYPE 声明的 XML changelog,标准 changelog 不要加 DOCTYPE。
第二次执行又插入了数据,或报唯一约束冲突
Liquibase 只跳过已经记入 DATABASECHANGELOG 的 changeSet。如果第一次 update 没成功,表可能建了一半,记录里没有这次 changeSet,再跑会按 changelog 重新执行。先看 docker compose logs liquibase。确认库可以清空时,再执行 docker compose down -v,然后重新 docker compose up -d。不要在还要保留数据的库上删数据卷。
口令改了一处,还是登不进去
示例口令 liquibase_password 要在三个地方保持一致:liquibase.properties 的 password、POSTGRES_PASSWORD、LIQUIBASE_COMMAND_PASSWORD。数据卷已经用旧口令初始化之后,只改环境变量不会改掉库里的口令。
用 latest 或 5.0 可以吗
这两个标签目前指向 5.0.4,下次发布后会指向新版本。本文命令使用 5.0.4。
命令速查
bashdocker pull docker.xuanyuan.run/liquibase/liquibase:5.0.4 docker pull docker.xuanyuan.run/library/postgres:17.10 cd /www/wwwroot/liquibase docker compose up -d --build docker compose ps -a docker compose logs liquibase docker compose exec postgres psql -U liquibase -d liquibase_demo -c "SELECT id, username, email FROM users ORDER BY id;" docker compose down
没有 Compose 时用「备选:docker run」里的命令。Liquibase 没有网页,也没有预置的登录账号。数据库示例用户是 liquibase,示例口令是 liquibase_password。
延伸阅读
镜像的 Dockerfile 现放在 liquibase/liquibase 仓库的 docker/ 目录。liquibase/docker 仓库已归档,问题提到上面的 Issues。
阅读原文
本文基于 liquibase/liquibase:5.0.4,实测引擎 Liquibase 5.0.4,Java 21.0.11,测试平台 Ubuntu 24.04 Linux。
评论交流
免责声明
本博客文章所提供的内容、技术方案、配置示例及部署指南等信息,仅供学习交流和技术参考使用。文章内容基于发布时的技术环境和版本信息编写,可能因时间推移、技术更新或环境差异而存在不适用的情况。
用户在参考本博客内容进行部署操作前,应当充分了解相关技术风险,并建议在测试环境中进行充分验证和测试,确认无误后再考虑在生产环境中使用。生产环境部署前,请务必进行数据备份,并制定相应的回滚方案。
用户因使用本博客内容进行部署操作而产生的任何损失、数据丢失、系统故障、安全风险或其他问题,均由用户自行承担全部责任。轩辕镜像官方不对因使用本博客内容而产生的任何直接或间接损失承担责任。
本免责声明的最终解释权归轩辕镜像官方所有。
