README
¶
Eureka CLI
Table of Contents
- Eureka CLI
- Table of Contents
- Purpose
- Architecture
- Prerequisites
- Monitor system components
- Commands
- Build the binary
- Install binary
- (Optional) Enable autocompletion
- Deploy the combined application
- Undeploy the combined application
- Preserve data volumes across redeployments
- Deploy the combined application from AWS ECR
- Deploy the ecs application
- Undeploy the ecs application
- Deploy the ecs-single application
- Undeploy the ecs-single application
- Deploy the ecs-migration application
- Undeploy the ecs-migration application
- Run IDP migration
- Deploy the import application
- Undeploy the import application
- Deploy child applications
- Undeploy child applications
- Intercept a module
- Create a port proxy
- Upgrade a module
- Run a local module
- Other commands
- Using a custom folio-module-sidecar
- Using a native folio-module-sidecar
- Using local backend module images
- Using local frontend module descriptors
- Using a backend module namespace
- Using private registry credentials
- Using a third-party application
- Using the UI
- Using Single Tenant UX
- Using the environment
- Using template environment variables
- Using per-sidecar environment variables
- Using extra volumes
- Using OpenTelemetry LGTM stack
- Add missing Vault secrets
- Troubleshooting
- Additional Resources
Purpose
- A CLI to orchestrate the deployment of a local Eureka-based development environment
Architecture
- The CLI is organized into layers: actions handle commands, services talk to external systems (Keycloak, Kong, Docker, Vault), and run configs manage profile-specific settings
- Commands like
interceptModuleandupgradeModulecoordinate these layers to streamline local development — you can reroute traffic to your local IntelliJ instance for live debugging, or build and deploy new module versions in one go. No more switching between terminals or remembering multi-step procedures
For detailed workflows and examples, check out the Module Development Guide.
Prerequisites
Install dependencies:
- Go compiler: last development-tested version is
go1.26.6 linux/amd64 - Rancher Desktop container daemon: last development-tested version is
v1.20.1- Enable dockerd (Moby) container engine
- Disable Check for updates automatically
- Disable Enable Kubernetes
On Windows, it is recommended to work exclusively in Windows Terminal running Git Bash:
- Windows Terminal: Install for better shell experience when paired with Git Bash
- Git Bash: Add to Windows Terminal as new default profile & Unix-like shell environment that supports Bash commands
Configure hosts (add entries to /etc/hosts or C:\Windows\System32\drivers\etc\hosts):
- Option 1 - Automated (recommended):
- Linux/macOS:
sudo ./misc/scripts/add-hosts.sh - Windows: Open PowerShell as Administrator and run:
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process; .\misc\scripts\add-hosts.ps1
- Linux/macOS:
- Option 2 - Manual: Add the following entries:
127.0.0.1 postgres.eureka127.0.0.1 kafka.eureka127.0.0.1 vault.eureka127.0.0.1 keycloak.eureka127.0.0.1 kong.eureka
WARNING: JVM sidecar images published on FOLIO Docker Hub are built with an invalid entrypoint and will fail to start. If you intend to use sidecars, you must build a native sidecar image locally — see Using a native folio-module-sidecar.
Monitor system components
- Dozzle UI: No auth
- Keycloak Admin Console: admin/admin
- Vault UI (Userpass method): admin/admin
- Kafka UI: No auth
- Kong Admin GUI: No auth
- Garage S3 API on port 3900: eurekaadmin/eurekaadminsecret
- OpenSearch Dashboards UI: No auth
- Grafana UI: No auth
Commands
Build the binary
mkdir -p ./bin
go build -o ./bin/ .
# Or pass GOOS and GOARCH to specify the platform and architecture explicitly
env GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o ./bin/ .
For more details, check out the Build Binary Guide to build a platform-specific binary.
Install binary
- After building and installing, the binary can be used from any directory
go install
eureka-cli deployApplication
If you wish to avoid the binary installation please use the relative path of the CLI binary, e.g.
./bin/eureka-cliwith the correct path to the config.
(Optional) Enable autocompletion
- Command autocompletion can be enabled in the shell of your choice. Below is an example for the Bash shell (
.bash_profileis preferred on Windows because it is auto-sourced)
go install
echo "source <(eureka-cli completion bash)" >> ~/.bash_profile
source ~/.bash_profile
Type
eureka-cliand hit TAB to see the available suggestions or full autocompletion.
Flags with smart autocompletion:
| Long | Short | Completion Source | Command(s) |
|---|---|---|---|
--profile |
-p |
Available profiles from config | All commands (global flag) |
--moduleName |
-n |
Backend modules from config | interceptModule, listModules, listModuleVersions, |
| undeployModule, updateModuleDiscovery | |||
--moduleType |
-y |
Container types (module, sidecar, etc) | listModules |
Deploy the combined application
By default, public images from DockerHub (folioci & folioorg namespaces) will be used.
Available flags:
Global flags (available for all commands):
| Long | Short | Description |
|---|---|---|
--buildImages |
-b |
Build Docker images |
--configFile |
-c |
Specify config file path |
--enableDebug |
-d |
Enable debug mode |
--onlyRequired |
-q |
Use only required system containers (deploySystem, deployApplication) |
--overwriteFiles |
-o |
Overwrite files in .eureka home directory |
--profile |
-p |
Select profile (combined, combined-native, combined-native-otel, export, search, edge, erm, ecs, ecs-single, ecs-migration, import) |
Command-specific flags:
| Long | Short | Description | Command(s) |
|---|---|---|---|
--all |
-a |
All modules for all profiles | listModules |
--apps |
Application names | purgeTenants | |
--cleanup |
Perform a cleanup operation | deployApplication, upgradeModule | |
--defaultGateway |
-g |
Use default gateway in URLs | interceptModule |
--enableEcsRequests |
Enable ECS requests | deployUi, buildAndPushUi, buildUi | |
--gatewayHostname |
Gateway Hostname | createPortProxy | |
--gatewayURL |
Gateway URL | purgeTenants | |
--id |
-i |
Module ID (e.g. mod-orders:13.1.0-SNAPSHOT.1021) | listModuleVersions |
--ids |
Tenant ids | purgeTenants | |
--keepVolumes |
-k |
Preserve system data volumes during undeployment | deployApplication, |
| undeployApplication, | |||
| undeploySystem | |||
--length |
-l |
Salt length for edge API key | getEdgeApiKey |
--linkedData |
Include Linked Data module in UI bundle | buildAndPushUi, deployUi, buildUi | |
--moduleName |
-n |
Module name (e.g. mod-orders) | interceptModule, listModules, |
| listModuleVersions, | |||
| undeployModule, updateModuleDiscovery, | |||
| upgradeModule | |||
--modulePath |
Module path (e.g. path to module in IntelliJ) | upgradeModule | |
--moduleType |
-y |
Filter by module type | listModules |
--moduleUrl |
-m |
Module URL | interceptModule |
--moduleVersion |
Module version (e.g. 13.1.0-SNAPSHOT.1093) | upgradeModule | |
--namespace |
DockerHub namespace | buildAndPushUi, upgradeModule | |
--platformLspURL |
Platform LSP UI URL | buildAndPushUi, deployUi, | |
| updateKeycloakPublicClients, buildUi | |||
--privatePort |
Private port | updateModuleDiscovery | |
--purgeSchemas |
Purge PostgreSQL schemas on uninstallation | removeTenantEntitlements, | |
| undeployApplication | |||
--removeApplication |
Remove application from the DB | undeployApplication | |
--restore |
-r |
Restore module & sidecar | interceptModule, updateModuleDiscovery |
--sidecarUrl |
-s |
Sidecar URL | interceptModule, updateModuleDiscovery |
--singleTenant |
Use for Single Tenant workflow | deployUi, buildAndPushUi, buildUi | |
--skipApplication |
Skip application operations | upgradeModule | |
--skipCapabilitySets |
Skip refreshing capability sets | undeployApplication | |
--skipModuleArtifact |
Skip building module artifact (jar and module descriptor) | upgradeModule | |
--skipModuleDeployment |
Skip module & sidecar deployment | upgradeModule | |
--skipModuleDiscovery |
Skip module discovery update | upgradeModule | |
--skipModuleImage |
Skip building module Docker image | upgradeModule | |
--skipRegistry |
Skip retrieving latest registry module versions | interceptModule, deployApplication, | |
| deployManagement, deployModules, | |||
| upgradeModule | |||
--skipTenantEntitlement |
Skip tenant entitlement operations | upgradeModule | |
--skipUi |
Skip UI build and deployment | deployApplication | |
--tenant |
-t |
Tenant name | getKeycloakAccessToken, getEdgeApiKey, |
| buildAndPushUi | |||
--tokenType |
Token type | getKeycloakAccessToken | |
--updateCloned |
-u |
Update Git cloned projects | deployApplication, deployUi, |
| buildAndPushUi, buildUi | |||
--user |
-x |
User for edge API key generation | getEdgeApiKey |
--versions |
-v |
Number of versions to display | listModuleVersions |
eureka-cli -c ./config.combined.yaml deployApplication
If no profile or config file is passed, the combined profile will be inferred and used.
- Use the debug
-dflag to troubleshoot your environment deployment and see how the CLI interacts with services by logging in verbose mode
eureka-cli deployApplication -d
- For resource-constrained environments, use the
-qflag to deploy only the required system containers
eureka-cli deployApplication -q
- The profile
-pflag eliminates the need to define a config file path by relying on the default configs that are automatically created in the.eurekahome directory
eureka-cli -p combined deployApplication
Available profiles are: combined, combined-native, combined-native-otel, export, search, edge, erm, ecs, ecs-single, ecs-migration and import (combined, combined-native, combined-native-otel, ecs, ecs-single, ecs-migration and import are standalone applications).
- It can be combined with the
-oflag to overwrite all existing files in the.eurekahome directory to receive changes from upstream
eureka-cli -p combined deployApplication -oq
# Can be simplified without specifying the profile (combined is the default profile)
eureka-cli deployApplication -oq

