The golden rule:
- No one changes schema manually
- Every schema change goes through Git + Liquibase
docker-compose.yml:
[kmaau@rocky ~]$ mkdir ${HOME}/liquibase-demo
[kmaau@rocky ~]$ ls -ld ${HOME}/liquibase-demo
drwxr-xr-x. 2 kmaau kmaau 6 Aug 3 20:50 /home/kmaau/liquibase-demo
[kmaau@rocky ~]$ cd ${HOME}/liquibase-demo
[kmaau@rocky liquibase-demo]$ vi docker-compose.yml
::
::
[kmaau@rocky liquibase-demo]$ cat docker-compose.yml
[[blue:services:]]
[[blue: postgres:]]
[[blue: image: postgres:18]]
[[blue: container_name: postgres-demo]]
[[blue: environment:]]
[[blue: POSTGRES_DB: appdb]]
[[blue: POSTGRES_USER: appuser]]
[[blue: POSTGRES_PASSWORD: password]]
[[blue: ports:]]
[[blue: - "5432:5432"]]
#
# spin up PostgreSQL in docker:
#
kmaau@rocky liquibase-demo]$ docker compose up --detach
[+] up 17/18
⠦ Image postgres:18 [[[green:⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]]] 150.2MB / 159.8MB Pulling
[[green:✔]] Image postgres:18 [[green:Pulled]] 10.0s
[[green:✔]] Network liquibase-demo_default [[green:Created]] 0.1s
⠦ Container postgres-demo [[green:Started]] 0.6s
kmaau@rocky liquibase-demo]$ docker image list
i Info → U In Use
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
postgres:18 3a82e1f56c8f 644MB 168MB U
[kmaau@rocky liquibase-demo]$ docker compose ps --all
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
postgres-demo postgres:18 "docker-entrypoint.s…" postgres 7 seconds ago Up 6 seconds 0.0.0.0:5432->5432/tcp,
[::]:5432->5432/tcp
#
# connect to the PostgreSQL:
#
[kmaau@rocky liquibase-demo]$ psql --host=localhost --username=appuser --dbname=appdb
Password for user appuser: [[yellow:password]]
psql (18.4)
Type "help" for help.
appdb=# SELECT version();
version
--------------------------------------------------------------------------------------------------------------------
PostgreSQL 18.4 (Debian 18.4-1.pgdg13+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
(1 row)
appdb=# \quit
#
# configure Liquibase:
#
[kmaau@rocky liquibase-demo]$ mkdir db
[kmaau@rocky liquibase-demo]$ ls -ld db
drwxr-xr-x. 2 kmaau kmaau 6 Aug 4 03:03 db
[kmaau@rocky liquibase-demo]$ cd db
[kmaau@rocky db]$ tree .
.
├── changelog.xml
├── liquibase.properties
└── postgresql-42.7.13.jar
0 directories, 3 files
[kmaau@rocky db]$ cat liquibase.properties
[[blue:changeLogFile=changelog.xml]]
[[blue:url=jdbc:postgresql://localhost:5432/appdb]]
[[blue:username=appuser]]
[[blue:password=password]]
[[blue:classpath=postgresql-42.7.13.jar]]
[kmaau@rocky db]$ cat changelog.xml
[[blue:<?xml version="1.0" encoding="UTF-8"?>]]
[[blue:<databaseChangeLog]]
[[blue: xmlns="http://www.liquibase.org/xml/ns/dbchangelog"]]
[[blue: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"]]
[[blue: xsi:schemaLocation="]]
[[blue: http://www.liquibase.org/xml/ns/dbchangelog]]
[[blue: https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">]]
[[blue:]]
[[blue: <changeSet id="001" author="kmaau">]]
[[blue: <createTable tableName="users">]]
[[blue: <column name="id" type="BIGINT">]]
[[blue: <constraints primaryKey="true" nullable="false"/>]]
[[blue: </column>]]
[[blue: <column name="username" type="VARCHAR(100)"/>]]
[[blue: </createTable>]]
[[blue: </changeSet>]]
[[blue:]]
[[blue:</databaseChangeLog>]]
#
# create first schema:
#
[kmaau@rocky db]$ liquibase validate
Starting Liquibase at 03:23:47 using Java 25.0.4 (version 5.0.3 #10665 built at 2026-05-13 17:55+0000)
Liquibase Version: 5.0.3
No validation errors found.
Liquibase command 'validate' was executed successfully.
[kmaau@rocky db]$ liquibase update
Starting Liquibase at 03:25:15 using Java 25.0.4 (version 5.0.3 #10665 built at 2026-05-13 17:55+0000)
Liquibase Version: 5.0.3
Running Changeset: changelog.xml::001::kmaau
UPDATE SUMMARY
Run: 1
Previously run: 0
Filtered out: 0
-------------------------------
Total change sets: 1
Liquibase: Update has been successful. Rows affected: 0
Liquibase command 'update' was executed successfully.
[kmaau@rocky db]$ psql --host=localhost --username=appuser --dbname=appdb --command "\dt+;"
Password for user appuser: [[yellow:password]]
List of tables
Schema | Name | Type | Owner | Persistence | Access method | Size | Description
--------+-----------------------+-------+---------+-------------+---------------+------------+-------------
public | databasechangelog | table | appuser | permanent | heap | 16 kB |
public | databasechangeloglock | table | appuser | permanent | heap | 8192 bytes |
public | users | table | appuser | permanent | heap | 0 bytes |
(3 rows)
Cleanup:
[kmaau@rocky db]$ docker compose down
[+] down 2/2
[[green:✔]] Container postgres-demo [[green:Removed]] 0.3s
[[green:✔]] Network liquibase-demo_default [[green:Removed]]
kmaau@rocky ~]$ cd ${HOME}
[kmaau@rocky ~]$ rm -rf ${HOME}/liquibase-demo