Tools for re-creating different Liferay environments and sharing those environments with others, built on Liferay Workspace and Docker Compose.
This Liferay Workspace is set up so you can immediately spin up an environment with Liferay, a database, Elasticsearch and an NGINX webserver set up. Different features and services can be included or omitted as needed.
To start up the environment, run ./gradlew start.
To shut down the environment, run ./gradlew stop.
- Set the Liferay Docker image version
- Deploy OSGi configs
- Deploy portal-ext.properties
- Deploy hotfixes
- Deploy custom modules and projects
- Set the Liferay version for building modules
- Deploy a Document Library
- Deploy license files
- Enable clustering
- Configure Liferay ports
- Enable MySQL 8.4
- Enable PostgreSQL 16.3
- Enable DB2 11.5
- Enable MariaDB 10.6
- Import a database dump
- Enable database partitioning (MySQL and PostgreSQL only)
- Configure database port
- Supports Liferay clustering OOTB
- Enable standalone Elasticsearch
- Configure Elasticsearch ports
- Supports Liferay clustering OOTB
- Enable NGINX (HTTP)
- Enable NGINX (HTTPS)
- Use custom hostnames
- Configure webserver ports
- Supports Liferay clustering OOTB
- Build a custom Liferay image with custom modules and configs included
- Start up and shut down the Docker Compose containers
- Start up environment
- Shut down environment
- Restart environment
- Export container data
- Zip the workspace for sharing
- Export the Liferay logs, reports, and routes directories
- Clean up prepared hotfixes
- Clean up all prepared data and built Liferay Docker images
- You must have
dockeranddocker composeinstalled
Set the liferay.workspace.docker.image.liferay property in gradle.properties.
This will override the Docker image version that is determined from the liferay.workspace.product property (see Set the Liferay version for building modules).
gradle.properties:
liferay.workspace.docker.image.liferay=liferay/dxp:7.2.10-sp8Place OSGi .config files in the ./configs/common/configs directory. They will be included in the built Liferay image.
OSGi config files:
./configs/common/configs/SomeConfigFile.config
Place *.properties files in the ./configs/common directory. They will be included in the built Liferay image.
Properties files:
./configs/common/portal-ext.properties
Add hotfix URLs to the lr.docker.environment.hotfix.urls property in gradle.properties as a comma-separated string. Each URL listed will be downloaded and placed into the ./configs/common/patching directory, which will be included in the built Liferay image.
gradle.properties:
lr.docker.environment.hotfix.urls=\
https://releases-cdn.liferay.com/dxp/hotfix/2024.q2.7/liferay-dxp-2024.q2.7-hotfix-4.zip,\
https://releases-cdn.liferay.com/dxp/hotfix/2024.q2.7/liferay-dxp-2024.q2.7-hotfix-5.zipNote: Local file URLs are also supported using the file:// protocol.
Liferay Workspace will automatically build and deploy custom modules and projects contained in the Workspace to the built Liferay Docker image. More documentation on creating and building projects can be found at Liferay Learn.
Document library files can be added to Liferay in one of two ways:
-
Add the document library folder to
./configs/common/data/document_library -
Include the document library as part of the data directory defined by the
lr.docker.environment.data.directoryproperty. See Data Features for more details on how to create and use data directories.
Document library files for method #1:
./configs/common/data/document_library
If you receive a very large Document Library and do not need the actual file contents, you may want to copy the file structure only. The lec script provides the lec importDLStructure command to assist with this:
lec importDLStructure <source_directory>The source directory's file structure only will be copied into ./configs/common/data/document_library.
Add a license files to ./configs/common/osgi/modules.
Note: The Gradle command to start the server will fail if there are no license files and you are trying to start up a Liferay DXP image.
Clustering can be enabled by setting the lr.docker.environment.cluster.nodes property in gradle.properties. Setting it to 0 means no clustering is enabled. Setting it to 1 or more will add that many cluster nodes in addition to the main Liferay instance.
gradle.properties:
# This will start the main Liferay instance and 2 additional cluster nodes
lr.docker.environment.cluster.nodes=2You can configure the Liferay ports in the ports.env file. Each variable in this file defines a range from which the
exact port numbers will be automatically chosen based on availability.
ports.env:
LIFERAY_PORT=8080-8089
LIFERAY_GOGO_SHELL_PORT=11311-11319
LIFERAY_DEBUG_PORT=8000-8009
LIFERAY_YOURKIT_PORT=10001-10010To customize Liferay's JVM arguments, modify the LIFERAY_JVM_OPTS variable in ./liferay-jvm-opts.env. This file already includes several default arguments for better server performance.
Set the lr.docker.environment.service.enabled[mysql] property to true or 1 in gradle.properties.
gradle.properties:
lr.docker.environment.service.enabled[mysql]=trueSet the lr.docker.environment.service.enabled[postgres] property to true or 1 in gradle.properties.
gradle.properties:
lr.docker.environment.service.enabled[postgres]=trueSet the lr.docker.environment.service.enabled[db2] property to true or 1 in gradle.properties.
gradle.properties:
lr.docker.environment.service.enabled[db2]=trueSet the lr.docker.environment.service.enabled[mariadb] property to true or 1 in gradle.properties.
gradle.properties:
lr.docker.environment.service.enabled[mariadb]=trueDatabase dump files can be added to the ./dumps directory at the root of the Workspace. They will automatically be copied into the database container.
./dumps/dumpfile.sql # raw database dump file
./dumps/dumpfile.gz # compressed database dump file downloaded from LXC or extracted from a password-protected archive
For SaaS database backups, you will often be provided with a password-protected 7zip file. While ideally you would extract the file, the tool can also automatically extract it for you if you set the lr.docker.environment.lxc.backup.password property in gradle.properties to the provided password.
./dumps/dumpfile.7z # database dump file provided by SRE team (SaaS), usually password-protected
./dumps/dumpfile.zip # database dump file provided by SRE team (SaaS), usually password-protected
When importing SaaS database backups, make sure to set properties to allow the tool to find information related to the SaaS environment.
lr.docker.environment.lxc.environment.name=abc1prd
For example, if the projectId is lxcabc1-abc1prd, the environment name name is abc1prd. The tool will use this information to automatically copy configurations from the liferay/liferay-lxc repository, which it assumes will be located at ${user.home}/dev/projects/liferay-lxc. If it is not located at this location, you will need to set the correct path as an environment variable.
export LXC_REPOSITORY_PATH=/home/me/dev/projects/liferay-lxc
Set the lr.docker.environment.database.partitioning.enabled property to true or 1 in gradle.properties.
gradle.properties:
lr.docker.environment.database.partitioning.enabled=trueThe database port can be configured by the DATABASE_PORT environment variable in the ports.env file.
ports.env:
DATABASE_PORT=54321-54330Set the lr.docker.environment.liferay.user.password property to the password you wish to type when signing in using existing users in imported databases.
lr.docker.environment.liferay.user.password=testSet the lr.docker.environment.service.enabled[elasticsearch] property to true or 1 in gradle.properties.
gradle.properties:
lr.docker.environment.service.enabled[elasticsearch]=trueThe Elasticsearch HTTP and transport ports can be configured by the ELASTICSEARCH_HTTP_PORT and
the ELASTICSEARCH_TRANSPORT_PORT environment variables respectively in the ports.env file.
ports.env:
ELASTICSEARCH_HTTP_PORT=9200-9209
ELASTICSEARCH_TRANSPORT_PORT=9300-9309Set the lr.docker.environment.service.enabled[mail] property to true or 1 in gradle.properties.
gradle.properties:
lr.docker.environment.service.enabled[mail]=trueThe mail service's web and SMTP ports can be configured by the MAIL_WEB_PORT and MAIL_SMTP_PORT environment variables
respectively in the ports.env file.
ports.env:
MAIL_WEB_PORT=1080
MAIL_SMTP_PORT=1025Set the lr.docker.environment.service.enabled[webserver_http] property to true or 1 in gradle.properties.
gradle.properties:
lr.docker.environment.service.enabled[webserver_http]=trueSet the lr.docker.environment.service.enabled[webserver_https] property to true or 1 in gradle.properties.
gradle.properties:
lr.docker.environment.service.enabled[webserver_https]=trueThe webserver HTTP and HTTPS ports can be configured by the WEBSERVER_HTTP_PORT and the WEBSERVER_HTTPS_PORT
environment variables respectively in the ports.env file.
ports.env:
WEBSERVER_HTTP_PORT=80
WEBSERVER_HTTPS_PORT=443Specify the hostnames through which you want to access Liferay using the lr.docker.environment.web.server.hostnames property.
You can provide multiple hostnames, separated by commas.
lr.docker.environment.web.server.hostnames=localhost./gradlew exportContainerData
This will export data from each of the running containers to a timestamped directory inside of ./exported_data. This directory can then be directly referenced by the lr.docker.environment.data.directory property to re-use that data on future startups.
Note: This repo intentionally does not bind-mount container directories to host directories as it can easily cause startup issues due to user permission mismatches. It is a known issue with Docker Compose.
Set the lr.docker.environment.data.directory property in gradle.properties to a relative or absolute path to a directory. This directory structure illustrates where each service directory is mapped in the respective container:
data_folder (directory in their repsective container)
├── elasticsearch -> /usr/share/elasticsearch/data/
├── liferay -> /opt/liferay/data
└── mysql -> /var/lib/mysql
gradle.properties:
lr.docker.environment.data.directory=exported_data/data_20241206.175343Set the lr.docker.environment.glowroot.enabled property to true or 1 in gradle.properties to enable Glowroot.
gradle.properties:
lr.docker.environment.glowroot.enabled=trueSet the lr.docker.environment.yourkit.enabled property to true or 1 in gradle.properties to enable YourKit.
gradle.properties:
lr.docker.environment.yourkit.enabled=trueYou can provide the download URL of the preferred YourKit version zip in the lr.docker.environment.yourkit.url property.
gradle.properties:
lr.docker.environment.yourkit.url=https://www.yourkit.com/download/docker/YourKit-JavaProfiler-2025.3-docker.zip./gradlew shareWorkspace
This will zip up the workspace as-is, including the declared data folder, into a shareable zip file. The zipped workspace will be timestamped and placed in the ./shared_workspaces directory. It will omit unnecessary files such as the .gradle and .git directories, as well as other exported data folders and shared workspaces in the exported_data and shared_workspaces directories.
The shared workspace should be immediately usable by simply unzipping the archive, cd to the unzipped folder, and starting up with ./gradlew start.
./gradlew start
./gradlew stop
By default, stopping a container will delete all persistent data, which has the desirable side-effect that product team members always start from a clean reproduced environment, but has the undesirable side-effect that customer support engineers always lose all changes since the last saved reproduced environment.
To change this behavior, set the following in your gradle-local.properties:
lr.docker.environment.clear.volume.data=false./gradlew restart
This will also stop the environment, so please see the previous note which describes the strategy for persisting data between restarts.
./gradlew exportContainerData
./gradlew shareWorkspace
./gradlew exportLiferayLogs
This will copy the logs, reports, and routes directories to the ./exports/liferay directory on your machine (host machine).
./gradlew cleanPrepareHotfixes
./gradlew clean
Creating new workspaces can be tedious, so we have provided a shell script to help automate some repetitive tasks.
To use this shell script, add the following snippet to your .bashrc or .zshrc, replacing the values with the correct file paths:
#
# Liferay Environment Composer
#
# This environment variable should be the path to your clone of this repository (or your own fork).
export LIFERAY_ENVIRONMENT_COMPOSER_HOME="$HOME/Documents/liferay/liferay-environment-composer"
# Source the cli
[[ -s "$LIFERAY_ENVIRONMENT_COMPOSER_HOME/scripts/cli/shell-source.sh" ]] && source "$LIFERAY_ENVIRONMENT_COMPOSER_HOME/scripts/cli/shell-source.sh"
# Optional: This environment variable should point to a directory where you want projects created when running `lec init`.
export LIFERAY_ENVIRONMENT_COMPOSER_WORKSPACES_DIR="$HOME/Documents/liferay/tickets"
This will provide the lec script alias and the lecd shell function.
Create a new workspaces:
lec init # No args
lec init LPP-12345 # Pass in the LPP ticket number
lec init LPP-12345 dxp-2025.q3.0 # Pass in the LPP ticket number and the Liferay version
lec init LPP-12345 abc1prd # Pass in the LPP ticket number and the SaaS environment nameStart the workspace:
lec start # Starts the environment and tails the logsStop the workspace:
lec stop # Shuts down the environmentStop the workspace and delete the Docker volumes:
lec clean # Shuts down the environment and deletes the Docker volumesJump to a workspace:
lecd # Choose from a list
lecd workspace-name # Pre-filter the list by name. If only one matches, jump there.