Deploys the system without optional containers depending on the profile, such as netcat, kafka-ui, garage, opensearch, opensearch dashboards and ftp-server.
- In case you want to update your local repository of platform-lsp (UI), you can do so with the combined
-buflags
eureka-cli deployApplication -bu
This will update the cloned projects and force-build Docker images locally before deploying the environment.
- System containers can also be built or rebuilt separately from environment deployment. This is particularly useful if you want to verify the images without a full deployment
eureka-cli buildSystem
Undeploy the combined application
eureka-cli undeployApplication
This removes all containers and deletes the system data volumes (PostgreSQL, Vault, Kafka, Garage, FTP, OpenSearch plugins), so the next deployment starts from scratch and re-runs tenant initialization. To keep the data volumes, add the
--keepVolumes(-k) flag; see Preserve data volumes across redeployments.
Preserve data volumes across redeployments
- To keep the system data volumes during teardown for a faster redeployment, add the
--keepVolumes(-k) flag
eureka-cli undeployApplication -k
All containers are removed, but tenants, entitlements, users, roles and other data survive in the preserved volumes. The next
deployApplicationdetects the existing state and skips tenant initialization, application creation and entitlement setup, which makes it considerably faster than a cold deployment.
- To restart the whole environment in one command while preserving data, combine
--cleanupwith-k
eureka-cli deployApplication --cleanup -k
- Scope and caveats:
- The flag only has an effect where the system teardown runs: standalone profiles,
undeploySystem, anddeployApplication --cleanup. Undeploying a child application (e.g.-p erm) never removes the system data volumes, so-kis accepted but changes nothing there. - Anonymous container volumes are left behind as orphans when
-kis used; rundocker volume pruneoccasionally to clean them up. - Retained volumes are expected to match the redeployed application version. Retaining volumes across an incompatible version change (e.g. a schema migration) can cause confusing errors on the next deployment; when in doubt, undeploy without
-kand start clean. - Note that a redeploy re-resolves module versions from the registry, so newer snapshot versions published since the teardown are picked up automatically (already-present images are never re-pulled, but new version tags are). To redeploy exactly the module set that created the retained data, pass
--skipRegistry.
- The flag only has an effect where the system teardown runs: standalone profiles,
Deploy the combined application from AWS ECR
To use AWS ECR as your container registry instead of the public Folio DockerHub, set AWS_ECR_FOLIO_REPO in your environment. When this environment variable is defined, it is assumed that this repository is private and you have also defined credentials in your environment. The value of this variable should be the URL of your repository.
- Set AWS credentials explicitly
export AWS_ACCESS_KEY_ID=<access_key>
export AWS_SECRET_ACCESS_KEY=<secret_key>
export AWS_ECR_FOLIO_REPO=<repository_url>
eureka-cli deployApplication
- Reuse stored AWS credentials found in
~/.aws/config
export AWS_ECR_FOLIO_REPO=<repository_url>
AWS_SDK_LOAD_CONFIG=true eureka-cli deployApplication
See docs/AWS_CLI_SETUP_GUIDE.md to prepare AWS CLI beforehand.
Deploy the ecs application
The ecs application is a standalone application that deploys a UI container for each consortium. By default, it creates 3 tenants for the first consortium (ecs) and 2 tenants for the second one (ecs2). This profile also deploys mod-okapi-facade and mod-search modules along with the opensearch system container.
eureka-cli -p ecs deployApplication -oq
Undeploy the ecs application
eureka-cli -p ecs undeployApplication
Deploy the ecs-single application
We can use the ecs-single profile to deploy an environment with just a single consortium. This profile will provision only 3 tenants (i.e. ecs, university and college) with just a single UI container. This profile is preferred over the ecs when there is an obvious resource constraint on the host machine because of how many Kafka topics need to be maintained during and after tenant entitlement.
eureka-cli -p ecs-single deployApplication -oq
Undeploy the ecs-single application
eureka-cli -p ecs-single undeployApplication
Deploy the ecs-migration application
The ecs-migration profile deploys a single consortium with one central and eight member tenants. This profile is primarily used to cross-check production workflows when there are issues reported with the migration to ECS. Because of the number of tenants involved, it is advised to run this profile with the maximum amount of resources to avoid frequent restarts on failure.
eureka-cli -p ecs-migration deployApplication -oq
Undeploy the ecs-migration application
eureka-cli -p ecs-migration undeployApplication
Run IDP migration
# Create/remove IDP users in Keycloak
./misc/scripts/migrate_users.sh -a {{create OR delete}} --consortium-id {{consortium uuid}}
The migration progress can be monitored from the
federated_identitytable in the Keycloak DB.
Deploy the import application
The import application is another standalone application that contains a combination of modules required by the team responsible for data-import functionality in FOLIO.
eureka-cli -p import deployApplication -oq
Undeploy the import application
eureka-cli -p import undeployApplication
Deploy child applications
The CLI also supports deploying child applications on top of existing ones. The deployApplication command behaves differently when application.dependencies is set in the config file.
Deploy the export application
- This application contains modules and system containers required for data export functionality that relies on Garage (S3-compatible object storage) and FTP
eureka-cli -p export deployApplication

