Create custom rollback statements in Liquibase
Last updated: November 18, 2025
Procedure
Decide if you need to create rollback statements.
Liquibase can automatically generate rollback SQL for many Change Types, such as createTable, renameColumn, and addColumn. If your changelog only uses these supported Change Types, you do not need to manually define rollback logic—Liquibase will handle it for you when you run the rollback command.
However, not all Change Types support automatic rollback. For example, dropTable, insert, and all formatted SQL changesets do not have a clearly defined inverse operation, so Liquibase cannot generate rollback SQL for them. In these cases, you must provide custom rollback logic inside the changeset.
Note: When rolling back stored logic, Liquibase does not restore the previously stored version. Instead, Liquibase rolls back to the exact file/code specified in the custom rollback.
You can also override the default rollback even for Change Types that do support auto rollback, giving you more control over how your database is restored.
You can use automatic rollback with XML, JSON, and YAML changelogs for any Change Type marked as "Supported" in What change types can I use auto rollback with?
Add a rollback statement.
For any change that you'd like to add rollback to, add a <rollback> tag inside the <changeSet> tag.
If you prefer to store rollback logic in a separate file, use the rollbackSqlFile tag.
Note: You can include options like custom delimiters in the rollbackSqlFile tag.
Add additional rollback statements.
You can also specify multiple Change types within a single <rollback> statement or across multiple <rollback> statements
Change Type syntax
Raw SQL
Additional Options
Override default rollback commands
The <rollback> tag describes how to roll back a change using SQL statements, Change Types, or a reference to a previous changeset. You can use any Change Type in the <rollback> element, such as dropTable, sql, and sqlFile:
YAML
- changeSet:
id: 1
author: liquibaseuser
changes:
- createTable:
tableName: testTable
columns:
- column:
name: id
rollback:
- dropTable:
tableName: testTable
JSON
{
"changeSet": {
"id": 1,
"author": "liquibaseuser",
"changes": [
{
"createTable": {
"tableName": "testTable"
}
}
],
"rollback": [
{
"dropTable": {
"tableName": "testTable"
}
}
]
}
}
XML
<changeSet id="1" author="liquibaseuser">
<createTable tableName="testTable">
<column name="id" type="int"/>
</createTable>
<rollback>
<dropTable tableName="testTable"/>
</rollback>
</changeSet>
Alternatively, you can have raw SQL in the content part of the <rollback> element. For example:
<changeSet id="1" author="liquibaseuser">
<createTable tableName="testTable">
<column name="id" type="int"/>
</createTable>
<rollback>
drop table testTable
</rollback>
</changeSet>
Liquibase treats the raw SQL within <rollback> the same as in the <sql> Change Type, with stripComments set to true, splitStatements set to true, and endDelimiter set to ;. For more details, see the XML example from the sql documentation.
Directly reference changeset
The following example shows how you can use a <rollback> tag to reference the changeset that originally created a statement. This example uses changeset 2 to implement rollback logic against changeset 1:
SQL
--changeset liquibaseuser:1
create table testTable ( id int primary key, name varchar(255) );
--changeset liquibaseuser:2
--rollback drop table testTable;
--rollback changesetId:1 changesetAuthor:liquibaseuser changesetPath:optional/path/to/myChangeLog.sql
YAML
- changeSet:
id: 1
author: liquibaseuser
changes:
- createTable:
tableName: testTable
columns:
- column:
name: id
- changeSet:
id: 2
author: liquibaseuser
rollback:
- dropTable:
tableName: testTable
changeSetId: 1
changeSetAuthor: liquibaseuser
changeSetPath: optional/path/to/myChangeLog.yaml
JSON
{
"changeSet": {
"id": 1,
"author": "liquibaseuser",
"changes": [
{
"createTable": {
"tableName": "testTable",
"columns": [
{
"column": {
"name": "id",
"type": "varchar(255)"
}
}
]
}
}
]
{
"changeSet": {
"id": 2,
"author": "liquibaseuser",
"rollback": [
{
"dropTable": {
"tableName": "testTable"
}
},
"changeSetId": "1",
"changeSetAuthor": "liquibaseuser",
"changeSetPath": "optional/path/to/myChangeLog.json"
]
}
}
XML
<changeSet id="1" author="liquibaseuser">
<createTable tableName="testTable"/>
<column name="id" type="varchar(255)"/>
</createTable>
</changeSet>
<changeSet id="2" author="liquibaseuser">
<dropTable tableName="testTable"/>
<rollback changesetId="1" changesetAuthor="liquibaseuser" changesetPath="optional/path/to/myChangeLog.xml"/>
</changeSet>
Empty rollback statements
If you do not want to revert a change in a rollback mode, use either the keyword empty or the keyword not required inside the rollback tag. In XML, YAML, and JSON changelogs, you can also use an empty string inside the rollback tag.
SQL
--changeset liquibaseuser:1 create table testTable ( id int primary key, name varchar(255) ); --rollback empty
YAML
- changeSet: id: 1 author: liquibase changes: - createTable: tableName: testTable columns: - column: name: id type: int rollback: empty
JSON
{ "changeSet": { "id": 1, "author": "example", "changes": [ { "createTable": { "tableName": "testTable", "columns": [ { "column": { "name": "id", "type": "int" } } ] } } ], "rollback": "empty" } }
XML
<changeSet id="3" author="liquibaseuser"> <createTable tableName="testTable"> <column name="id" type="int"/> </createTable> <rollback>empty</rollback> </changeSet>
You can also use an empty string (<rollback></rollback>) or a self-closing-tag (<rollback/>).
Note: The output Change Type support automatic rollbacks.