eureka-cli

command module
v0.0.0-...-7ea8f00 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

README

Eureka CLI

Table of Contents

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 interceptModule and upgradeModule coordinate 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
  • Option 2 - Manual: Add the following entries:
    • 127.0.0.1 postgres.eureka
    • 127.0.0.1 kafka.eureka
    • 127.0.0.1 vault.eureka
    • 127.0.0.1 keycloak.eureka
    • 127.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

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-cli with 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_profile is preferred on Windows because it is auto-sourced)
go install
echo "source <(eureka-cli completion bash)" >> ~/.bash_profile
source ~/.bash_profile

Type eureka-cli and 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 -d flag 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 -q flag to deploy only the required system containers
eureka-cli deployApplication -q
  • The profile -p flag eliminates the need to define a config file path by relying on the default configs that are automatically created in the .eureka home 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 -o flag to overwrite all existing files in the .eureka home 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 

CLI Deploy Combined with Only Required System Containers

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 -bu flags
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 deployApplication detects 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 --cleanup with -k
eureka-cli deployApplication --cleanup -k
  • Scope and caveats:
    • The flag only has an effect where the system teardown runs: standalone profiles, undeploySystem, and deployApplication --cleanup. Undeploying a child application (e.g. -p erm) never removes the system data volumes, so -k is accepted but changes nothing there.
    • Anonymous container volumes are left behind as orphans when -k is used; run docker volume prune occasionally 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 -k and 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.
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_identity table 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

CLI Deploy Export Application

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

CLI Deploy Search Application

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

CLI Deploy Edge Application

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 undeployApplication command, 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

CLI Intercept Module

  • 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

CLI Intercept Module

  • Create and open a new order in the Orders app

CLI Intercept Module

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

CLI Intercept Module

  • 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

CLI Intercept Module

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.eureka is added to /etc/hosts beforehand, 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.

CLI Upgrade Module

  • 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.

CLI Upgrade Module

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.

CLI Upgrade Module

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) / Grails gradle.properties). The image is built with the foliolocal namespace and the module version is SNAPSHOT-incremented, exactly as upgradeModule does.
# 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's 1.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 --applicationName adds 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 version and changes nothing.

  • The command supports the same --skip* step flags as upgradeModule (--skipModuleArtifact, --skipModuleImage, --skipModuleDeployment, --skipApplication, --skipModuleDiscovery, --skipTenantEntitlement) for iterative development, plus --cleanup to 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 upgradeModule rolls back: the just-created app-local version 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 (each runLocalModule replaces them anyway, undeploying the pair before redeploying), so simply re-run the command once the underlying issue is fixed.

  • To tear everything down, point undeployApplication at the local application with --applicationName (use the same profile you deployed under, so the container names match). The module + sidecar containers, the app-local application, 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

CLI Check Ports

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.0 local image in your config by setting sidecar-module.custom-namespace to true
sidecar-module:
  image: custom-folio-module-sidecar
  version: 1.0.0
  custom-namespace: true

