A professional command-line orchestrator for quickly standing up Liferay Portal and DXP environments using Docker Compose.
Note
Project History: This tool was originally born as part of the liferay-docker-scripts repository. It has since evolved into a standalone application to provide better modularity and multi-instance stability.
ldm is designed to be both fast for power users and helpful for newcomers. It follows a consistent usage pattern:
- Sensible Defaults: Whenever a standard Liferay convention exists,
ldmuses it automatically (e.g., port8080, managed DB namelportal). - Smart Context: If you run a command from inside a project folder,
ldmautomatically detects the project context. - Interactive Fallback: If a required piece of information (like a project name or a Liferay tag) is missing from your command and cannot be detected,
ldmwill prompt you interactively or show you a list of choices. - Graceful Abort: You can type
qat any interactive prompt to safely cancel the operation.
The badges below represent our verified support for various Docker providers. Environments marked as Hardened have received specific logic refinements to handle complex file-sharing and permission scenarios.
| Component | Verified Versions | Notes |
|---|---|---|
| Traefik | v3.6.1+ |
Automatic API version negotiation enabled. |
| Elasticsearch | 8.19.1, 7.17.24 |
Dual support with auto-plugin installation and optimized Liferay config. |
- Multi-Instance Session Isolation: Run multiple demos side-by-side on the same machine without session cross-talk.
ldmautomatically manages unique session cookie names and virtual hostnames. - Orchestrated Search Snapshots: Save and restore the exact state of a demonstration, including the database, files, and Elasticsearch 8.x index state.
- Service-Specific Lifecycle: Manage individual components within a project surgically. Restart or view logs for a single extension without affecting the rest of the stack.
- Client Extension Lifecycle: Automatically detects and builds Server-Side Client Extensions (SSCE). Subdomains are automatically generated, and traffic is routed based on
LCP.json. - Zero-Config SSL: Automated HTTPS using
mkcertand a global Traefik proxy. Works on Docker Desktop, Colima, and WSL2. - Proactive License Verification: Automatically detects Liferay XML licenses in
common/,deploy/, orosgi/modules/folders. Warns you before boot if a DXP license is missing or expired. - Fail-Fast Design: Proactive environment checking. LDM verifies Docker reachability, volume mounts, resource allocations (CPU/RAM), and Compose functionality before execution, providing clean, actionable error messages instead of tracebacks.
- Port Conflict Detection: Proactively verifies that required host ports (80, 443, 9200, etc.) are available before starting, preventing cryptic Docker errors.
- Atomic Configuration: All project metadata and property updates use safe atomic writes to prevent file corruption during interruptions.
- Integrity Verification: As of v2.4.0, all project snapshots and pre-warmed bootstrap seeds include mandatory SHA-256 checksums. LDM automatically verifies these during recovery and import to ensure data validity.
- Global Project Registry: Proactively detects project and hostname collisions across the entire filesystem, preventing infrastructure conflicts.
- Architecture-Aware: The tool detects your OS automatically to fetch the correct optimized binary during self-updates.
- Shell Autocompletion: TAB completion for commands and project names across Bash, Zsh, and Fish.
- Fuzzy Interactive Selection: Quickly filter through dozens of projects by typing a few characters in any interactive menu.
- Installation Guide
- Architecture Overview
- Test & Validation Strategy
- Security Posture & Disclosures
- Future Roadmap
LDM is designed to be pipeline-friendly. The ldm doctor command returns a non-zero exit code if critical environment issues are detected.
Ensure your environment is healthy before attempting to start a project:
ldm doctor --skip-project && ldm run my-projectCheck if services are running before executing operations:
# Start infrastructure only if it's not already running
ldm ps || ldm infra-setup --searchYou can use LDM in automated scripts to verify infrastructure:
if ldm doctor --skip-project; then
echo "Environment is healthy, proceeding..."
else
echo "Critical environment failure!"
exit 1
fiThe standalone binary is the recommended way to use LDM.
# For macOS (Apple Silicon)
sudo curl -L https://github.com/peterrichards-lr/liferay-docker-manager/releases/latest/download/ldm-macos-arm64 -o /usr/local/bin/ldm
# For macOS (Apple Intel)
sudo curl -L https://github.com/peterrichards-lr/liferay-docker-manager/releases/latest/download/ldm-macos-x86_64 -o /usr/local/bin/ldm
# For Linux / WSL2
sudo curl -L https://github.com/peterrichards-lr/liferay-docker-manager/releases/latest/download/ldm-linux -o /usr/local/bin/ldm
# Make it executable
sudo chmod +x /usr/local/bin/ldmFor detailed instructions and Windows setup, see the Full Installation Guide.
Important
Binary vs Script: If you have installed the Standalone Binary, use ldm in your commands. If you are using the Manual Installation, use ./ldm (on Linux/macOS) or ldm.bat (on Windows) from the root of this repository.
# 1. THE CONFIDENCE BOOSTER: Run Liferay with pre-configured samples
ldm run my-sample-project --samples
# 2. THE DEVELOPER FLOW: Initialize from a workspace and start monitoring
ldm init-from /path/to/workspace my-project
# 3. THE ARCHIVE FLOW: Import a static snapshot of a workspace
ldm import /path/to/workspace my-static-project
# 4. THE RECOVERY FLOW: Re-create a deleted project from a snapshot folder
ldm run my-recovered-project --snapshot ~/Desktop/old-baseline-snapshot
# 5. THE SURGICAL FLOW: Instantly edit project metadata
ldm edit my-project
# Monitor an existing project (manually)
ldm monitor /path/to/workspaceTo protect the integrity of the application cache (~/.shiv) and ensure consistent file permissions, never run LDM with the sudo prefix.
LDM is designed to run as a standard user and will automatically request elevation (prompting for your password) only for specific tasks that require it:
ldm doctor --fix-hosts: Requires elevation to append entries to/etc/hosts.ldm upgrade: May require elevation to replace the binary in system paths like/usr/local/bin.
If you are using sudo because of Docker "Permission Denied" errors, do not use sudo ldm. Instead, add your user to the docker group:
sudo usermod -aG docker $USERThen restart your terminal session.
Display a tabulated overview of all initialized LDM sandbox environments.
ldm list
ldm lsInitialize and start a project stack.
# Run with a specific tag and virtual hostname
ldm run --tag 2024.q4.0 --host-name demo.local
# Automatically grab the latest Quarterly Release
ldm run demo --tag-latest --release-type qr
# Disable Omni-Admin Captchas for automated testing
ldm run demo --tag-latest --no-captcha
# Run on a custom port
ldm run my-project --port 8081
# Using the alias
ldm up demo
# Initialize with "Confidence Booster" samples
ldm run demo --samples
# Interactive run (will prompt for version and project name)
ldm runLDM uses smarter defaults for SSL based on your hostname. When a custom --host-name is used, SSL is enabled by default to support modern Liferay features like Client Extensions.
| Command | Host Name | SSL Default | Access URL |
|---|---|---|---|
ldm run |
localhost |
False |
http://localhost:8080 |
ldm run --host-name my.local |
my.local |
True |
https://my.local |
ldm run --no-ssl |
localhost |
False |
http://localhost:8080 |
ldm run --host-name my.local --no-ssl |
my.local |
False |
http://my.local:8080 |
LDM automatically hardens modern environments (DXP 2024+ and modern Quarterly Releases) to ensure stable startup:
- JVM Module Exports: Automatically injects mandatory
--add-opensflags for JDK 17+ (coveringjava.net,java.lang.reflect,security, and more). - Hardened MySQL 8.4 (LTS):
- Standardized on the MariaDB JDBC Driver and
MariaDB103Dialectto mirror Liferay Cloud (LXC) environments. - Forces
mysql_native_passwordauthentication for CI compatibility. - Includes performance-optimized connection parameters (e.g.,
rewriteBatchedStatements,prepStmtCacheSize). - Redline Configuration: Explicitly sets
hibernate.dialectandjdbc.default.*properties inportal-ext.propertiesto ensure reliable interpretation of mixed-case keys (likedriverClassName). - Prioritizes
LIFERAY_JDBC_DEFAULT_*environment variables ONLY for runtime user overrides; LDM baseline always usesportal-ext.properties.
- Standardized on the MariaDB JDBC Driver and
- Proactive Boot Sequencing: Configures
depends_onwith healthchecks to ensure Liferay only starts once the database is fully ready to accept connections.
... [Previous content kept] ...
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).
Initialize a project from a source workspace and establish a persistent link. This command records the workspace path in the project metadata and automatically starts the monitor process to sync your code changes in real-time.
# ldm init-from <source_path> [project_name] [--host-name custom.local]
ldm init-from ~/repos/my-workspace my-project --host-name forge.demo
# Initialize with the latest tag and disable CAPTCHAs for CI testing
ldm init-from ~/repos/my-workspace my-ci-project -y --tag-latest --no-captchaScaffold a new project by taking a one-time static import of an existing workspace. This project is detached from the source; changes to the source workspace will not be synced. Follows the same internal deployment sequence as init-from.
# ldm import <source_path> [project_name] [--host-name custom.local]
ldm import ~/repos/my-workspace my-static-project
# Import using a specific release type filter
ldm import ~/repos/my-workspace my-static-project --tag-latest --release-type qrAll project initialization commands follow these security and naming rules:
- Interactive Hostname: If no
--host-nameis provided, LDM will prompt you (defaulting tolocalhost). - SSL Auto-Enable: If a custom hostname is used (anything other than
localhost), LDM automatically enables SSL and routes traffic via port 443. - Explicit Control: You can override the auto-SSL behavior using
--sslor--no-ssl. - Port Mapping: When SSL is active, the direct port
8080mapping is removed to ensure all traffic passes through the secure Traefik proxy.
Restarts the background watch process for a project linked to a Liferay workspace. This command can only be used for projects created with init-from. It automatically syncs built artifacts (.jar, .war, .zip) whenever they are updated in the workspace.
ldm monitor [project_name] --delay 2.0View real-time logs. Supports filtering by project, specific services, or global infrastructure components.
ldm logs [project] [service1] [service2] ...
# Examples:
ldm logs # All logs for current project
ldm logs demo # All logs for 'demo' project
ldm logs --tail 250 # Show last 250 lines (default: 100)
ldm logs --all # Dump recent logs for all running projects
ldm logs --infra # Show logs for all global infrastructure (ES, Proxy, etc.)
ldm logs --infra es # Show logs only for Global Elasticsearch
ldm logs --infra proxy # Show logs only for Global SSL Proxy
ldm logs demo liferay # Only Liferay logs for 'demo'
ldm logs demo liferay my-ext # Multi-service tailingManage the lifecycle of a project or a specific service.
ldm stop [project] [service] # Stop containers gracefully
ldm restart [project] [service] # Stop and then start
ldm down [project] [service] # Remove containers (and optionally -v volumes)
ldm rm [project] # Alias for 'down'
# Examples:
ldm stop --all # Stop all running projects in the workspace
ldm restart --all # Restart all running projects
ldm restart # Full stack restart (graceful stop + run)
ldm down --volumes # Tear down stack and clear all database/data stateSurgically reset or completely restore a project to its original vanilla state.
ldm reset [project] [target] # Clear specific data (state|db|search|all)
ldm re-seed [project] # Wipe ALL data and re-apply vanilla seedExamples:
ldm reset demo state # Clear only the OSGi bundle state
ldm reset demo db # Clear only the database data
ldm re-seed demo # Total project reset to Day Zero (Seeded)View the status of all projects in the current workspace.
ldm statusTip
Projects marked with a 🌱 (seedling) emoji were initialized from a Seeded State, meaning they started with a pre-calculated database and OSGi cache for near-instant boot times.
Hot-deploy built artifacts or rebuild extension images.
ldm deploy [project] [service] --rebuild
# Examples:
ldm deploy # Sync all artifacts and refresh stack
ldm deploy demo my-ext --rebuild # Rebuild and restart one extensionScale services within a project for multi-node simulation and clustering tests.
ldm scale [project] service=count
# Examples:
ldm scale demo liferay=2 # Scale Liferay to 2 nodes (enables clustering)
ldm scale demo my-ext=3 # Scale a client extension to 3 nodesBackup and recover project states, including files, DB, and search indices.
Examples:
# Create a named snapshot
ldm snapshot demo --name "post-setup-gold-standard"
# List snapshots for a project
ldm restore demo --list # Non-interactive list of all snapshots
ldm restore demo --index 1 # Restore to index 1Jump into a container shell for deep inspection or connect to the OSGi Gogo console for runtime management.
Interactive Shell Examples:
# Enter bash in the Liferay container
ldm shell demo
# Common Shell Tasks (inside container):
# 1. View live Tomcat logs
cd tomcat/logs && tail -f catalina.out
# 2. Check injected environment variables
env | grep LIFERAY
# 3. Verify mounted OSGi configurations
ls osgi/configsGogo Shell Examples:
# Connect to the Gogo shell (requires --gogo-port during run)
ldm gogo demo
# Common Gogo Commands:
# 1. List all active bundles
lb
# 2. Check for unresolved dependencies
diag
# 3. List declarative services (SCR)
scr:listManage persistent environment variables in project metadata.
ldm env [project] KEY=VALUE
ldm env [project] --remove KEY
ldm env # Interactive manager (view and edit all)Rapidly modify project configuration files in your system's $EDITOR (defaults to vi or notepad).
ldm edit [project] # Edit .liferay-docker.meta
ldm edit [project] --target properties # Edit portal-ext.propertiesSynchronize 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-envManage Liferay internal logging levels (Log4j2) without restarts.
# List current custom levels
ldm log-level --list
# Set a specific category to DEBUG
ldm log-level [project] --bundle portal --category com.liferay.portal --level DEBUG
# Interactive configuration
ldm log-levelVerify host environment health, Docker resources (CPUs/Memory), and project dependencies. Now includes checks for required tools: mkcert, telnet, nc, lcp, and the Docker Compose V2 plugin.
ldm doctor # Health check for current/selected project
ldm doctor --all # Batch validate every project in your workspace
ldm doctor --fix-hosts # Automatically add missing domains to /etc/hosts (will prompt for sudo)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 psLaunch 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]Automatically download and install the latest version of LDM for your architecture. Includes integrity verification.
ldm upgrade # Standard upgrade to latest
ldm upgrade --repair # Re-download current version to fix integrity issuesConfigure shell autocompletion for ldm. Supports Bash, Zsh, and Fish.
ldm completionSetup Summary:
- Run
ldm 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 and project names.
Display the comprehensive manual page for LDM. This provides an offline reference for all commands, options, and architecture details.
ldm manTo support the native system man ldm command, add this to your shell profile (.zshrc or .bashrc):
export MANPATH="$MANPATH:$HOME/.ldm/man"Refresh project-specific SSL certificates immediately.
ldm renew-ssl # Interactive selector
ldm renew-ssl demo # Renew for 'demo' specifically
ldm renew-ssl --all # Renew certificates for every projectInitialize or recreate the baseline global configuration (common/ folder) from internal resources.
ldm init-commonIndependently 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-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 SearchTip
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.
Migrates a project from using the internal Sidecar search to the shared Global Search container.
ldm migrate-search [project]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.
Surgically clear project data folders. This command requires the project to be stopped.
ldm reset [project] [target]Available Targets:
state(Default): Clears theosgi/statefolder.search: Clears internal Sidecar indices.db: Clears the Hypersonic database (if used).global-search: Deletes the project's indices from the shared Global Search container.all: Performs all of the above.
Examples:
ldm reset demo state # Clear OSGi state for 'demo'
ldm reset demo search,db # Clear local search and DB
ldm reset demo all # Total project data wipeIdentify and remove 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.
ldm pruneWhat 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.
- Temporary Files: Residual
.*.tmpfiles left behind by interrupted sync or build operations.
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.
ldm clear-cacheView or set global LDM configuration settings (stored in ~/.ldmrc).
ldm config # View all global settings
ldm config key value # Set a global preference
ldm config key --remove # Remove a preferencelogging.json: Managed vialog-levelcommand.common/: Files here (configs, XML licenses, LPKG files) are synced to all project stacks.services/: Place standaloneDockerfiledirectories here for orchestration.
- Docker Engine: Docker Desktop, Colima, or native WSL2.
- Docker Compose: v2 (Plugin) is mandatory. Legacy v1 standalone is not supported.
- Resources: Recommended 4 CPUs and 8GB RAM allocated to Docker.
- Note:
ldm doctorexpects these minimums. If you allocate exactly 8GB, Docker may report ~7.7GB due to system overhead; the tool accounts for this by allowing a 7.5GB threshold.
- Note:
- Python: 3.10+ (if not using binary)
- mkcert: (Optional) For automated local SSL.
If ldm doctor reports insufficient resources in Colima, you can increase them with these commands:
colima stop
colima start --cpu 4 --memory 8- Fuzzy Search Selection: In any project selection menu, you can simply start typing to filter the list. The menu will update in real-time to match project names or version tags.
- Smart Project Detection:
ldmresolves project locations using this priority:- Direct Path: Absolute or relative path (e.g.,
ldm logs ./my-projorldm logs /opt/ldm/proj). - CWD: If the current directory is an LDM project.
- Global Workspaces: Searches
LDM_WORKSPACE(if set),~/ldm, and/Volumes/SanDisk/ldm. - Deep Search: Scans the above directories for projects matching the name in their
.liferay-docker.meta.
- Direct Path: Absolute or relative path (e.g.,
- Quick Quit: You can type
qat any interactive prompt to safely abort the current command. - Initialization Overrides: When a project already exists, you can choose:
y(Yes): Overwrite configuration and artifact files.n(No): Continue initialization but skip/keep existing files.c(Clean): Delete the entire project folder and start fresh.q(Quit): Abort the process entirely.
- Bypass Prompts: Use the
-yor--non-interactiveflag to skip all confirmations and use default values. This is ideal for scripts and CI/CD pipelines. - Tag Prefix Search: When running
ldm runwithout a tag, you can enter a prefix (e.g.,2025.q4) to filter the available Liferay versions from Docker Hub. Alternatively, use the--tag-prefixswitch to bypass the prompt entirely. - Tag Discovery: If no prefix or release type is provided, the tool fetches the latest available tags from Docker Hub.
- Automated Latest Tags: In automated environments, use
--tag-latest(withldm initorldm run) to automatically discover and use the most recent stable tag, bypassing all interactive prompts. - Omni-Admin Captcha: During testing or CI workflows, you can use the
--no-captchaflag during initialization to automatically disable Liferay's mandatory Omni-Admin CAPTCHA checks.
If you want to contribute to LDM or test your changes locally, follow these steps.
The easiest way to develop is to install LDM in "editable" mode. This allows your changes to the ldm_core package to take effect immediately.
# Clone the repo
git clone https://github.com/peterrichards-lr/liferay-docker-manager.git
cd liferay-docker-manager
# Install in editable mode
pip install -e .
# Run the entry point
python3 liferay_docker.py --helpYou can build a single-file executable to test how the tool behaves as a binary.
Used for macOS and Linux. Fast and lightweight, but requires python3 to be present on the host.
# Build only
./scripts/package-shiv.sh
# Build and install to /usr/local/bin/ldm (requires sudo)
./scripts/package-shiv.sh --installBundles the Python interpreter inside the file. Works even on machines without Python installed.
# Build only
./scripts/package-pyinstaller.sh
# Build and install to /usr/local/bin/ldm (requires sudo)
./scripts/package-pyinstaller.sh --installThe resulting binary will be found in the dist/ folder (for PyInstaller) or the root (for Shiv).
MIT © Peter Richards