Data Management¶
๐ฑ Seeding (Instant Boot)¶
For new projects, LDM automatically attempts to download a Seeded State matching your specific configuration (Liferay version, Database type, and Search mode).
- Database: Pre-initialized schema for Postgres, MySQL (8.4), or HSQL.
- OSGi Cache: Pre-resolved bundle state to skip the resolution phase.
- Search Index: Pre-warmed Elasticsearch indices.
| Option | Effect |
|---|---|
--no-seed |
Disable automatic seeding and start with a completely fresh, un-initialized project. |
ldm re-seed |
Wipe all data for an existing project and re-apply the vanilla seed for that version. |
How Seed Selection Works:
LDM prioritizes an exact match for your environment (e.g., mysql + sidecar). If an exact match isn't available on GitHub, it falls back to the High-Performance Baseline (postgresql + shared).
๐ External Database Connection¶
If you want to use an external database (such as a shared development DB, an RDS instance, or a standalone local MySQL/PostgreSQL server) instead of generating an isolated database container inside your project's stack, use --database-mode external during initialization.
external is a mode, not an engine: it answers who runs the database, and the engine is still whatever it is. Naming it with --db is what lets LDM resolve the JDBC driver and the per-tag Hibernate dialect for your server.
Note
--db external remains valid -- it is the older spelling, kept working deliberately (LDM-#1511). LDM infers the engine from the JDBC URL scheme instead: jdbc:postgresql:// is PostgreSQL, jdbc:mysql:// and jdbc:mariadb:// are MySQL. A URL naming an engine LDM does not support falls back to writing the URL, username and password alone, exactly as before. Projects already carrying db_type: "external" in their meta are migrated on their next run and keep booting unchanged.
Interactive DB Wizard¶
When the mode resolves to external and no JDBC URL is recorded yet, LDM runs an interactive wizard that prompts you for:
- JDBC URL (e.g.
jdbc:postgresql://db.internal.network:5432/lportal) - Database Username
- Database Password
What Happens Under the Hood?¶
- LDM formats your answers into standard Liferay JDBC properties (
jdbc.default.url,jdbc.default.username,jdbc.default.password) and securely appends them directly into your project'sportal-ext.properties. Where the engine is known,jdbc.default.driverClassNameandhibernate.dialectare written too. - It completely excludes the
dbservice block from the generateddocker-compose.yml, and nodepends_onrefers to it. - Liferay boots up normally, but opens a connection out to your specified external database instead of looking for a local container on the Docker bridge network.
Warning
Since the database lives outside of LDM's control, features like automatic Seeding, reset db, ldm db query and complete snapshot backups do not operate on your external database. LDM refuses those rather than reaching for a container it does not own.
snapshot & restore¶
Backup and recover project states, including files, DB, and search indices.
Examples:
# Create a named snapshot
ldm snapshot demo --name "post-setup-gold-standard"
# Delete a specific snapshot by index or name
ldm snapshot demo --delete 1
ldm snapshot demo --delete "post-setup-gold-standard"
# Bulk management/pruning
ldm snapshot demo --keep-last 5 # Delete all but the 5 most recent snapshots
ldm snapshot demo --older-than 30 # Delete all snapshots older than 30 days
# List snapshots for a project
ldm restore demo --list # Non-interactive list of all snapshots
ldm restore demo --index 1 # Restore to index 1
ldm restore demo --name "post-setup-gold-standard" # Restore by name
What a restore does to the search indices. A restore always clears the project's indices on the shared search node first, then restores the snapshot's search indices if it carries any. A snapshot taken while search ran as a sidecar, taken before search existed, or whose search snapshot failed, carries none โ the indices are still cleared, and the reindex LDM schedules for the next boot rebuilds them from the restored database.
This matters because a reindex alone cannot repair a stale index. It re-adds documents for classes that still have indexers; documents whose classes no longer have one are exactly the ones it leaves behind. Before LDM-#2119 the clearing step was skipped whenever there was no search snapshot to put back, and those documents survived the restore invisibly.
If the shared search service is not running, LDM says so and leaves the indices alone rather than reporting a clean restore it did not perform.
What ldm rm --delete keeps¶
ldm rm --delete removes the containers, the volumes, the registry entry and
the project directory. Before the directory goes, LDM archives the part of it
that cannot be regenerated to ~/.ldm/removed/<project>-<timestamp>.tar.gz, and
names the path in its output (LDM-#1703).
The archive holds four things and nothing else:
| Kept | Why |
|---|---|
meta |
the resolved tag, port, database and search mode |
files/ |
portal-ext.properties, including every feature flag |
osgi/configs/ |
configuration overrides |
routes/ |
the routes definition |
Everything heavy is deliberately left out. On a booted 2026.q1.12-lts project,
osgi/ was 1.1 GB and data/ 19 MB -- both rebuildable from the image and the
seed -- while the four above came to roughly 20 KB. Copying the gigabyte would
defeat the delete, whose purpose is to free the space; copying the kilobytes
costs nothing and preserves the only record of how the project was configured.
The newest 50 archives are kept and older ones pruned, which at that size costs about a megabyte.
Credentials are removed by default¶
The archive would otherwise carry the project's database and admin passwords in
plaintext -- jdbc_pass and the credentials entries in meta, and
default.admin.password / jdbc.default.password in
files/portal-ext.properties -- for the last fifty deleted projects,
indefinitely. So LDM removes them, leaving a marker in place of each value:
Everything else is kept, including the admin email, so you know which account to reset. A commented-out password is redacted too: a disabled property is still a plaintext password on disk.
If you would rather keep them -- a password set long ago can be exactly the irreproducible thing worth preserving -- opt in, per command or permanently:
ldm rm demo --delete --keep-credentials # this one removal
ldm config set tombstone_keep_credentials true # every removal
Warning
Opting in means the archive holds credentials in plaintext, and securing
~/.ldm/removed becomes yours to do. LDM writes archives 0600 and the
directory 0700, which is a floor, not a guarantee -- a backup tool copying
that directory copies the passwords with it.
Important
A tombstone restores configuration, never state. Untar it over a fresh
ldm run <project> of the same tag and you get the same setup with an empty
instance. If the database contents mattered, ldm snapshot is the tool that
preserves them, and it has to have been run before the delete.
# Remove a project, freeing the space; the config archive path is printed
ldm rm demo --delete -y
# What was kept
tar -tzf ~/.ldm/removed/demo-20260914-071500.tar.gz
# Rebuild the shape, then restore the configuration over it
ldm run demo -t <the tag from meta>
tar -xzf ~/.ldm/removed/demo-20260914-071500.tar.gz -C ~/ldm/demo
package (Portable Package Export)¶
Bundles a project snapshot (code elements, database backup, document library, and Elasticsearch indices) into a single portable .ldmp package (tarball) alongside a .ldmp.sha256 checksum file. This package is ideal for sharing local environments with other developers or releasing template stacks via GitHub Releases.
# Create a fresh snapshot and package the environment
ldm package
# Package using the latest existing snapshot (skips snapshot generation step)
ldm package --use-latest
# Specify a custom directory output path and bind a GitHub repository identifier
ldm package my-project --output /tmp/packages --repo my-owner/my-repo
โ ๏ธ CI/CD Release Pipelines vs. Local DB Packaging¶
When packaging an .ldmp release using automated CI/CD pipelines (e.g. GitHub Actions), it is important to note that the database containers are typically offline/not running in the headless CI environment.
If your repository contains custom build/packaging hooks that query active Docker containers (for example, checking for running databases to export schemas), the resulting .ldmp package will be generated with a vanilla or blank database, resulting in a default welcome site when other developers import it.
Case Study: Liferay AI Commerce Accelerator (AICA)¶
- The Issue: AICA's packaging script (
scripts/package-ldmp.sh) checks for the running database container usingdocker ps | grep -q "aica-db". - Headless CI Failure: In GitHub Actions (
release.yml), the database containeraica-dbis not running. The packaging hook falls back to generating a blankdatabase.sqland an emptyfiles.tar.gz. The resulting.ldmppackage uploaded to the GitHub Release is empty. -
The Solution:
-
Build the package locally on your host machine where your active database container is running:
-
Manually upload the populated
.ldmppackage and its.ldmp.sha256checksum directly to your GitHub Release assets, replacing the empty files created by the headless CI builder.
hydrate (Local Cloud Backup Hydration)¶
Creates or restores a project from a local Liferay Cloud backup layout (database.gz and volume.tgz).
Database Handling¶
LDM automatically attempts to detect the database type (MySQL or PostgreSQL) by analyzing the database.gz dump header.
- Auto-Detection: If the type is successfully detected, LDM will use it automatically.
- Validation: If you specify
--db, LDM verifies it matches the backup. A mismatch will cause the command to exit. - Ambiguity:
- In interactive mode, if detection fails, LDM prompts you to select a type (defaulting to
postgresql). - In non-interactive mode, if detection fails and no
--dbis provided, LDM exits with an error.
# LDM will auto-detect the DB type from the backup
ldm hydrate /path/to/backup/folder [project-name]
# Manually specify or override (validated against the backup)
ldm hydrate /path/to/backup/folder my-project --tag 2024.q1.3 --db postgresql
cloud-fetch (Fetch Cloud State)¶
Synchronize an existing local project with data, logs, and configuration from Liferay Cloud (LCP). This is used for local debugging and state hydration, not for importing source code.
Note
Prerequisite: You must have the LCP CLI installed and authenticated (lcp login).
# 1. Discover available cloud environments
ldm cloud-fetch --list-envs
# 2. Stream remote logs from UAT to your local terminal
ldm cloud-fetch [project] uat liferay --logs
# 3. Pull the latest Cloud backups (DB/Data) into your local project snapshots
ldm cloud-fetch [project] uat --download
# 4. Sync Cloud environment variables to your local project metadata
ldm cloud-fetch [project] uat --sync-env
reset and re-seed¶
Surgically clear project data folders or completely restore a project to its original vanilla state. These commands require the project to be stopped.
ldm reset [project] [target] # Clear specific data (state|db|global-search|all)
ldm re-seed [project] # Wipe ALL data and re-apply vanilla seed
Available Targets (for reset):
state(Default): Clears theosgi/statefolder and its named volume.data: Clears the project's data folder and its named volume.logs: Clears the project's log directory.db: Removes the project's database volume (<project>-db-db-data).global-search: Deletes this project's indices from the shared Global Search node. Scoped by the project's own index prefix, so no other project's indices are touched.all: Clearsstate,data,logs,dbandglobal-search, and tears the stack down withdocker compose down -vso anonymous volumes go too.search: Refused, not implemented. A sidecar Elasticsearch runs inside the Liferay container and only exists while that container runs, butresetstops the stack before it wipes anything โ so there is no endpoint left to delete through. Its index files live in the project's own data volume, soldm reset <project> dataremoves them. See LDM-#2127.
A comma-separated list is accepted: ldm reset demo state,db. Duplicates
collapse and the order you give is kept.
An unrecognised target is refused with exit 1, naming the valid ones. Before LDM-#2127 any string was accepted and reported success having done nothing.
Examples:
ldm reset demo state # Clear OSGi state for 'demo'
ldm reset demo db # Remove the database volume only
ldm reset demo all # Total project data wipe, database included
ldm re-seed demo # Total project reset to Day Zero (Seeded)
allincludes the database. Before LDM-#2118 it did not: the teardown omitted-vand the volume sweep looked for<project>-dataand<project>-state, never<project>-db-db-data. The command reported success, the project showed asNot Created, and the next start quietly booted the old database.
Last Updated: 2026-10-12 | Last Reviewed: 2026-10-12