The version must be set explicitly.

  • Deploy the environment with this config, in our example we deploy an edge application with custom-folio-module-sidecar:1.0.0 sidecars
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:latest local image in your config by setting sidecar-module.custom-namespace to true and sidecar-module.native-binary-cmd with 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 version must be set explicitly. If the sidecar-module block is already configured for your profile, then disregard this last step.

  • Deploy the environment using the combined-native profile that already has JAVA_OPTIONS env var removed from the sidecar-module in 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-path is 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-path to 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 by docker 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) or folioorg (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 pull is needed for images hosted on a private registry
  • Images without stored credentials for their host are pulled anonymously, as before
  • When AWS_ECR_FOLIO_REPO is set, the ECR authorization token is used instead, except for backend module images in namespaces.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.descriptor is a local file path (~ and $HOME are expanded) or an http(s) URL of an application descriptor, the same JSON that is registered in mgr-applications. Its modules and uiModules replace the platform applications and FAR as the module inventory of the profile; far.url is not consulted and the ~/.eureka/modules.json cache is bypassed. Only modules also listed under backend-modules / frontend-modules are deployed, as with any profile. Module descriptors embedded in the application descriptor (moduleDescriptors, uiModuleDescriptors) are registered inline; other modules are registered with the descriptor url given for them in the application descriptor (as with the platform's applications from FAR), or one derived from registry.url when there is none, or fetched from that URL when application.fetch-descriptors is true.
  • The descriptor's dependencies block is ignored. application.dependencies in the profile names the parent applications that exist in the local installation, because profile names and versions are managed by hand.
  • lsp.url is still read for the platform's eureka-components, so the sidecar version resolves as for every other profile and stays consistent with the base platform. If lsp.url is not set, there is no source for it and sidecar-module.version must 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-modules points at the external image namespace (see Using a backend module namespace) and docker login supplies 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-diku by default; the platform part is the name of the repository the UI is built from) is built automatically during deployApplication or deployUi for every tenant with deploy-ui: true in the config, and is reused on subsequent runs
  • To force a rebuild (e.g. to include the latest module versions), pass the -b flag, optionally combined with -u to first update the local platform-lsp repository, which brings the checkout to stripes-branch as 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 before deployApplication to 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: false for the tenant in the config or pass --skipUi to deployApplication
eureka-cli deployApplication --skipUi
  • To pull a prebuilt UI image from a registry instead of building locally, set the namespaces.platform-lsp-ui key in the config; the CLI will then pull {{namespace}}/{{platform}}-ui-{{tenant}} on every deployment instead of building, unless -b is passed, which builds locally. The buildAndPushUi command 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 buildUi before deployApplication so 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 a DOCKERHUB_TOKEN secret, and dispatch the Build And Push UI workflow to build the image on GitHub-hosted runners instead. Then set namespaces.platform-lsp-ui to 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 shares platform-lsp-ui-{{tenant}} with upstream. Pass -b -u to rebuild from the configured repository and branch, e.g. after changing stripes-branch or switching between same-named repositories; -b alone rebuilds the checkout as it is. A configured namespaces.platform-lsp-ui (see above) pulls instead of building, unless -b is passed, which builds locally.
  • The local checkout in ~/.eureka/misc/platform-lsp must come from the configured repository. If it was cloned from another one, the build stops with an error naming both repositories; pass -u to 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 from stripes-branch. With -u, the checkout is brought to stripes-branch as 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 file stripes-config selects) whose ${...} placeholders are among the ones the CLI substitutes (an unknown placeholder fails the build, a file without placeholders is used as is), and a package.json whose build script builds stripes.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-settings and @folio/ld-folio-wrapper are removed from stripes.modules.js and package.json if present, unless --linkedData or a multi-tenant deployment keeps them. A fork that keeps its modules inline in its Stripes config needs no stripes.modules.js; inline module lists are not filtered, so omit @folio/consortia-settings for single-tenant builds and @folio/ld-folio-wrapper when --linkedData is disabled. All edits happen in a temporary build copy, the checkout is never modified. package.json is 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-config names another file of the repository (path relative to its root) to build instead: it is substituted and written as stripes.config.js in 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-ui unset, otherwise deployUi pulls instead of using the image just built. undeployUi removes 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_UX from true to false for both mod-users-keycloak and mod-consortia-keycloak backend modules.

Using the environment

  • Access the UI from http://localhost:3000 using diku_admin username and admin password

UI Login page

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

UI Main page

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:3000 using ecs_admin username and admin password

UI ECS Main page

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) using ecs_admin2 username and admin password

UI ECS2 Main page

It contains 2 tenants, ecs2 central tenant and university2 member tenant.

  • Kong gateway is available at http://localhost:8000 and 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 environment entries in backend-modules can override template values (they appear later in the env slice)
  • If template-environment is 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 volumes entries
  • If extra-volumes is 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 javaagent VM option to the JAVA_OPTS global environment variable in the config file (use existing environment and JAVA_OPTS keys)
environment:
  ...
  # Java
  JAVA_OPTIONS: >-
    ...
    -javaagent:/etc/otel/opentelemetry-javaagent.jar
  ...
  • Add extra global environment variables to the config file (use existing environment key)
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.jar as a volume mount
docker inspect --format='{{json .Mounts}}' eureka-mgr-tenants | jq .

Inspect OpenTelemetry agent

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-users secret
# 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_HOST is set to point to the correct daemon (otherwise /var/run/docker.sock will 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 -o once before deploying any application (-o overwrites the extracted files under ~/.eureka with the versions embedded in the binary)
Command-based

Shell commands are failing to execute

  • Verify that all shell scripts located under ~/eureka/misc folder are saved using the LF (Line Feed) line break

Module readiness checks are failing

  • Verify that host.docker.internal is set in /etc/hosts and accessible on the host network
  • Or set application.gateway-hostname in 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-start and application.port-end in 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 listModuleVersions command)
  • 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 misc folder are no longer dependent on pinned and non-deterministic package versions of Alpine Linux
  • If deployApplication -bu fails on Error: worktree contains unstaged changes instead, simply retry the command

Additional Resources

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL