LDM Frequently Asked Questions & Walkthroughs¶
This document collects common technical questions, best practices, and walkthroughs for Liferay Docker Manager (LDM) development and demonstration.
1. CLI Commands & General Diagnostics¶
Q: I hit an attribute error when running ldm fix-hosts ('LiferayManager' object has no attribute 'manager'). How do I resolve it?¶
This was a bug in version 2.11.68 (and earlier) where the BaseHandler.cmd_fix_hosts mixin method tried to call self.manager.cmd_doctor(...) instead of self.cmd_doctor(...). Since BaseHandler is inherited directly by LiferayManager, the self context is already the manager instance, and self.manager is None (reserved for type stub analysis).
Status: Fixed in master (PR #236). Calling ldm fix-hosts delegates directly and safely to self.cmd_doctor(fix_hosts=True).
2. Quick Environment Swapping & Resource Optimization¶
Q: What is the recommended workflow for switching between multiple demo environments quickly — should each demo be a separate ldm init project, or is there a faster way to swap DXP versions on the same project?¶
Recommendation: Initialize separate LDM projects for each demo environment.
Why?¶
Swapping DXP versions (e.g., DXP 7.4 vs. DXP 2026) on the same database will trigger schema drift or database locking errors on startup because Liferay applies irreversible database schema changes dynamically during boot.
Walkthrough: Rapid Swapping Workflow¶
- Initialize separate environments once:
- Switch between them cleanly:
- Shared Database Mode: If you run many demos concurrently, configure LDM to run in Shared Database mode to place projects on a global database container (
liferay-db-globalfor PostgreSQL,liferay-db-mysql-globalfor MySQL/MariaDB), resolving host memory issues.
Q: How can I run multiple LDM demo environments side-by-side without exhausting my local machine's CPU, RAM, and Disk space?¶
Running multiple Liferay DXP instances locally can quickly overwhelm standard laptop resources. LDM includes several built-in features specifically designed to minimize resource consumption during parallel multi-demo execution:
1. Shared Database Mode (use_shared_db)¶
By default, each LDM project spins up its own isolated database container (PostgreSQL, MySQL or Hypersonic). In Shared Database Mode, LDM skips launching project-specific databases and routes all projects to a global database container, namespaced as lportal_<project_id> (always lowercase):
Note
There is one global container per engine -- liferay-db-global for PostgreSQL and liferay-db-mysql-global for MySQL/MariaDB -- each provisioned lazily on the first project that needs it. An all-PostgreSQL or all-MySQL fleet therefore gets the full saving below; a mixed fleet runs both globals, which erodes it (though two containers still beat one per project). Hypersonic cannot be shared -- it runs inside the Liferay JVM, so its mode is always embedded, and --db hypersonic --database-mode shared is refused outright (LDM-#1511).
- Resource Saved: Saves ~500MB to 1GB of RAM per running project by eliminating redundant database container overhead. Measured on one machine with
docker stats --no-stream, the database container itself accounts for 108-127 MiB of that per project; the rest is the overhead it brings with it. For scale, the same project's Liferay heap is-Xmx3072m-- roughly 25x the database -- so this is a real saving but a modest one, which is why LDM offers shared mode rather than defaulting to it. - Usage: Configure it globally or toggle it for specific projects:
- Discoverability: LDM mentions this once, after your first project is created, naming the engine shared mode would use (LDM-#1510). It is deliberately not repeated: the completion banner is already dense, and a line added to it on every run gets skipped. The tip is also suppressed entirely once
database_modeis set in~/.ldmrcor/etc/ldmrc-- to anything,isolatedincluded -- because at that point you have decided.
2. Local Database Connection Pool Throttling¶
LDM automatically configures Liferay's database pool settings to be lean by default:
- Limits: Constrains pools to
jdbc.default.maxActive=15,jdbc.default.minIdle=2, andjdbc.default.maxIdle=5(compared to Liferay's default of 100+ connections). - Resource Saved: Prevents the database container from allocating hundreds of active connections/threads, drastically reducing CPU context-switching and RAM overhead.
3. Elasticsearch Scheduling and Memory Limits¶
The global search container (liferay-search-global) is configured with strict footprint gates:
- Heap limits: Restricts the Elasticsearch Java heap space to 512MB by default (
-Xms512m -Xmx512m). - CPU scheduling: Forces
-e "processors=1"configuration. - Resource Saved: Prevents Elasticsearch from aggressively consuming multi-gigabyte RAM allocations and pinning multiple CPU cores during reindexing.
4. Smart Cache Hydration (Fast volume restore)¶
When hydrating or resetting a project, LDM uses hash-based change detection (saved in .ldm_volume.sha256) to check if the volume.tgz archive has changed:
- Resource Saved: Bypasses redundant write extractions of large documents and media assets. This avoids high Disk I/O bottlenecks and high CPU compression loads, spinning up stacks in seconds.
5. Docker Log Driver Rotations¶
Every scaffolded container is configured with size and rotation limits:
- Limits: Restricts logs to
max-size: 10mandmax-file: 3rotations. - Resource Saved: Prevents Docker logging directories from silently expanding and consuming hundreds of gigabytes of disk space over time.
3. Pre-Baking Custom Data & Workspace Seeding¶
Q: Is there a way to pre-bake custom data (like specific Fragments, Objects, or Client Extensions) into a seed pack, so demos start with my customizations instead of generic samples?¶
Yes. Depending on your needs, you can distribute this state using two workflows:
Option A: Portable Workspace Packages (.ldmp)¶
You can export any customized environment (database dump + document library assets + configurations) into a single portable package.
- Spin up a clean sandbox project:
-
Log in and configure your fragments, objects, pages, and client extensions.
-
Capture a snapshot:
- Package the workspace:
This produces custom-demo-template.ldmp. Your colleagues can import and spin up this exact custom environment instantly by running:
Option B: Mounting Custom Assets (Auto-Deploy)¶
To seed static code/modules without capturing database dumps, use the LDM staging folder:
- Place backend OSGi bundles (
.jarfiles) in[project]/files/osgi/modules/ - Place configurations (
.configfiles) in[project]/files/osgi/configs/ - Place Client Extension ZIP files in
[project]/client-extensions/
LDM automatically mounts and hot-deploys these assets during stack start (ldm run).
4. Pre-Warmed Seed Packs Lifecycle¶
Q: How often are the pre-warmed seed packs updated after a new LTS release drops?¶
- Weekly Automation: The GitHub Actions workflow
generate-seeded-states.ymlautomatically rebuilds and uploads seed archives every Sunday to theseeded-statesrelease tag. - Target Support: The workflow queries upstream releases to cover the latest 4 Quarter releases and 2 active LTS lines.
- Manual Rebuilds: Maintainers can trigger the workflow manually (
workflow_dispatchon GitHub Actions) if a new LTS patch is released mid-week to make it immediately available for offline seeding.
5. Client Extension and Theme Packaging¶
Q: Is there native support planned for client extensions or custom themes in the snapshot/package workflow (ldm package)?¶
It is fully supported natively.
- Metadata manifests automatically record the resources included inside the snapshot (e.g.
includes_client_extensions,includes_osgi_modules). - Source & Build Packaging: When running
ldm package, LDM packs both the database/DL snapshot and the source workspace (including custom theme folders and client extensions). - Hydration auto-builds: Upon
ldm import, LDM restores the database, moves client extension zip files toclient-extensions/, and builds/syncs code components so the theme and extensions load seamlessly on startup. - Under-reporting manifests are corrected, not rejected: a package whose manifest claims client extensions or OSGi modules but lists none is scanned on import and the listing is rebuilt from the package's own contents, with a warning. The import is only refused when the package ships nothing for the category it claims. Packages built before LDM-#1568 can carry this contradiction; the published AI Commerce Accelerator package is one.
6. Remote Collaboration & Sharing (Tunnels)¶
Q: How can I share my local running DXP demo instance with a remote client or colleague? What is the difference between lfr-tunnel, ngrok, and other tunnel options?¶
To facilitate collaboration, LDM includes a dedicated ldm share command which generates public-facing secure HTTPS URLs pointing to your local running instance.
Exposing your project¶
Run the sharing command:
This will output a public HTTPS URL (e.g., https://my-awesome-demo.lfr.direct) that anyone on the internet can use to view your demo.
Supported Providers in LDM¶
-
lfr-tunnel(Native Liferay Tunnel - Recommended): -
How it works: Runs the custom Liferay
lfr-tunnelCLI binary directly on your host machine. - Security: Resolves the tunnel token securely using the local token config (
LFT_CLIENT_TOKENor system keystore). -
Resolution: LDM resolves an existing client -- it does not install one -- checking
LDM_LFR_TUNNEL_BIN, thenlfr_tunnel_binin~/.ldmrc, thenPATH, then~/liferay/lfr-tunnel/(the location endpoint protection allows), then the legacy~/.ldm/bin/. If none is found it explains how to install one, or you can use the containerisedlfr-tunnel-dockerprovider and skip the host binary entirely. -
lfr-tunnel-docker(Docker Sidecar): -
How it works: Launches the tunnel client inside an isolated Docker sidecar container bundled in your project network stack.
-
Benefit: No binary installation is required on your host system, making it completely portable.
-
ngrok: -
How it works: Launches an
ngroktunnel mapping your local port. - Benefit: Highly standard and stable tunnel provider; useful if you already have a paid ngrok subscription/token configured on your machine.
What about Cloudflare?¶
While Cloudflare (cloudflared) is not currently a built-in choice in the LDM CLI --provider argument, you can easily use it alongside LDM. Simply launch the cloudflared daemon manually on your terminal:
This maps your local Liferay HTTP port (8080) to a free, secure Cloudflare tunnel URL.
7. Version Upgrades, Rollbacks, and Database Lifecycle¶
Q: What is the difference between an LDM snapshot restore after a failed upgrade attempt versus a standard database/tag downgrade?¶
They are completely different operations with different safety guardrails:
- Standard Downgrade: Attempting to boot an existing database containing schema updates from a newer Liferay version (e.g.
2026.q1.9) on an older Liferay version (e.g.2024.q4.1). Since Liferay databases do not support backward schema migrations, this is blocked by LDM (Downgrade detected...) to prevent database corruption. - Restore after Failed Upgrade: Returning to a working state by replacing the corrupted/upgraded database entirely with a clean snapshot taken prior to the upgrade. During
ldm restore, LDM wipes the database container/volume, restores the Version A SQL dump, updates the Liferay version metadata back to Version A, and regeneratesdocker-compose.ymlto boot with the previous Liferay version tag. This is fully supported and safe.
Q: How can we tell if a Liferay version database upgrade has completed successfully? When is the "point of no return" for rollbacks?¶
- Determining Success: Database upgrades run asynchronously inside the Liferay container after boot. LDM is an orchestrator and cannot instantly verify success when starting the stack. You must monitor the logs (
ldm logs -f) to verify that the upgrade tool completed without critical SQL schema errors, and check that the portal container state transitions to Healthy (Liferay is ready). - Point of No Return: A rollback using the pre-upgrade snapshot is safe only immediately after boot during developer validation. Once the environment is released to general users and new production data (content, users, documents) is written to the upgraded database, rolling back to the snapshot will wipe all business data created since the upgrade started. At this stage, rollbacks are no longer recommended without manual database delta migrations.
Q: Why are upgrade prompts (such as "Upgrade detected...") sometimes skipped when upgrading a Liferay version?¶
CreatedProject Status (First Run): If a project was newly initialized or imported but has never run successfully on the machine, its status isCreatedand itslast_run_liferay_versionproperty is empty. LDM assumes this is a fresh start, not an upgrade, and skips the database backup/auto-upgrade prompts because there is no existing database to back up or upgrade.- Direct Database Imports: If you import a database dump from an older Liferay version directly into a newly created project, LDM cannot inspect the SQL tables to know what schema version is inside. To run Liferay's auto-upgrade tool on boot for such imports, you must pass the
--upgrade-dbflag explicitly:
Last Updated: 2026-09-03 | Last Reviewed: 2026-10-21