Deploy the search application
- The search application provides OpenSearch capabilities required by the Inventory App and ECS setup
eureka-cli -p search deployApplication

Deploy the edge application
- The edge application provides modules with an included mod-okapi-facade to work with the Edge API, Karate tests, or with Mosaic integration
eureka-cli -p edge deployApplication

Deploy the erm application
- The erm application contains modules, including mod-okapi-facade, for working with the ERM, OA and Serials
eureka-cli -p erm deployApplication
Undeploy child applications
- All child applications can be undeployed with the same
undeployApplicationcommand, which will remove both the modules and system containers used by the app
eureka-cli -p {{profile}} undeployApplication
Replace
{{app}}or{{profile}}with either of the supported child profiles: export, search, edge or erm.
Intercept a module
The Intercept command allows rerouting traffic from a Kong service to a custom sidecar before reaching your local instance started in IntelliJ.
- Start your local module instance in IntelliJ with correct environment variables and JVM flags

- Enable interception to instance in IntelliJ by deploying a custom sidecar
# With custom module and sidecar URLs
eureka-cli interceptModule -n mod-orders -m http://host.docker.internal:36002 -s http://host.docker.internal:37002
# With default module and sidecar URLs (i.e. only with ports)
eureka-cli interceptModule -n mod-orders -gm 36002 -s 37002
# With a different profile, e.g. using the import profile
eureka-cli -p import interceptModule -n mod-orders -gm 36002 -s 37002

- Create and open a new order in the Orders app

- Check the logs of the instance started in IntelliJ; it should now be receiving traffic from the custom sidecar

- Disable interception by redeploying the default containers of both the module and the sidecar into the environment
eureka-cli interceptModule -n mod-orders -r
# With a different profile, e.g. using the import profile
eureka-cli -p import interceptModule -n mod-orders -r
# Restore using the locally built module image, saved with foliolocal namespace
eureka-cli interceptModule -n mod-orders -r --namespace foliolocal

To intercept multiple modules, make sure to use the right set of environment variables, JVM flags and instance ports for each target module.
Create a port proxy
Create a port proxy (Windows only) to route traffic to a specific deployed sidecar container. This command can help resolve some HTTP client issues in some modules when intercepted by the interceptModule command.
- To create a proxy between your instance deployed in IntelliJ and some sidecar in the environment, pass the module name to which the sidecar is associated with (e.g. mod-inventory-storage), and the external port number of the HTTP server on the sidecar
# Route the traffic from mod-inventory-storage-sc.eureka:8082 on the host network to host.docker.internal:37002 deployed as a container
# both the gateway hostname, i.e. host.docker.internal as well as the sidecar internal port 8082 can be overridden by the command
eureka-cli createPortProxy -n mod-inventory-storage -s 37002
This command assumes that the host, e.g.
mod-inventory-storage-sc.eurekais added to/etc/hostsbeforehand, because on some corporate machines scripted addition of hosts can be banned by group policies.
Upgrade a module
The upgrade command can upgrade or downgrade a module to any SNAPSHOT or released version. Maven-based modules require the Maven CLI to be configured globally; Gradle-based modules are auto-detected and built with the Gradle Wrapper of the module repository.
- To upgrade a module, pass the module name together with the path to the cloned repository, which we will use to build the artifact before making the container image
# Here we upgrade mod-orders from 13.1.0-SNAPSHOT.1093 to 13.1.0-SNAPSHOT.1094
eureka-cli -p combined-native upgradeModule -n mod-orders --modulePath ~/Folio/folio-modules/mod-orders
The version is automatically resolved and incremented; image will be built with foliolocal namespace.

- If you want to downgrade back to an already prebuilt version (i.e. those that were downloaded from the Docker Hub or AWS ECR), set the desired version together with the correct namespace (e.g. folioci)
# Here we downgrade mod-orders from 13.1.0-SNAPSHOT.1094 back to 13.1.0-SNAPSHOT.1093
eureka-cli -p combined-native upgradeModule -n mod-orders --moduleVersion 13.1.0-SNAPSHOT.1093 --namespace folioci --modulePath ~/Folio/folio-modules/mod-orders
We set namespace to folioci in order to use the existing Docker Hub image.

In both cases, the application patch version will be incremented, as this does not have any functional reason for the CLI to roll back. Additionally, if the application is associated with multiple tenant entitlements, all of them will be upgraded to the latest version.
- The upgrade command also has other flags that can be passed in order to skip a certain step in the process.

