CLI System Commands¶
system doctor (legacy: doctor)¶
Verify host environment health, Docker resources (CPUs/Memory), disk space (warns on dangling volumes), and project dependencies. Includes checks for required tools: mkcert, telnet, nc, lcp, lfr-tunnel, and the Docker Compose V2 plugin.
The lfr-tunnel row reports the client's version and the date it was last
installed or self-upgraded, for whichever binary ldm share would use --
LDM_LFR_TUNNEL_BIN, the lfr_tunnel_bin setting, or PATH. The date comes
from the file itself, because lfr-tunnel -version reports no build date.
The check is offline and does not compare against the gateway's minimum
version; a client below that floor is explained when ldm share runs. A
missing client is a warning, not a failure -- sharing is optional, and the
containerised lfr-tunnel-docker provider needs no host binary.
For every registered remote target, doctor also compares the SSH user in
~/.ldmrc against the user that node's Docker context actually dials, and
reports a DRIFTED row when they disagree (LDM-#1797). The two are stored
separately and nothing keeps them in step, so the failure shows up as
Permission denied (publickey) naming a user ldm target ls never mentions.
The repair it names is ldm target add <name> --user <user>, which keeps the
node's other settings.
ldm system doctor # Health check for current/selected project
ldm system doctor --all # Batch validate every project in your workspace
ldm system doctor --detailed # Show detailed troubleshooting hints and automatic fixes
ldm system doctor --fix # Automatically apply recommended fixes
ldm system doctor --bundle # Generate a sanitized zip bundle of logs and config
ldm system doctor --slug # Output a machine-readable environment identifier string
ldm system doctor --fix-hosts # Add missing domains to /etc/hosts (will prompt for sudo)
ldm system doctor --ssl # Run SSL diagnostic workflow to verify certificate chains and routing
ldm system doctor --ssl --domain aica.local # Check a specific domain
# Legacy flat form (still works):
ldm doctor --fix
system fix-hosts (legacy: fix-hosts)¶
Manually append missing project hostnames to your system's /etc/hosts file. This command is automatically triggered by ldm run if a resolution failure is detected, but can also be called surgically.
# Fix all hostnames for a project (including extension subdomains)
ldm system fix-hosts my-project
# Add a specific raw hostname
ldm system fix-hosts custom.local
# Run a full fix for all projects via doctor
ldm system doctor --fix-hosts
# Legacy flat form (still works):
ldm fix-hosts my-project
system seeds¶
List available pre-warmed database seeds from GitHub.
Note
Seed Retention Policy: LDM only maintains one pre-warmed seed per quarterly release (the absolute latest patch). When a new patch is released, the previous seed is deleted to conserve storage and reduce confusion.
wait (Readiness Gating)¶
Blocks execution until a Liferay instance is genuinely ready for work. This is highly recommended for CI/CD pipelines and complex deployment scripts. Unlike basic Docker healthchecks, ldm wait performs a 3-Phase Verification:
- Log Readiness: Scans Docker logs for the Tomcat
"Server startup"marker. - HTTP Availability: Polls the instance until it responds with an
HTTP 200or302on its primary port. - CPU Idle State: Actively monitors the container's CPU usage, blocking until it drops below the threshold (default: 15.0%) for consecutive checks (default: 3). This is configurable via
--cpu-idle-thresholdand--cpu-idle-checksoptions.
# Wait for the current project to be fully idle (up to 10 minutes)
ldm wait
# Wait for a specific project with a custom timeout
ldm wait my-project --timeout 300
status (alias: ps)¶
Lightweight summary of all active global services and running projects.
ldm status # Show active global services and running projects
ldm status --all # Show all managed projects (including stopped ones)
ldm ps
# Machine-readable output for scripts/automation, instead of the color-coded
# tables -- a stable JSON object: {"infrastructure": [...], "projects": [...]}.
# Combines with --detailed (per-container service/status/ports/image) and -a/--all.
ldm status --json
info¶
Displays a user-friendly, formatted view of a project's internal metadata (.liferay-docker.meta). This is incredibly useful for diagnosing configuration issues or verifying project state without opening the file manually.
ldm info [project]
ldm info [project] --credentials # Print the default admin credentials block
ldm info [project] --credentials --credential-type database # Print the database credentials
ldm info [project] --credentials --password-only # Print ONLY the raw password string (useful for CI scripts: `export PASS=$(ldm info --credentials --password-only)`)
The output includes an ID: row -- the project's internal UUID (LDM-#1393).
LDM identifies projects by this, not by their name: the name is yours to choose
and can legitimately be shared by two projects in different directories, so it
cannot be a reliable identity.
You will not see it anywhere else. ldm list and every routine command work in
project names, because that is how you think about them. It appears here because
this is the diagnostic view, and it is the value stamped on every container and
volume as com.liferay.ldm.project.uuid -- so it is what to match when working
out which Docker resources belong to which project:
Projects created before this existed have no ID, and the row is simply omitted. One is assigned the next time their metadata is written.
Note
Security Posture regarding ldm info --credentials:
This feature intentionally outputs clear-text credentials to stdout. Because LDM is a local development and CI tool, the caller already has read access to the local .liferay-docker.meta file on disk. Providing a native extraction method encourages developers to dynamically source passwords in automated integration scripts, rather than hardcoding sensitive data into version control (which is significantly riskier).
browser (alias: open)¶
Launch the project URL in your system browser. If no project is specified, LDM will present a list of currently running projects to select from.
ldm browser [project]
ldm open [project]
ldm browser [project] -u /path # Open a specific path (e.g., /web/guest)
ldm browser --list # List available URLs without opening
ldm browser --remove # Remove saved custom URLs from history
system upgrade (legacy: upgrade)¶
Automatically download and install the latest version of LDM for your architecture. Includes integrity verification. If the automatic process fails, LDM will provide a manual curl or PowerShell command to complete the installation.
Tip
Post-Upgrade Release Notes: After a successful upgrade, LDM will automatically display a "What's New" release notes banner summarising the key changes in the new version. This is shown once on the next command run after the upgrade completes.
ldm system upgrade # Standard upgrade to latest stable
ldm system upgrade --pre-release # Upgrade to the latest pre-release/beta
ldm system upgrade --repair # Re-download current version to fix integrity issues
ldm system upgrade --check # Check for updates without installing
# Legacy flat form (still works):
ldm upgrade --beta
Note
Old builds are cleaned up: on macOS and Linux each LDM version unpacks itself into its own ~/.shiv/ldm_<build id>/ directory (about 80 MB). On that same first run after an upgrade, LDM now removes the previous versions' directories, leaving the running one and anything that is not LDM's untouched (LDM-#2296). Before this, nothing removed them. To reclaim the space on an older install, delete every ~/.shiv/ldm_* directory except the newest while no ldm command is running.
system completion (legacy: completion)¶
Configure shell autocompletion for ldm. Supports Bash, Zsh, Fish, and PowerShell.
ldm system completion # Auto-detect your shell
ldm system completion zsh # Generate setup for Zsh specifically
ldm system completion bash # Generate setup for Bash
ldm system completion fish # Generate setup for Fish
# Legacy flat form (still works):
ldm completion
Setup Summary:
- Run
ldm system completionto get the command for your shell. - Add the provided command to your shell profile (
.zshrc,.bashrc, orconfig.fish). - Restart your terminal.
This enables TAB completion for all commands, namespaces, subcommands, and project names.
system man (legacy: man)¶
Display the comprehensive manual page for LDM. This provides an offline reference for all commands, options, and architecture details.
Native Integration (man ldm)¶
To support the native system man ldm command, add this to your shell profile (.zshrc or .bashrc):
infra renew-ssl (legacy: renew-ssl)¶
Refresh project-specific SSL certificates immediately.
ldm infra renew-ssl # Interactive selector
ldm infra renew-ssl demo # Renew for 'demo' specifically
ldm infra renew-ssl --all # Renew certificates for every project
# Legacy flat form (still works):
ldm renew-ssl demo
infra init-common (legacy: init-common)¶
Initialize or recreate the baseline global configuration (common/ folder) from internal resources.
infra setup / infra down / infra restart (legacy: infra-setup, infra-down, infra-restart)¶
Independently manage global infrastructure services (Traefik proxy, Search sidecar, Bridge).
ldm infra setup # Start global services manually
ldm infra setup --search # Also initialize the Global Search container
ldm infra setup --es7 # Force Global Search to use legacy Elasticsearch 7
ldm infra setup --ssl-port 8443 # Custom SSL port for Traefik proxy (default: 443 or LDM_SSL_PORT)
ldm infra setup --force-recreate # Force recreate proxy containers to re-apply ports
ldm infra down # Stop and remove global services
ldm infra restart # Reset all global services in one go
ldm infra restart --search # Restart and also initialize/restart Global Search
ldm infra restart-proxy # Restarts only the Traefik proxy container
# Legacy flat forms (still work):
ldm infra-setup --search
ldm infra-down
ldm infra-restart
Tip
Sidecar Fallback: If the Global Search (ES8) container is not running, ldm will automatically default to Liferay's internal Sidecar search. It also cleans up global ES configurations in your project to ensure the Sidecar initializes correctly.
infra migrate-search (legacy: migrate-search)¶
Migrates a project from using the internal Sidecar search to the shared Global Search container.
What it does:
- Verifies the project is stopped.
- Ensures the Global Search container is running (offers to start it).
- Deletes internal indices (
data/elasticsearch7ordata/elasticsearch8). - Re-syncs Global ES configurations from
common/. - Offers to restart the project immediately.
system prune (legacy: prune)¶
Identify and reclaim disk space by safely removing orphaned resources. This command scans your Docker environment for containers and global search snapshots that no longer have a matching project folder on your disk, as well as cleaning up temporary files, large asset caches, and (with --images) unused Docker images and build cache. If ldm system doctor warns you about low disk space, run this with --all.
ldm system prune
ldm system prune --seeds --samples # Also clear large pre-warmed asset caches
ldm system prune --images # Also reclaim unused Docker images and build cache
ldm system prune --all # Run all pruning operations without asking (includes --images)
ldm system prune --clean-hosts # Remove all LDM-tagged entries from /etc/hosts
# Legacy flat form (still works):
ldm prune --seeds --samples
What it cleans:
- Orphaned Containers: Any container with the
com.liferay.ldm.managedlabel whose project folder was manually deleted. - Orphaned Search Snapshots: Leftover Elasticsearch 8.x snapshots in the global vault from deleted projects.
- Pre-warmed Seeds: (Optional) Large Database + Search + OSGi state archives used for instant project initialization.
- Sample Extensions: (Optional) Cached sample client extensions.
- Docker Images & Build Cache: (Optional,
--images) Unused/dangling Docker images and unused build cache -- usually the single biggest reclaimable consumer on a long-lived dev machine, viadocker image prune -afanddocker builder prune -af. - Temporary Files: Residual
.*.tmpfiles left behind by interrupted sync or build operations. - Legacy LDM Volumes: (Optional,
--legacy-volumes) Named volumes created before LDM labelled volumes with their owning project. Docker only labels a volume at creation, so these never acquire labels and are matched by name pattern instead. Because a name is weaker evidence of ownership than a label, every candidate is listed and grouped by project, volumes belonging to a registered project are excluded, and confirmation is always interactive ----alldoes not cover it.
clear-cache¶
Clears the local Docker Hub tag cache. LDM caches Liferay tags for 24 hours to improve performance; use this command to force a fresh fetch from the registry.
system relocate¶
Safely move your LDM global configuration, Docker volumes, and cached assets to an external drive (e.g., an external SSD). This is highly recommended for macOS users to save internal disk space and bypass filesystem locking issues.
config (get / set / remove)¶
View or set generic custom environment variables inside a project's metadata. The config command now has explicit subcommands for clarity, while the legacy positional form is still supported.
# New namespaced form:
ldm config get MY_VAR # Get a project-level variable
ldm config set MY_VAR "value" # Set a project-level variable
ldm config remove MY_VAR # Remove a variable
# Legacy positional form (still works):
ldm config MY_VAR "value" # Detected as 'set'
ldm config MY_VAR --remove # Detected as 'remove'
Credential-shaped values are hidden (--reveal)¶
Values whose key name looks like a credential -- anything containing
password, secret, token, credential or private, or a whole word of
key, pat, auth or pass -- are not printed.
ldm config # gemini_api_key = [REDACTED]
ldm config gemini_api_key # refuses, exits non-zero
ldm config gemini_api_key --reveal # prints the value
The listing masks them. Reading one by name refuses and exits non-zero
rather than printing nothing, so a script writing
TOKEN=$(ldm config gemini_api_key) fails at that line instead of silently
binding an empty string.
--reveal is the opt-in, matching ldm info --credentials --password-only.
The value goes to your terminal history and scrollback, and to anything
capturing the session, so prefer reading ~/.ldmrc directly when you only need
to confirm a value is present.
Key names are always shown; only values are hidden. The match errs toward
hiding -- tombstone_keep_credentials is a boolean and is masked anyway,
because showing a secret is worse than hiding a flag (LDM-#1970).
config defaults (legacy: defaults)¶
View or manage LDM's Cascading Configuration Defaults. This system resolves settings (like the default DB type, search mode, or host name) using a hierarchy: Convention -> Global -> User -> Project.
# View the resolved configuration tree and their sources
ldm config defaults
# Set a custom default just for your local user (~/.ldmrc)
ldm config defaults db_type mysql
# Remove a local user default to fall back to the convention
ldm config defaults --remove db_type
# Set a system-wide global default (requires permissions, writes to /etc/ldmrc)
sudo ldm config defaults port 9090 --global
# Return every customised default to convention ("get me back to stock")
ldm config defaults --reset-all
# ...without the confirmation prompt, for automation
ldm -y config defaults --reset-all
# ...at the global level instead (/etc/ldmrc)
sudo ldm config defaults --reset-all --global
# Legacy flat form (still works):
ldm defaults db_type mysql
config revert¶
Returns a named or current project to the configuration defaults. Where
ldm config defaults --reset-all affects projects you have not created yet,
this one affects a project that already exists.
# Revert the project in the current folder
ldm config revert
# ...or a named one
ldm config revert my-project
# Without the confirmation prompt, for automation
ldm -y config revert my-project
# Revert a state-bearing key as well (see below)
ldm config revert my-project --force-key host_name
# Revert everything, including every state-bearing key
ldm config revert my-project --force
What it touches¶
LDM-controlled configuration only — the settings LDM resolves for a project. A project's metadata also holds container names, its UUID, credentials and run history; none of that is configuration and none of it is touched. Nothing you have changed inside Liferay itself is touched either.
Values return to the resolved default (convention, then any global default, then any user default) — not raw convention. A deliberate global default is what a project with no opinion of its own is supposed to get.
What it refuses, and why¶
Five settings are not reverted by default, because the value is not the whole of the decision — each has already had an effect outside LDM, and changing the value back does not reverse it:
| Setting | Why |
|---|---|
host_name |
written into Liferay's virtualhost table; the running site would still answer on the old name |
db_type |
the data lives in that engine's volume, and another engine cannot read it |
database_mode |
the data lives in the container and volume that mode created |
search_mode |
the indices live where that mode put them, and reindexing is costly |
tag |
the database has been upgraded to that version, and Liferay does not support downgrades |
--force-key <key> reverts one of them; --force reverts all of them. Either
way LDM changes the setting only — it does not migrate data, move
containers, or rewrite virtualhosts. Untangling those consequences is yours.
Exit code 5 when nothing differs from the defaults: nothing failed, and
nothing needed to change.
Note
Changing a project's configuration does not change a running container.
Run ldm run <project> afterwards for the new values to take effect.
Resetting everything (--reset-all)¶
--remove clears one key. --reset-all clears them all, which previously
meant 23 separate invocations or hand-editing ~/.ldmrc.
It lists what will change before it changes anything, showing each current
value and the convention it returns to, and asks for confirmation (declined by
default). -y skips the prompt.
Two boundaries are worth knowing:
- Only the keys the defaults system owns are touched.
ldm config setwrites other keys into the same file; those are left alone. - It affects NEW projects only. A project that has already run froze its
settings into its own
meta, and clearing a default here does not reach it. Reverting an existing project is a separate concern -- not every key can be safely reverted once a project exists, because some have external consequences (ahost_namewritten into Liferay's virtualhost table, data living in a particular engine's volume).
Exit code 5 when nothing was customised: nothing failed, and nothing needed
to change.
Backward Compatibility Reference¶
All legacy flat-form commands are automatically translated to their namespaced equivalents by the preprocess_args layer. Both forms are valid and permanent:
| Legacy Command | New Canonical Form |
|---|---|
ldm init-from |
ldm link |
ldm prune |
ldm system prune |
ldm doctor |
ldm system doctor |
ldm upgrade |
ldm system upgrade |
ldm completion |
ldm system completion |
ldm man |
ldm system man |
ldm fix-hosts |
ldm system fix-hosts |
ldm dev-setup |
ldm system dev-setup |
ldm infra-setup |
ldm infra setup |
ldm infra-down |
ldm infra down |
ldm infra-restart |
ldm infra restart |
ldm init-common |
ldm infra init-common |
ldm renew-ssl |
ldm infra renew-ssl |
ldm migrate-search |
ldm infra migrate-search |
ldm cloud-fetch |
ldm cloud fetch |
ldm env |
ldm config env |
ldm feature |
ldm config feature |
ldm log-level |
ldm config log-level |
ldm edit |
ldm config edit |
ldm defaults |
ldm config defaults |
ldm init-from |
ldm link |
ldm start |
ldm db start |
All CLI Options Reference¶
The following is a comprehensive index of all registered CLI option flags and their descriptions:
--archetype: Apply an Extensible Stack Archetype (e.g. 'keycloak-sso', 'clustered')--ascii: Enable ASCII-safe output translation.--auto-install-lfr-tunnel(deprecated): LDM no longer downloads a client. The flag still parses and still means "do not prompt", so it continues to work with alfr_tunnel_install_cmdconfigured in~/.ldmrc; without one it warns and explains how to install the client.
LDM resolves an existing lfr-tunnel client rather than installing one, in
this order:
LDM_LFR_TUNNEL_BINlfr_tunnel_binin~/.ldmrc- anywhere on
PATH ~/liferay/lfr-tunnel/lfr-tunnel-- the location endpoint protection is configured to allow (lfr-tunnel.exeon Windows)~/.ldm/bin/lfr-tunnel-- where LDM used to install its own copy. Still resolved so existing setups keep working, but never written to again.
Every candidate is resolved to its real path (symlinks included) before it is
probed or executed, so a legacy ~/.ldm/bin/lfr-tunnel that is itself a
symlink to the whitelisted location is invoked by that resolved, whitelisted
path -- not by the unresolved, non-whitelisted symlink.
If no client is found, LDM explains how to install one and points at the
containerised lfr-tunnel-docker provider, which needs no host binary.
--background: Run dashboard in background.--backup-dir: Directory path to backup archives.--benchmark: Display performance benchmark on execution. On a command that boots Liferay this also prints a startup phase breakdown -- see Where a single slow boot spent its time.--build-info: Inject build metadata into the source.--bump: Increment the version logically.--clone-only: Force cloning the Git repository instead of downloading the LDM package (.ldmp).--clear-lock: Clear the stale concurrency project lock for the specified project and exit.
--container: Show detailed Docker container diagnostic checks.--docker: Show detailed Docker diagnostic checks.--domain: Custom domain prefix (e.g. lfr-demo.online, lfr-demo.se).--download: Force downloading of dependencies.--dry-run: Preview execution without mutations.--files-only: Extract or backup files/folders only.--force-boot: Restart the container immediately to apply the scheduled reindex, without prompting.--force-downgrade: Force a version downgrade (bypassing safety validations).--grep: Grep search pattern for filtering log lines.--grep-i: Case-insensitive grep search.--grep-v: Inverted grep search (select non-matching lines).--hydrate-from: Automatically hydrate data from a Liferay Cloud environment.--image: Custom Docker image to use for the sharing tunnel sidecar.--index: Force indexing check.--inspector: Expose the lfr-tunnel local inspector dashboard on port 4040.--keep-config: Retain global config file ~/.ldmrc.--keep-last: Keep only the specified number of most recent snapshots.--latest: Restore the most recent snapshot.--leave-running: Keep the running project active and abort the import if it is currently running.
--legacy-volumes: Also offer LDM volumes created before ownership labelling existed. Always interactive, never covered by--all.--list-backups: List backups in project work-folders.--list-envs: List all cloud environments.--logs: Stream container logs.--name: Specify target name.--no-color: Disable ANSI color codes in output.--no-env-sync: Skip syncing environment variables from Liferay Cloud.--no-home-warn: Suppress warning when running LDM from the root of the user's home directory.--tunnel-managed-cors: Skip local CORS patching and defer entirely to the tunnel gateway's dynamic header injection.--no-move: Skip moving existing data (just create symlinks).--no-restart: Do not automatically stop and restart the containers.--no-run: Update the metadata without automatically restarting the stack.--no-unicode: Disable Unicode characters in output and force ASCII safe-replacements.--older-than: Delete snapshots older than the specified number of days.
tray¶
Launch the cross-platform LDM System Tray GUI application to monitor runtime container health, view active URLs, and launch/stop instances.
ldm tray # Launch System Tray GUI
ldm tray --autostart # Install launch-on-login autostart
ldm tray --uninstall-autostart # Remove launch-on-login autostart
Global and Subcommand Flags Reference¶
--all: Apply command across all managed projects.--autostart/--install-autostart: Install System Tray application to launch automatically on user login.--bundle: Generate a sanitized zip bundle of logs and config.--credentials: Print admin or service credentials.--cpu-idle-checks: Number of consecutive checks required to verify Liferay is idle (default: 3).--cpu-idle-threshold: CPU percentage threshold below which Liferay is considered idle (default: 15.0).--detailed: Show detailed diagnostic information.--domain: Specify target domain for diagnostics or routing.--download: Download asset or seed.--dry-run: Print actions without executing them.--fix: Automatically apply recommended fixes.--fix-hosts: Add missing domains to/etc/hosts.--force: Force operation without prompting.--format: Specify output format (json,table,csv).--fragment-patch-timeout(wait): Per-loop seconds to poll the headless-admin-site API for OSGi/Site Initializer readiness before patching fragment overrides (default: 300, or 900 when auto-detected on an external drive). Runs after — and additive to —wait's own--timeout; also overridable viaLDM_FRAGMENT_PATCH_TIMEOUT.--global: Apply setting globally.--help: Display CLI help menu.--index/-i: Select project by 1-based index from list.--leave-running: Leave workspace containers running after operation.--list: List items or URLs.--no-ssl: Bypass local SSL/mkcert generation and use HTTP.--non-interactive: Run in non-interactive mode.--output: Directory path to save generated output.--overwrite-registry: Automatically overwrite existing project registry entries.--ports: Comma-separated ports to expose.--print: Output current version string only.--project: Show detailed Project diagnostic checks.--project-id: Specific project ID to target.--default: Set target compute node as active global default.--force-recreate: Force recreation of Docker containers.--host: Host IP address or SSH connection string for target compute node.--key: Path to SSH key identity file for target compute node.--promote: Promote current pre-release to stable.--provider: Tunnel provider (defaults to lfr-tunnel).--quiet: Quiet mode (suppress info logs).--reboot: Force container reboot instead of runtime reindexing.--reindex: Force full search reindex on startup.--restore: Restore project backup/snapshot.--service: Specify container service.--set: Directly set version string.--share-domain: Custom domain for sharing.--share-image: Custom Docker image for sharing sidecar.--share-inspector: Expose local inspector dashboard on port 4040.--share-provider: Sharing provider to use.--status: Check status without performing upgrade.--stop-running: Automatically stop running project.--subdomain: Custom subdomain prefix.--sync-env: Sync configuration env vars.--system: Show detailed system diagnostic checks.--tail: Number of lines to show from end of logs.--timestamps: Show timestamps.--token: CSRF/Authentication token for dashboard mutations.--trigger: Event trigger for workflow.--tui: Launch interactive terminal configuration menu.--uninstall-autostart: Remove System Tray launch-on-login autostart.--up: Automatically start project after reseeding.--url: Remote packages download URL; forshare start, the public tunnel URL to claim (shorthand for--subdomainplus--domain).--user: SSH user name for target compute node connection.--version: Target specific version of LDM.--wait-for-bundles: Comma-separated list of expected OSGi bundle symbolic names.--wait-for-deployables: Scan workspace for JARs/YAMLs and block until deployed.--stream-status: Stream Liferay startup milestones to stdout in real-time.--stream-logs: Stream raw Docker container logs to stdout in real-time.--workflow-name: Name of workflow file.-V: Show LDM version.-q: Quiet mode.
target (Multi-Node Compute Node Management)¶
Manage local and remote compute target nodes for multi-node execution, live workload migration, and remote container orchestration:
ldm target add win-wsl --host 192.168.1.50 --user developer --key ~/.ssh/id_rsa --default
ldm target ls
ldm target use win-wsl
ldm target status win-wsl
ldm target set win-wsl
ldm target migrate local win-wsl
ldm target rm win-wsl
target add adds or updates -- omitted flags are kept¶
Running target add against a name that already exists merges onto the
stored node: any flag you do not pass keeps its stored value (LDM-#1797).
It used to replace the node wholesale, so the obvious repair for a wrong SSH
user reported "registered successfully" and silently erased the node's
--mac-address, producing the activation failure described below.
Clearing a value is still possible -- an absent flag and a flag passed with an empty value are different inputs:
ldm target add node-a --mac-address "" # removes the pin
ldm target add node-a --user "" # removes the SSH user
--default is the exception: a store_true flag has no empty spelling, so
target add can only set it. Use ldm target use <name> to move the default.
An update reports what it changed and what it kept:
✅ Target node 'node-a' (203.0.113.10) updated.
ℹ Changed SSH user: ldm-automation -> ec2-user
ℹ Docker context 'node-a' rebuilt to dial ssh://ec2-user@203.0.113.10.
ℹ Kept host 203.0.113.10, MAC pin 06:ff:c5:f9:cf:a5 (not passed).
An update that moves nothing reports stored settings unchanged rather than a
bare success -- and still names the Docker context it rebuilt, since a remote
target add always rebuilds one.
--mac-address (licence activation on remote nodes)¶
Liferay's licence binds to a MAC address. On a remote node the container takes
a bridge-assigned address, validation fails, and the portal serves the DXP
Activation page instead of the Sign In form -- with a healthy container and
License registered in the log, so nothing looks wrong.
Use the node's own primary NIC address (the one the licence allows). LDM does
not infer it: a node typically shows ens5 beside docker0 and br-*, all
plausible and only one licensed, and a wrong value produces the failure above.
Re-run ldm target add with the same name to change it -- and see above: the
re-run keeps every other setting you do not pass.
target add reads the node's interfaces over SSH and warns when the value
you gave matches none of them, naming each one so you can pick the right one:
⚠️ MAC 02:42:de:ad:be:ef matches none of 'node-a's interfaces
(docker0 02:42:11:22:33:44, ens5 06:1a:2b:3c:4d:5e).
It warns rather than refuses, and records the value as given: Liferay validates the container's MAC against the licence and does not care what the host's interfaces are, so a licence bound to a MAC that is not a current NIC is legitimate, if unusual. A node that cannot be reached says nothing at all -- unable to ask is not the same as wrong (LDM-#1780).
The check is one SSH round trip, and target add announces it because
registering a node that is currently stopped is legitimate and pays the full
connect timeout there -- measured 6s to 16s against an unroutable host:
No --mac-address, no round trip and no line.
LDM re-reads the MAC from the running container and refuses when it does not match, because writing the value into the compose file is a request rather than a guarantee. The MAC can only be set at container creation, so a changed value requires a recreate.
See docs/explanation/remote-node-architecture.md for the measurement and the
bridge-only safety caveat.
target migrate¶
Migrate a running project workload live between target compute nodes with zero manual data loss:
(Performs pre-flight target probe -> creates live snapshot on source -> stops source stack -> syncs workspace via rsync -> reassigns project target metadata -> starts workload on destination node).
Last Updated: 2026-10-23 | Last Reviewed: 2026-10-23