- Concept
- Version · 6.0
- Create
Generate your changelog structure with --object-changelogs
Last updated: September 29, 2026
If you are starting from an existing database, you do not have to build a changelog structure by hand. The --object-changelogs parameter tells generate-changelog and diff-changelog to split what they generate across separate files, either one folder per object type or one file per table.
This is the fastest route to a working structure, and it is worth considering before you commit to laying anything out yourself.
Generate the structure
Run either generate-changelog or diff-changelog with --object-changelogs. The file you name with --changelog-file becomes a master changelog that includes the generated files, and the objects themselves are written to a separate directory beside it.
The name of that directory depends on which command you run. generate-changelog compares your database against an empty database, so it writes to a directory named objects. diff-changelog compares two databases, so each run writes to a new directory named objects- followed by the time the run started, counted in milliseconds since midnight UTC on 1 January 1970, as in objects-1757980800000. If the target directory already exists, the command stops and asks you to remove or rename it rather than overwrite the files in it.
Inside that directory, each path is prefixed with the object's schema when the generated change carries one. For formatted SQL changelogs, the per-object filenames follow the naming of the master changelog. When the master carries a database type segment, Liquibase inserts the same short name before the extension, as in orders.postgresql.sql. When the master has no segment, the per-object files have none either, as in orders.sql.
Organize by object type
Set --object-changelogs=all to give every supported object type its own directory, or name individual types to externalize only those. For example, --object-changelogs=table,view externalizes tables and views and leaves the rest in the master changelog. Files are grouped by object type, as <schema>/<type>/<object>.
The type directories are tables, views, inserts, sequences, indexes, storedprocedures, functions, triggers, packages, synonyms, types, foreignkeys, primarykeys, uniqueconstraints, checkconstraints, and columns.
Example output with --object-changelogs=all
Set --object-changelogs=none to keep every changeset in the single changelog file.
What you get depends on your platform and your schema
Folders are only created for object types that actually exist in your database, so your output will not match the example above unless your schema happens to contain all of it.
Support also varies by platform, because Liquibase can only generate an object type on a database where it can read that type:
packages/is created on Oracle and DB2 version 10 and latersynonyms/is created on Oracle, DB2, and SQL Serversequences/is not created on MySQL, SQLite, or Sybase, and requires MariaDB 10.3 or later and SQL Server 2012 or laterstoredprocedures/is created on Oracle, MySQL, SQL Server, DB2, Snowflake, and PostgreSQL 11 or laterfunctions/is created on Oracle, MySQL, SQL Server, DB2, Snowflake, and PostgreSQLtriggers/is created on Oracle, MySQL, SQL Server, DB2, and PostgreSQLtypes/holds PostgreSQL composite types and is not created on any other platform
CockroachDB is excluded from the PostgreSQL entries above.
Package body files sit in packages/ alongside the package spec, with _body appended to the object name, as in my_package_body.xml. On formatted SQL changelogs they carry the database short name as well, as in my_package_body.oracle.sql.
Organize by table
--object-changelogs=by-table arranges output around tables instead of object types. Each table gets its own file holding the table and the objects whose lifecycle is tied to it, so everything about one table sits in one place. Liquibase writes up to two files per table.
<schema>/tables/<table>holds the structural changes: thecreateTable, its columns, and the primary key and unique constraints that Liquibase can create alongside it.<schema>/tables/<table>-postholds the children that must wait: check constraints, triggers scoped to that table, and any index or key that might depend on another object.
Liquibase includes the structural file at its first-arrival position in the master changelog and the post file at its last-arrival position. That ordering means a deferred child runs after anything it references. A trigger that calls a user-defined function, for example, is included after the changelog that creates the function.
Example by-table output
Which file an index or key goes in
Indexes and keys are routed by what they reference, not by their type. An index stays in the structural file when Liquibase can confirm it is safe there, meaning none of its columns use a user-defined function and its using value is a built-in storage mode. Otherwise it moves to the post file. A primary key or unique constraint that names a backing index with forIndexName also moves to the post file, and the index it names follows it there so the index is created first. When Liquibase cannot determine that an object is safe, it defers the object to the post file.
Objects that are not table children
Objects that do not belong to a table keep their own per-type directories: views, sequences, synonyms, storedprocedures, functions, packages, types, inserts, and ddltriggers. A trigger that is not scoped to a table is written to ddltriggers rather than folded into a table file.
Foreign keys go in pair-named directories in the form <base>_<reference>. A self-referential key uses <table>_<table>. When the tables carry schema names, each side is schema-qualified, as in public.orders_public.users. Because the directory name already carries both qualified table names, foreign key directories sit at the top level of the objects directory instead of under a schema directory.
by-table with diff-changelog
Both commands use the same layout, but diff-changelog applies it to the differences it finds rather than to a whole database. You get files only for the tables that differ, and a table file can contain a change such as addColumn with no createTable in it. A table that differs only by a deferred child, such as a new check constraint, produces just the -post file.
Rules and restrictions
by-table is mutually exclusive with all and with every per-type value. A combination such as by-table,index is rejected, because folding children into their parent table's file conflicts with the per-type folder layout. Liquibase accepts the value written as by-table, by_table, or BY_TABLE.
Note: --object-changelogs accepts the same values on generate-changelog and diff-changelog. For the full value list and how to set the parameter in each environment, see the reference page for the command you are running.
When to generate and when to lay it out yourself
Generate when you have an existing database, want the by-object or by-table layout, and want to be running today. You can always reorganize later, because the changesets are what matter and the files are just where they live.
Lay it out yourself when you want the by-release layout, when your team has a directory convention that generation will not match, or when you are starting from an empty database and there is nothing to generate from.
Either way, read the best practices on the layout you choose. Generation gives you files, not a convention for what happens next.
Organize changes by object and Organize changes by release