Run a local module
Whereas upgradeModule changes a module that is already part of the deployed application, runLocalModule builds and runs a brand-new backend module straight from a local source folder - a module that is not registered in FOLIO LSP/FAR (a private-repo module, a fork, or one that has not yet been added to platform-lsp). This lets you develop, integration-test and demo such modules without publishing them first.
The new module is layered into the environment through a dedicated child application (default app-local) that depends on the currently deployed base application, following the same child-app pattern used by app-export, app-edge, app-search and app-erm. Build prerequisites are the same as upgradeModule: the Maven CLI for Maven modules, a checked-in Gradle Wrapper for Gradle/Grails modules, and a running Docker daemon.
- Point the command at the module repository root; the module name and version are auto-detected from the build file (
pom.xml/build.gradle(.kts)/ Grailsgradle.properties). The image is built with thefoliolocalnamespace and the module version is SNAPSHOT-incremented, exactly asupgradeModuledoes.
# Build foliolocal/mod-private:<version>, deploy the module + sidecar, create app-local-1.0.0
# (dependencies: [app-combined-1.0.0]), register Kong discovery and entitle the configured tenants
eureka-cli runLocalModule --modulePath ~/repos/mod-private
The first run creates
app-local-1.0.0(consistent with the base application's1.0.0); every subsequent run patch-bumps it (1.0.1,1.0.2, ...) and upgrades the tenant entitlements to the new version.
- Override the auto-detected identity or the owning application with the optional flags. Re-running against the same
--applicationNameadds the module (or replaces it if the same name already exists) and bumps the local application version.
# Override module name/version and use a custom local application name
eureka-cli runLocalModule --modulePath ~/repos/mod-private --moduleName mod-private --moduleVersion 1.2.0-SNAPSHOT.5 --applicationName app-local
# Add a second private module - both coexist under a single new app-local version
eureka-cli runLocalModule --modulePath ~/repos/mod-another
If the module name collides with a module already provided by the base application, the command fails fast with
<name> is already provided by application <base app>; use upgradeModule to change its versionand changes nothing.
-
The command supports the same
--skip*step flags asupgradeModule(--skipModuleArtifact,--skipModuleImage,--skipModuleDeployment,--skipApplication,--skipModuleDiscovery,--skipTenantEntitlement) for iterative development, plus--cleanupto revert the build artifact after a successful run. -
If a run fails during Kong discovery registration or tenant entitlement, it is rolled back the same way
upgradeModulerolls back: the just-createdapp-localversion is removed (the previous version, if any, is left intact) and the command exits non-zero with the underlying error. The deployed module + sidecar containers are left running (eachrunLocalModulereplaces them anyway, undeploying the pair before redeploying), so simply re-run the command once the underlying issue is fixed. -
To tear everything down, point
undeployApplicationat the local application with--applicationName(use the same profile you deployed under, so the container names match). The module + sidecar containers, theapp-localapplication, its tenant entitlements and Kong discovery entries are removed, while the base application is left untouched.
eureka-cli undeployApplication --applicationName app-local
Other commands
The CLI includes several useful commands to enhance developer productivity. Here are the most important ones that can be used independently.
- List deployed system containers
eureka-cli listSystem
- List deployed modules
# Using the current profile
eureka-cli listModules
# For a particular module and its sidecar in a profile
eureka-cli listModules -n mod-orders
# For only modules in a profile
eureka-cli listModules -y module
# For only sidecars in a profile
eureka-cli listModules -y sidecar
# For only management modules
eureka-cli listModules -y management
# Using all modules for all profiles including mgr-*
eureka-cli listModules -a
- List the available module versions in the registry or fetch a specific module descriptor by version
# List versions for a module
eureka-cli listModuleVersions -n edge-orders
# Get module descriptor for a particular version
eureka-cli listModuleVersions -n edge-orders -i edge-orders-3.3.0-SNAPSHOT.88
# Get module descriptor for a particular version but specify the number of versions to display (the default value is 5)
eureka-cli listModuleVersions -n edge-orders -i edge-orders-3.3.0-SNAPSHOT.88 -v 10
- Get current Vault Root Token used by the modules
eureka-cli getVaultRootToken
- Get Keycloak Access Token for a tenant
eureka-cli getKeycloakAccessToken -t diku
- Get an Edge API key for a user and tenant
eureka-cli getEdgeApiKey -t diku -x diku_admin
- Reindex inventory and instance record OpenSearch indices
eureka-cli -p {{profile}} reindexIndices
This command assumes that mod-search module and opensearch system container are deployed or if
{{profile}}is being replaced by either search, ecs, ecs-single or ecs-migration profiles.
- Check if module internal ports are accessible
eureka-cli checkPorts

The CLI also exposes an internal port 5005 for all modules and sidecars that can be used for remote debugging in IntelliJ.
Using a custom folio-module-sidecar
If your workflow relies on a custom implementation of folio-module-sidecar, the CLI also supports deploying an environment with sidecars using a custom Docker image.
- Git clone folio-module-sidecar from GitHub
git clone https://github.com/folio-org/folio-module-sidecar.git
- Apply your changes locally & build a custom local image
cd folio-module-sidecar
mvn clean package -DskipTests
docker build --tag custom-folio-module-sidecar:1.0.0 .
This example uses a non-native image build, see https://github.com/folio-org/folio-module-sidecar/blob/master/README.md for how to build a native Docker image.
- Use the newly built
custom-folio-module-sidecar:1.0.0local image in your config by settingsidecar-module.custom-namespaceto true
sidecar-module:
image: custom-folio-module-sidecar
version: 1.0.0
custom-namespace: true
The
versionmust be set explicitly.
- Deploy the environment with this config, in our example we deploy an edge application with
custom-folio-module-sidecar:1.0.0sidecars
eureka-cli -p edge deployApplication
- Check the sidecar image version after application deployment
eureka-cli -p edge listModules
Using a native folio-module-sidecar
The CLI also supports using native sidecars built with GraalVM. These sidecars have a different and lighter resource profile, making them ideal for deployments with only a few resources to spare.
- Git clone folio-module-sidecar from GitHub
git clone https://github.com/folio-org/folio-module-sidecar.git
- Build the artifact locally
cd folio-module-sidecar
mvn clean install -Pnative -DskipTests \
-Dquarkus.native.remote-container-build=true \
-Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21
This builds a native binary using a container-based GraalVM with a Linux toolchain.
- Build a custom local Docker image
docker build -f docker/Dockerfile.native-micro -t folio-module-sidecar-native .
- Use the newly built
folio-module-sidecar-native:latestlocal image in your config by settingsidecar-module.custom-namespaceto true andsidecar-module.native-binary-cmdwith valid args
sidecar-module:
image: folio-module-sidecar-native
version: latest
custom-namespace: true
native-binary-cmd: [
"./application", "-Dquarkus.http.host=0.0.0.0", "-Dquarkus.log.category.'org.apache.kafka'.level=INFO"
]
The
versionmust be set explicitly. If thesidecar-moduleblock is already configured for your profile, then disregard this last step.
- Deploy the environment using the combined-native profile that already has
JAVA_OPTIONSenv var removed from thesidecar-modulein the config, as this is not interpreted by the substrate VM in the image
eureka-cli -p combined-native deployApplication
Using local backend module images
When developing backend modules locally, you can deploy them with custom module descriptors without pushing to a registry.
- Build your module locally and generate a module descriptor
cd <module-directory>
mvn clean package -DskipTests
docker build --tag <module-name>:<version> .
- Configure the module to use the local descriptor in your config file
backend-modules:
<module-name>:
version: "<version>"
local-descriptor-path: "/path/to/module/target/ModuleDescriptor.json"
When
local-descriptor-pathis specified, the Docker image will not be pulled from a registry and the descriptor will be loaded from the local filesystem.
- Deploy the environment with your local module
eureka-cli deployApplication
Using local frontend module descriptors
- To use a local frontend module descriptor, add
local-descriptor-pathto the module config
frontend-modules:
folio_users:
version: "<version>"
local-descriptor-path: "/path/to/ui-module/module-descriptor.json"
- Deploy the environment with your local module
eureka-cli deployApplication
Using a backend module namespace
The namespaces.backend-modules config key sets the image namespace of all backend modules of the profile, for images hosted outside the FOLIO Docker Hub namespaces (a private mirror or the external registry of a third-party application).
namespaces:
backend-modules: docker.libsdev.k-int.com/knowledgeintegration
- Backend module images are then pulled as
{{namespace}}/{{module}}:{{version}}, e.g.docker.libsdev.k-int.com/knowledgeintegration/mod-ill:1.11.0-SNAPSHOT.278 - The key takes precedence over
AWS_ECR_FOLIO_REPO: backend module images are pulled from the namespace with the credentials stored bydocker login(see Using private registry credentials) instead of the ECR token, while the sidecar image still comes from ECR when it is configured; the UI image is not affected - If it is omitted, images are pulled from
folioci(snapshots) orfolioorg(releases)
Using private registry credentials
Image pulls use the credentials stored by docker login for the registry host of each image, read from ~/.docker/config.json (or $DOCKER_CONFIG/config.json), including credentials kept in a credsStore or per-host credHelpers credential helper.
docker login docker.libsdev.k-int.com
eureka-cli deployApplication
- No manual
docker pullis needed for images hosted on a private registry - Images without stored credentials for their host are pulled anonymously, as before
- When
AWS_ECR_FOLIO_REPOis set, the ECR authorization token is used instead, except for backend module images innamespaces.backend-modules, which use the stored credentials
Using a third-party application
A third-party application is one whose descriptor and images are not published in the FOLIO registries and Docker Hub namespaces, whether it comes from a service provider, a consortium or an in-house project. It is deployed from its own child-application profile, the same way such an application is registered in a real Eureka deployment. The profile names the application, its external registry and its external image namespace, so the environment ends up with stock FOLIO images next to images from the third party's Docker registry.
profile:
name: ill
application:
name: app-ill
version: 1.0.0
descriptor: ~/app-ill-1.0.0.json
dependencies:
name: app-combined
version: 1.0.0
lsp:
url: https://raw.githubusercontent.com/folio-org/platform-lsp/refs/heads/snapshot/platform-descriptor.json
registry:
url: https://folio-registry.k-int.com
namespaces:
backend-modules: docker.libsdev.k-int.com/knowledgeintegration
backend-modules:
mod-ill:
private-port: 8080
frontend-modules:
k-int_ill-ui:
docker login docker.libsdev.k-int.com
eureka-cli -p combined deployApplication
eureka-cli -p ill deployApplication
application.descriptoris a local file path (~and$HOMEare expanded) or anhttp(s)URL of an application descriptor, the same JSON that is registered inmgr-applications. ItsmodulesanduiModulesreplace the platform applications and FAR as the module inventory of the profile;far.urlis not consulted and the~/.eureka/modules.jsoncache is bypassed. Only modules also listed underbackend-modules/frontend-modulesare deployed, as with any profile. Module descriptors embedded in the application descriptor (moduleDescriptors,uiModuleDescriptors) are registered inline; other modules are registered with the descriptorurlgiven for them in the application descriptor (as with the platform's applications from FAR), or one derived fromregistry.urlwhen there is none, or fetched from that URL whenapplication.fetch-descriptorsistrue.- The descriptor's
dependenciesblock is ignored.application.dependenciesin the profile names the parent applications that exist in the local installation, because profile names and versions are managed by hand. lsp.urlis still read for the platform'seureka-components, so the sidecar version resolves as for every other profile and stays consistent with the base platform. Iflsp.urlis not set, there is no source for it andsidecar-module.versionmust be set explicitly.- Module versions with semver build metadata (
1.11.0-SNAPSHOT.278+1787670557413-d0e2d3d) are not supported: Docker rejects+in an image tag, and Eureka cannot entitle such a module because the gateway service is named after the module ID and rejects+as well. The CLI passes versions through unchanged; the descriptor must use versions without build metadata. namespaces.backend-modulespoints at the external image namespace (see Using a backend module namespace) anddocker loginsupplies the pull credentials (see Using private registry credentials).
Using the UI
The environment depends on the platform-lsp project to combine and assemble frontend modules into a single UI package. No official prebuilt UI image is published to any registry, so by default the CLI builds the UI image locally from the platform-lsp repository.
- The image
{{platform}}-ui-{{tenant}}(platform-lsp-ui-dikuby default; the platform part is the name of the repository the UI is built from) is built automatically duringdeployApplicationordeployUifor every tenant withdeploy-ui: truein the config, and is reused on subsequent runs - To force a rebuild (e.g. to include the latest module versions), pass the
-bflag, optionally combined with-uto first update the local platform-lsp repository, which brings the checkout tostripes-branchas it is on origin and discards local changes (see Building the UI from a platform fork)
# Will rebuild the platform-lsp image; a running UI container is kept, see below
eureka-cli deployUi -b -u
# Will do the same during a full deployment
eureka-cli deployApplication -b -u
- To replace a deployed UI container with a newly built image, remove the old container first
eureka-cli undeployUi
eureka-cli deployUi -b
- To build the image without touching any containers, use
buildUi; run it beforedeployApplicationto keep the memory-heavy UI build from competing with a running platform
eureka-cli buildUi -u
- To skip the UI entirely (e.g. for backend-only development), set
deploy-ui: falsefor the tenant in the config or pass--skipUitodeployApplication
eureka-cli deployApplication --skipUi
- To pull a prebuilt UI image from a registry instead of building locally, set the
namespaces.platform-lsp-uikey in the config; the CLI will then pull{{namespace}}/{{platform}}-ui-{{tenant}}on every deployment instead of building, unless-bis passed, which builds locally. ThebuildAndPushUicommand builds such an image and pushes it to a namespace of your choice.
namespaces:
platform-lsp-ui: my-namespace
eureka-cli buildUi -u
The UI build is memory-hungry (the node process can peak at around 8 GB). Run
buildUibeforedeployApplicationso the build does not compete with a running platform for memory. If the build is too heavy for your machine altogether, fork this repository, add aDOCKERHUB_TOKENsecret, and dispatch theBuild And Push UIworkflow to build the image on GitHub-hosted runners instead. Then setnamespaces.platform-lsp-uito that namespace as shown above, and the CLI will pull the image rather than build it.
Building the UI from a platform fork
The repository, branch and Stripes config the UI is built from are set per profile; all three are optional and default to the upstream platform-lsp snapshot branch and its stripes.config.js. Point them at a fork, e.g. a vendor platform that adds its own UI modules, to build that platform instead:
application:
stripes-url: https://example.org/vendor/platform-fork.git # default https://github.com/folio-org/platform-lsp.git
stripes-branch: stable # default snapshot
stripes-config: stripes.local-eureka.config.js # default stripes.config.js
- The image is named after the repository, e.g.
platform-fork-ui-diku, so repositories with different names keep separate images and switching between them reuses the image that exists; a fork that keeps the upstream name sharesplatform-lsp-ui-{{tenant}}with upstream. Pass-b -uto rebuild from the configured repository and branch, e.g. after changingstripes-branchor switching between same-named repositories;-balone rebuilds the checkout as it is. A configurednamespaces.platform-lsp-ui(see above) pulls instead of building, unless-bis passed, which builds locally. - The local checkout in
~/.eureka/misc/platform-lspmust come from the configured repository. If it was cloned from another one, the build stops with an error naming both repositories; pass-uto replace the checkout, which discards everything in it. - Without
-u, the checkout is built as it is: local branches, commits and edits are kept, and a warning names the checked-out branch if it differs fromstripes-branch. With-u, the checkout is brought tostripes-branchas it is on origin: the branch is fetched and checked out, untracked files and uncommitted changes are discarded, other local branches are kept. - A
file://URL works as well, e.g. to build from a bare repository on the same machine - The fork must keep the platform-lsp layout the build relies on:
docker/Dockerfile, a Stripes config (stripes.config.js, or the filestripes-configselects) whose${...}placeholders are among the ones the CLI substitutes (an unknown placeholder fails the build, a file without placeholders is used as is), and apackage.jsonwhosebuildscript buildsstripes.config.js, as the platform-lsp one does; the CLI edits these files in the build copy and relies on the build reading them. The optional modules@folio/consortia-settingsand@folio/ld-folio-wrapperare removed fromstripes.modules.jsandpackage.jsonif present, unless--linkedDataor a multi-tenant deployment keeps them. A fork that keeps its modules inline in its Stripes config needs nostripes.modules.js; inline module lists are not filtered, so omit@folio/consortia-settingsfor single-tenant builds and@folio/ld-folio-wrapperwhen--linkedDatais disabled. All edits happen in a temporary build copy, the checkout is never modified.package.jsonis rewritten only when one of the two modules was actually removed, with every other field kept (the rewritten file has its keys sorted); otherwise it is used as it is. - The build substitutes and builds
stripes.config.js. For a fork that keeps several Stripes configs,application.stripes-confignames another file of the repository (path relative to its root) to build instead: it is substituted and written asstripes.config.jsin the build copy, the same placeholder rules apply, and a file that is not in the repository fails the build. - To switch a running environment to a fork, build first so a failing build leaves the running UI alone, then replace the container, with
namespaces.platform-lsp-uiunset, otherwisedeployUipulls instead of using the image just built.undeployUiremoves the UI containers of every tenant the platform knows, not only those of the current profile.
eureka-cli buildUi -u
eureka-cli undeployUi
eureka-cli deployUi
One UI bundle per tenant
The UI container eureka-platform-lsp-ui-{{tenant}} is one per tenant, and its image is built from one repository. Child application profiles register their UI modules through frontend-modules for entitlement but do not contribute to the bundle and should not set deploy-ui: for a tenant whose UI is already deployed, a second profile reuses the existing image and skips the running container. A fork therefore has to contain the UI modules of every application it hosts, the base ones included; with several third-party applications one fork carries all of them.
The UI is owned by the profile that deploys the platform.
Using Single Tenant UX
Single tenant UX is enabled by default for ecs, ecs-single and ecs-migration profiles. This functionality allows users in member tenants to automatically log in to their respective tenant spaces from a single user login form configured for the central tenant. Single Tenant UX uses shadow users created in the central tenant and the Keycloak realm to perform authentication with the correct member tenant identity provider.
- To disable this functionality from running automatically during the deployment, set
SINGLE_TENANT_UXfromtruetofalsefor bothmod-users-keycloakandmod-consortia-keycloakbackend modules.
Using the environment
- Access the UI from
http://localhost:3000usingdiku_adminusername andadminpassword

- After successful login, the UI can be used like any other FOLIO application

If your environment was deployed using the ecs profile, your consortiums are represented by two different UI instances.
- The first instance can be accessed from
http://localhost:3000usingecs_adminusername andadminpassword

It contains 3 tenants, ecs central tenant and university and college member tenants.
- And the second one can be accessed from
http://localhost:3001(via an incognito Chrome browser) usingecs_admin2username andadminpassword

It contains 2 tenants, ecs2 central tenant and university2 member tenant.
- Kong gateway is available at
http://localhost:8000and can be used to get an access token directly from the backend
# Admin user: diku_admin
curl --request POST \
--url http://localhost:8000/authn/login-with-expiry \
--header 'Content-Type: application/json' \
--header 'X-Okapi-Tenant: diku' \
--data '{"username":"diku_admin","password": "admin"}' \
--verbose
# Limited user: diku_user
curl --request POST \
--url http://localhost:8000/authn/login-with-expiry \
--header 'Content-Type: application/json' \
--header 'X-Okapi-Tenant: diku' \
--data '{"username":"diku_user","password": "user"}' \
--verbose
Use combined, combined-native, combined-native-otel, or import standalone profiles, because these profiles create diku_admin and diku_user users.
# Admin user: ecs_admin
curl --request POST \
--url http://localhost:8000/authn/login-with-expiry \
--header 'Content-Type: application/json' \
--header 'X-Okapi-Tenant: ecs' \
--data '{"username":"ecs_admin","password": "admin"}' \
--verbose
# Admin user: ecs_admin2
curl --request POST \
--url http://localhost:8000/authn/login-with-expiry \
--header 'Content-Type: application/json' \
--header 'X-Okapi-Tenant: ecs2' \
--data '{"username":"ecs_admin2","password": "user"}' \
--verbose
Use an ecs profile that creates ecs_admin and ecs_admin2 users in different consortiums.
Using template environment variables
The template-environment config key works like environment but supports per-module placeholder resolution. Values containing {{.ModuleName}} are resolved to the module's name at deploy time.
Supported placeholder: {{.ModuleName}} — replaced with the module name (e.g. mod-orders)
template-environment:
OTEL_SERVICE_NAME: "{{.ModuleName}}"
- Per-module
environmententries inbackend-modulescan override template values (they appear later in the env slice) - If
template-environmentis omitted from a profile, no template env vars are injected
Using per-sidecar environment variables
Use [my backend module].sidecar-environment config key to define a unique set of environment variables to be used on a sidecar. These environment variables will override the global sidecar environment as defined in sidecar-module.environment.
backend-modules:
mod-scheduler:
sidecar-environment:
ROUTING_DYNAMIC_ENABLED: "true"
SIDECAR_FORWARD_UNKNOWN_REQUESTS: "false"
Using extra volumes
The extra-volumes config key accepts a list of volume mounts applied to all backend modules.
extra-volumes:
- /host/path:/container/path
- Extra volumes are prepended to any per-module
volumesentries - If
extra-volumesis omitted, no additional volumes are mounted
Using OpenTelemetry LGTM stack
OpenTelemetry LGTM is a docker image that combines OpenTelemetry Collector with Grafana UI, Grafana Loki, Grafana Tempo, Prometheus and Pyroscope. Use this image with the OpenTelemetry instrumentation agent to deploy an environment with advanced logging, tracing and metrics collection enabled in a few steps.
- Download and configure the latest OpenTelemetry instrumentation agent (the latest tested is v2.25.0)
mkdir -p $HOME/eureka
rm -rf $HOME/eureka/opentelemetry-javaagent.jar
curl -L -o $HOME/eureka/opentelemetry-javaagent.jar https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.25.0/opentelemetry-javaagent.jar
Check GitHub releases page to use a different version of the agent: https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases.
- Add extra volumes in the config file
extra-volumes:
- $HOME/eureka/opentelemetry-javaagent.jar:/etc/otel/opentelemetry-javaagent.jar:ro
- Add OTEL_SERVICE_NAME template environment in the config file
template-environment:
OTEL_SERVICE_NAME: "{{.ModuleName}}"
- Add a new
javaagentVM option to the JAVA_OPTS global environment variable in the config file (use existingenvironmentandJAVA_OPTSkeys)
environment:
...
# Java
JAVA_OPTIONS: >-
...
-javaagent:/etc/otel/opentelemetry-javaagent.jar
...
- Add extra global environment variables to the config file (use existing
environmentkey)
environment:
...
# OpenTelemetry
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-lgtm:4317
OTEL_EXPORTER_OTLP_PROTOCOL: grpc
OTEL_METRICS_EXPORTER: otlp
OTEL_LOGS_EXPORTER: otlp
...
- Start your profile of choice as normal and then deploy OpenTelemetry LGTM service separately
# Deploy a profile of choice, e.g. combined-native-otel
eureka-cli deployApplication -p combined-native-otel -oq --cleanup
# Deploy OpenTelemetry LGTM service
docker compose -p eureka -f ~/.eureka/misc/docker-compose.otel.yaml up -d
Make sure the native sidecar image is already built on your machine before deploying any of the
combined-native*profiles.
- Verify that the containers were deployed and contain
opentelemetry-javaagent.jaras a volume mount
docker inspect --format='{{json .Mounts}}' eureka-mgr-tenants | jq .

Add missing Vault secrets
The environment may fail to add Vault secrets during tenant entitlement. If a secret is missing, you can add it post-deployment with the add_missing_secret.sh script.
- Add the missing
mod-userssecret
# Single user
./misc/scripts/add_missing_secret.sh -t diku -u admin
# Multiple users (space-delimited)
./misc/scripts/add_missing_secret.sh -t diku -u "mod-users mod-roles-keycloak mod-inventory"
This script upserts the secret to Vault and resets the associated Keycloak user password in the specified realm (it is idempotent).
Troubleshooting
General
If there are multiple instances of a container daemon (e.g. Rancher Desktop, Docker Desktop, Podman) running on the host machine:
- Verify that
DOCKER_HOSTis set to point to the correct daemon (otherwise/var/run/docker.sockwill be used)
Vault UI does not have a Userpass sign-in method or login fails with admin/admin credentials
- Your local Vault image is outdated, update the git repository of the CLI, rebuild the binary for your platform of choice and run
eureka-cli buildSystem -oonce before deploying any application (-ooverwrites the extracted files under~/.eurekawith the versions embedded in the binary)
Command-based
Shell commands are failing to execute
- Verify that all shell scripts located under
~/eureka/miscfolder are saved using the LF (Line Feed) line break
Module readiness checks are failing
- Verify that host.docker.internal is set in
/etc/hostsand accessible on the host network - Or set
application.gateway-hostnamein the config to either your physical network IP or some locally accessible hostname
"Bind for 0.0.0.0:XXXXX failed: port is already allocated."
- Verify that the config port range is free from running processes (e.g. from some opened Kubernetes NodePort on port 30102)
- Or reassign
application.port-startandapplication.port-endin the config to a different range, e.g. to 20000-20999
"Failed to load module descriptor by url: <https://folio-registry.dev.folio.org/_/proxy/modules/mod-XXX>"
- Verify that the module descriptor for the latest snapshot version already exists in the registry (can check with
eureka-cli listModuleVersionscommand) - Or set a fixed module version in the config (i.e. using
<module>.version)
"The module is not entitled on tenant ..."
- Rerun the deployment again with more available RAM
When trying to deploy with eureka-cli deployApplication -bu or building with eureka-cli buildSystem:
ERROR: unable to select packages:
bind-tools-9.18.41-r0:
breaks: world[bind-tools=9.18.39-r0]
- Update your local CLI git repository and rebuild the binary; our Dockerfiles in the
miscfolder are no longer dependent on pinned and non-deterministic package versions of Alpine Linux - If
deployApplication -bufails onError: worktree contains unstaged changesinstead, simply retry the command
Additional Resources
- Module Development Guide - Comprehensive guide for using
interceptModuleandupgradeModulecommands with workflows, examples, and troubleshooting - Build Binary Guide - Instructions for building platform-specific binaries
- CLI Development Setup Guide - Live compilation and VSCode debugging setup for CLI development
- AWS CLI Setup Guide - Using AWS ECR as container registry
- Windows WSL Guide - Configuring WSL2 on Windows
Documentation
¶
Overview ¶
Copyright © 2026 Open Library Foundation
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.