Skip to content

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.

ldm init my-project --db postgresql --database-mode external

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:

  1. JDBC URL (e.g. jdbc:postgresql://db.internal.network:5432/lportal)
  2. Database Username
  3. 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's portal-ext.properties. Where the engine is known, jdbc.default.driverClassName and hibernate.dialect are written too.
  • It completely excludes the db service block from the generated docker-compose.yml, and no depends_on refers 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:

"jdbc_pass": "<removed by ldm -- re-enter this value after restoring>"

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 using docker ps | grep -q "aica-db".
  • Headless CI Failure: In GitHub Actions (release.yml), the database container aica-db is not running. The packaging hook falls back to generating a blank database.sql and an empty files.tar.gz. The resulting .ldmp package uploaded to the GitHub Release is empty.
  • The Solution:

  • Build the package locally on your host machine where your active database container is running:

    ldm package
    
  • Manually upload the populated .ldmp package and its .ldmp.sha256 checksum 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 --db is 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 the osgi/state folder 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: Clears state, data, logs, db and global-search, and tears the stack down with docker compose down -v so anonymous volumes go too.
  • search: Refused, not implemented. A sidecar Elasticsearch runs inside the Liferay container and only exists while that container runs, but reset stops 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, so ldm reset <project> data removes 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)

all includes the database. Before LDM-#2118 it did not: the teardown omitted -v and the volume sweep looked for <project>-data and <project>-state, never <project>-db-db-data. The command reported success, the project showed as Not Created, and the next start quietly booted the old database.


Last Updated: 2026-10-12 | Last Reviewed: 2026-10-12