Advanced CLI Usage & Flags¶
This document covers advanced flags and commands intended for CI/CD automation, complex edge-cases, debugging, and extreme performance tuning. For standard workflow commands, see the CLI Reference.
Filesystem & Volumes (macOS / Windows)¶
These flags control how LDM mounts volumes to bypass filesystem locking limitations on non-Linux hosts.
--internal-state: Forces the use of an internal Docker volume for the OSGi state folder (osgi/state). LDM enables this automatically if it detects the project is on an external drive (/Volumes/). Use this to manually bypassaccess_denied_exceptionlock errors on slow or external filesystems.--no-vol-cache: Disables the:cachedmount delegation flag on macOS and Windows. Use this if you are experiencing severe file synchronization delays between the host and the container.
Initialization & Seeding State¶
These flags modify how LDM handles the initial startup of a Liferay environment.
--persist-osgi: Maps the container'sosgi/statedirectory to the host instead of an anonymous Docker volume, allowing bundle state to persist across container restarts. This dramatically reduces subsequent Liferay startup times by bypassing the OSGi bundle resolution phase. Note: LDM will automatically invalidate and wipe this state if it detects the underlying Liferay image tag has changed to prevent bundle conflicts.--no-persist-osgi: Explicitly disables OSGi state persistence, forcing a clean OSGi resolution on every start.--no-seed: Completely bypasses the pre-warmed database and OSGi cache. The project will start totally fresh, forcing Liferay to build its schema and resolve all OSGi bundles from scratch.--vanilla: Starts a Liferay with nothing pre-populated. It implies
--no-seed, and additionally refuses to run alongside--samplesor--snapshot(which would restore content), and wipes any host-persisted OSGi state from--persist-osgiso no bundles arrive pre-resolved. Use--no-seedinstead if you only want to skip the pre-warmed seed while still restoring a snapshot — that combination stays legal.-n,--nightly: Targets the latest Liferay DXP nightly build (
7.4.13.nightly) from Docker Hub.--master: Alias for
--nightly. Targets the latest Liferay DXP master/nightly build.--pull: Forces Docker to pull the latest image layers before running or starting containers.
--no-osgi-seed: Bypasses only the OSGi cache seed. Useful if you are testing custom OSGi resolution logic or diagnosing a corrupted.osgi_state_archive.--verify/--no-verify: Controls whether LDM generates or checks the integrity checksum of snapshots and imports. Defaults to true. Disabling can speed up local imports slightly.--snapshot: Initialize a project directly from an external snapshot folder.--portal: Forces the use of the Liferay Portal (CE) image instead of the default DXP image.--refresh: Forces Docker to pull the latest image layers and refresh cached assets before startup.
Execution Flow & Networking¶
These flags alter the standard startup behavior and networking defaults.
--expose: Injects anngroksidecar container into your stack to expose your local Liferay instance to the public internet securely over HTTPS. It requires a free ngrok Auth Token, which LDM will prompt for once and save globally. Perfect for testing webhooks, SaaS integrations, or sharing your local dev environment.--no-up: Scaffolds the project folder and generates configurations, but skips starting the Docker containers. (Similar toldm init).--no-wait: Skips the readiness gating (health checks) after container startup, returning control to the terminal immediately.--timeout <seconds>: Overrides the maximum wait time for health checks (default is 900 seconds).-f,--follow: Automatically follows the container logs immediately after a successful startup.--force-ssl: Forces SSL termination via Traefik even if the host islocalhost.
CI/CD & Pipeline Automation¶
These flags are ideal for automated testing pipelines where interactivity is impossible.
--no-captcha: Disables Liferay's mandatory Omni-Admin CAPTCHA requirement. Strictly opt-in and easily reversible; running without this flag on a subsequent start will re-enable CAPTCHA.--fast-login: Automatically bypasses typical post-startup prompts (Terms of Use acceptance, initial password reset). Best used with an external database (--db mysqlorpostgresql), as password policy bypass has known limitations with the embedded Hypersonic database.--target <node>/--node <node>: Target a specific registered compute node (e.g.--target node-a). Overrides the default compute target for the execution context.--target-env: (Used withlink,clone, andimport). Overrides the environment name metadata.--build: (Used withlinkandclone). Forces a full rebuild of any Server-Side Client Extensions found in the source workspace during initialization.--on-validation-failure: Behaviour when a config file (e.g.fragment-overrides.json) fails schema validation in non-interactive mode. Choices:die(default) orignore.
JVM & Tomcat Tuning¶
Advanced options for memory constraints and Java-level debugging.
How LDM sizes the JVM by default, and how that compares with Liferay's published guidance, is recorded in the JVM & Database Tuning reference.
--lean: Enables a resource-optimized JVM profile. It caps memory and limits background threading. Highly recommended for laptops with less than 16GB of RAM or CI runners.
Per-setting overrides¶
Each of these overrides one value and leaves the rest adaptive — changing
the heap does not discard metaspace sizing, the platform compiler decision or
the reindex scale-up. Sizes accept 2048, 512m or 8g; an unreadable value
is ignored with a warning rather than failing the container at start.
--jvm-heap-min: Initial heap size.--jvm-heap-max: Maximum heap size.--jvm-metaspace: Metaspace size, initial and maximum.--jvm-new-size: Young generation size.--jvm-tiered-stop-at-level(true/false): Force the tiered-compiler stop level on or off. LDM enables it on macOS/Windows for faster bundle resolution; note it also drops the JVM code cache from 240 MB to 48 MB.
The exact JVM flags each one produces are listed in the tuning reference.
Each has a config key of the same name (jvm_heap_max, …) usable through
ldm config and project metadata. Precedence, profiles and the measured
reasoning are in the JVM & Database Tuning reference.
--jvm-args="<args>": Pass raw JVM arguments directly to Liferay. Example:--jvm-args="-Xmx8g -Xms8g"
This replaces LDM's defaults entirely. It is not additive: the adaptive heap and metaspace sizing, the platform-specific compiler settings and the reindex scale-up are all discarded, and only what you pass is used. To change one value while keeping the rest, see LDM-#1446.
Accepted but inert¶
These two flags are accepted for compatibility and have no effect. Passing either prints a warning.
--no-jvm-verify--no-tld-skip
Earlier versions of this page described them as disabling defaults that LDM
applied. Those defaults never existed: there is no -Xverify:none anywhere in
the codebase, and no TLD configuration at all. Both values were read from the
command line, written to project metadata, and never used (LDM-#1447).
-Xverify:none is also not something to reinstate — it has been deprecated
since JDK 13, and these images run Java 21, where it emits a warning and does
nothing. A genuine TLD scanning skip is still worth having and is tracked in
LDM-#1446.
System Tray GUI¶
The ldm tray command launches a native system tray application (menu bar icon) to quickly manage projects, view status, and open the dashboard. The tray is syntactic sugar and is entirely optional. If the UI dependencies fail to load, LDM will gracefully fallback to opening the Web Dashboard.
Dependencies:
- macOS / Windows: Dependencies (
pystray,Pillow) are bundled in the standalone binaries. - Linux: Native tray is not officially supported due to Wayland/AppIndicator fragmentation. If you wish to use it, you must manually install AppIndicator libraries (e.g.,
sudo dnf install libappindicator-gtk3on Fedora/RHEL, orsudo apt install libappindicator3-1on Ubuntu/Debian).
Autostart / Launch on Login Configuration¶
LDM includes built-in commands to automatically configure native system autostart on boot or user login across macOS, Windows, and Linux:
# Enable launch-on-login for System Tray
ldm tray --autostart
# Disable launch-on-login for System Tray
ldm tray --uninstall-autostart
- macOS: Provisions a macOS App Bundle (
~/Applications/Liferay Docker Manager.app) and a user LaunchAgent (~/Library/LaunchAgents/com.liferay.ldm.plist) configured to run automatically in the background at login. - Windows: Creates a startup script in
AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\Liferay Docker Manager.bat. - Linux: Creates a FreeDesktop Autostart entry at
~/.config/autostart/ldm.desktop.
Running in Background (Detached Terminal)¶
To launch the System Tray manually in the background without keeping a terminal window open:
Debugging & Diagnostics¶
--gogo-port <port>: Exposes the OSGi Gogo shell on a specific host port. Required if you plan to useldm gogo [project].--mount-logs: By default, logs remain inside the container. This flag bind-mounts thetomcat/logsdirectory directly to the host for external log aggregator testing.--delay <seconds>: (Used withmonitorandlink). Alters the debounce delay for the background file watcher. Useful on slow filesystems.
Search & Legacy Infrastructure¶
--sidecar: Forces the project to use Liferay's internal Sidecar search process rather than the shared Global Search container. LDM does this automatically if the global container is offline. (Note: Sidecar uses Elasticsearch 7 and is deprecated in Liferay 2025.Q2+. LDM will automatically ignore this flag and force Shared Search for newer releases).
--es7: Forces the Global Search infrastructure to use Elasticsearch 7 (legacy) instead of the default Elasticsearch 8. Use with
ldm infra-setup --es7. (Note: Elasticsearch 7 is deprecated in Liferay 2025.Q2+; future releases require Elasticsearch 8).
--search-mode¶
Selects where Liferay's search index lives. Available modes: sidecar, shared or remote.
| Mode | Where Elasticsearch runs | How LDM configures it |
|---|---|---|
sidecar |
Inside the Liferay container as a child process -- there is no separate container | Liferay's own default; LDM does not need to configure anything |
shared |
The global liferay-search-global container, shared across projects |
LDM writes com.liferay.portal.search.elasticsearch<N>.configuration.ElasticsearchConfiguration.config into the project's osgi/configs/ |
remote |
Your own external cluster | You provide the .config in osgi/configs/; LDM leaves it alone |
In shared mode each project gets its own index namespace via indexNamePrefix:
Liferay appends the company ID, so the actual indices look like
ldm-myproject-20101-workflow-metrics-tasks. The prefix is written lowercase because Liferay lowercases it regardless (StringUtil.toLowerCase), and a mixed-case value would not match the indices it creates.
Note
Search settings are OSGi configuration, not portal properties, so they cannot be set with LIFERAY_* environment variables. This is why the .config file is the mechanism.
--database-mode¶
Controls who owns the database container. Available modes: isolated, shared, external and embedded.
| mode | who runs the database |
|---|---|
isolated |
LDM, one container per project (the default) |
shared |
LDM, one global container per engine (#1361) |
external |
someone else -- LDM writes the JDBC details and creates nothing |
embedded |
nobody: it runs inside the Liferay JVM (Hypersonic) |
--db names the engine -- the dialect, driver and JDBC scheme -- and is a separate question. The two axes were muddled before LDM-#1511: external sat on the engine axis, so choosing it discarded the engine and --db postgresql --database-mode external could not be expressed at all.
Not every pairing is possible, and the impossible ones are refused. LDM exits 1 at parse time with a message naming both values, rather than accepting the pair and behaving unexpectedly later:
| engine | isolated | shared | external | embedded |
|---|---|---|---|---|
postgresql |
yes | yes | yes | no |
mysql / mariadb |
yes | yes | yes | no |
hypersonic |
no | no | no | yes |
hypersonic runs in-process, so embedded is the only mode it has; LDM applies it automatically and you never need to type it. --db hypersonic --database-mode shared is refused (it was previously accepted and downgraded with a warning), as is --db external --database-mode shared (previously accepted and meaningless -- both flags suppressed the same container).
Note
--db external is still accepted. It is the pre-#1511 spelling of --database-mode external, and LDM recovers the engine from the JDBC URL scheme you supply. Existing projects whose meta records db_type: "external" are migrated on their next run; where the scheme names an engine LDM does not support, the legacy read path (URL, username and password only) is kept so the project still boots.
shared supports PostgreSQL and MySQL/MariaDB. There is one global container per engine, and each is provisioned lazily -- on the first project that needs it:
--db |
Global container | Host port | Image |
|---|---|---|---|
postgresql (default) |
liferay-db-global |
5433 |
postgres:<resolved> |
mysql, mariadb |
liferay-db-mysql-global |
3307 |
mysql:<resolved> |
hypersonic cannot be shared -- it runs in-process, so its mode is always embedded. external is a mode of its own: LDM uses the JDBC URL you supplied and never consults a global container.
Which engine does shared mode use? The project's own --db, when it names one. When nothing does -- ldm db start outside a project, or a global container provisioned before any project has picked -- LDM resolves it from the db_type default, which is postgresql by convention (LDM-#1510):
ldm config defaults db_type mysql # shared mode now provisions liferay-db-mysql-global
ldm config defaults --remove db_type # back to PostgreSQL
This was previously implicit: the fallback was hardcoded to PostgreSQL, so turning shared mode on picked an engine silently and there was no way to say which. There is deliberately no separate shared_db_engine setting -- one question, one source, so two settings cannot disagree about which engine you meant.
An engine that has no global container -- hypersonic, or the legacy external marker -- is not a valid answer here and falls back to PostgreSQL rather than naming a container that can never exist.
Note
--db mysql and --db mariadb share one container. This is not a shortcut: LDM emits an identical jdbc:mariadb:// URL and MariaDB103Dialect for both engines, so Liferay cannot distinguish them from the connection down. A single container also avoids a third idle global on a mixed fleet.
A mixed fleet runs both globals. Shared mode exists to save roughly 500MB-1GB per project, and an all-PostgreSQL or all-MySQL fleet gets that saving in full. Mixing engines means both globals run and the saving erodes -- though two shared containers still beat one container per project, which is the comparison that matters.
Before v2.19.0, shared was PostgreSQL only and --database-mode shared --db mysql exited 1. That refusal was deliberate: the only global container was PostgreSQL while the MariaDB URL aimed at port 3306 of it, so the combination could never connect.
In shared mode every project gets its own database on the one cluster for its engine, named from the project:
The derived name is always lowercase. MyProject becomes lportal_myproject; Saarbrücken becomes lportal_saarbruecken.
This matters when the shared cluster is external and you provision the database yourself: create it with the lowercase name above, because that is the only name LDM will look for. Lowercasing also matches what PostgreSQL itself does with an unquoted CREATE DATABASE, so a hand-provisioned database and an LDM-provisioned one end up with the same name.
The same contract carries the name on MySQL, where database names are case-sensitive on Linux (lower_case_table_names=0). LDM's own global MySQL container runs with --lower_case_table_names=1, which folds names as PostgreSQL does; because LDM always derives lowercase, the name is predictable either way and on an external server too.
In isolated mode the database is always called lportal and the project name is not used.
Database Commands¶
ldm db query [project]: Safe, SELECT-only SQL execution against project databases. By default, this resolves credentials automatically and prompts for query confirmation.-s,--sql "<query>": Inline SQL statement to execute. If not provided, LDM will read from stdin.-f,--format {table,csv,json}: Output format (default:table).-
--allow-db-query: Explicitly bypasses the interactive confirmation prompt. -
ldm db start: Starts the shared global databases (e.g. Postgres, MySQL) defined in the infrastructure mode.
-
ldm db stop: Stops the shared global databases defined in the infrastructure mode.
ldm db reset-admin [project]: Forcefully resets the password for the default admin account (test@liferay.com) totestby injecting the known PBKDF2 hash directly into the database. Also activates the account and resets failed login locks.
Custom Containers
¶
ldm config add-container [project]: Interactively inspects and provisions an arbitrary Docker image to run alongside the Liferay stack.--image <image_name>: Required. The fully qualified Docker image name to add (e.g.,wordpress:latest).--service-name <name>: Optional. Overrides the extracted service name (e.g.,my-wordpress).
Compute & Target Node Power Management
¶
ldm node power wake <node>: Powers on or wakes a target compute node.--ttl <duration>: Specifies the wake duration before scheduled off-hours power enforcement (e.g.,--ttl 2hor--ttl 4h). Default:2h.ldm node power sleep <node>: Immediately shuts down a target compute node.ldm node power status: Displays active compute node power states and wake TTLs.ldm node power enforce: Enforces scheduled off-hours shutdowns.ldm node power sync-dns: Re-reads each node's public IPv4 address from AWS EC2 and updates~/.ldmrc, the node-power config and the Docker context wherever it changed.
These commands keep their node definitions and wake state in ~/.ldm/node-power-config.json and ~/.ldm/node-power-state.json, falling back to the targets in ~/.ldmrc when there is no config file. They run inside ldm itself (LDM-#2291); before v2.26.7 they failed in every packaged build because the utility they called was not shipped. In the repository, scripts/manage_target_nodes.py runs the same code against the repo-root .node-power-config.json, which is what the node-power-manager workflow uses.
Last Updated: 2026-10-23 | Last Reviewed: 2026-10-23