Dependency Management
LuCLI can manage CFML libraries and Lucee extensions as dependencies for your project, using the dependencies, devDependencies, and dependencySettings sections in lucee.json. Installed dependencies are recorded in lucee-lock.json only when lock files are enabled (dependencySettings.useLockFile: true).
This page focuses on how dependencies and extensions work, independent of the rest of the lucee.json configuration.
Git dependency caching
LuCLI can manage CFML dependencies and Lucee extensions via the dependencies, devDependencies, and dependencySettings sections in lucee.json. Installed dependencies are recorded in lucee-lock.json when lock files are enabled.
Git dependency caching
Git-based dependencies (source: "git") are installed by cloning their repositories and copying the requested ref (and optional subPath) into the project installPath. To speed up repeated installs, LuCLI maintains a shared git cache under:
~/.lucli/deps/git-cache(or${LUCLI_HOME}/deps/git-cachewhenLUCLI_HOME/-Dlucli.homeis set)
Behavior:
- The first time a given git dependency is installed, LuCLI clones the repository into the cache directory.
- Subsequent installs of the same dependency reuse the cached clone, running
git fetch --tagsto pull the latest refs before checking out the requestedref. - Each cached repository is keyed by name + URL hash, so different dependencies that share the same name but use different URLs will not collide.
- If the cached clone becomes invalid (broken
.gitdir, fetch failure, etc.), LuCLI automatically deletes that cache entry and re-clones it once.
You can control this behavior via a user-level setting in ~/.lucli/settings.json:
usePersistentGitCache(boolean, default:true)- When
true, LuCLI uses the shared cache under~/.lucli/deps/git-cache. - When
false, LuCLI uses a throwaway clone per install and deletes it afterwards (slower, but leaves no cached git repositories on disk).
- When
To manually clear the git cache, use:
lucli deps prune
This removes all cached git clones under ~/.lucli/deps/git-cache.
At a high level:
dependencies– production dependencies (CFML libraries, Lucee extensions, etc.).devDependencies– development-only dependencies.dependencySettings– controls how and where dependencies are installed.dependencySettings.useLockFile– enables/disableslucee-lock.jsonusage for dependency installs (default:false).lucee-lock.json– optional lock file (dependencySettings.useLockFile: true) recording resolved versions, install paths, and mappings.LUCEE_EXTENSIONS– built automatically from locked extension dependencies when the server starts.
Declaring Lucee extensions as dependencies
You can declare Lucee extensions in lucee.json using type: "extension". LuCLI supports several ways to identify the extension:
{
"dependencies": {
"h2": {
"type": "extension",
"id": "465E1E35-2425-4F4E-8B3FAB638BD7280A"
},
"redis": {
"type": "extension",
"slug": "redis"
},
"my-local-ext": {
"type": "extension",
"path": "./extensions/my-local-ext.lex"
},
"custom-provider-ext": {
"type": "extension",
"url": "https://extensions.example.com/my-ext.lex"
}
}
}
Supported forms:
- By ID:
idis a Lucee extension UUID. - By slug/name:
slugor name is resolved vialucee-extensions.json(seesrc/main/resources/extensions/EXTENSION_REGISTRY.md). - By local path:
pathpoints to a.lexfile on disk. - By URL:
urlpoints to a.lexdownload URL.
Installing dependencies (including extensions)
Use the dependency installer command from the project root:
# Install all dependencies and devDependencies
lucli deps install
# Production-only install (skip devDependencies)
lucli deps install --production
# Apply environment-specific dependencySettings & overrides
lucli deps install --env prod
# Preview what would be installed and the realized dependency config for the root project
lucli deps install --dry-run
# Preview dependencies for the root project AND any nested projects that contain a lucee.json
lucli deps install --dry-run --include-nested-deps
Behaviour:
- Reads
dependencies,devDependencies, anddependencySettingsfromlucee.json. - Applies environment overrides (when
--envis used) before resolving dependencies. - Installs supported dependency types:
source: "git"– CFML libraries installed from Git repositories.type: "extension"– Lucee extensions installed via providers, URLs, or local.lexfiles.
- For extension dependencies with
urlorpath, LuCLI materializes the extension file during install (downloads/copies into the dependencyinstallPath) by default. SetdependencySettings.materializeExtensionsOnInstall: falseto use metadata-only/cache-based behavior instead. - When
dependencySettings.useLockFileistrue, writes a normalized record for each dependency intolucee-lock.json(including version, source, install path, and, for extensions, their Lucee ID). - Prints the
LUCEE_EXTENSIONSvalue that will be set when the server starts. When lock files are enabled, this comes fromlucee-lock.json; otherwise LuCLI derives extension activation data directly fromlucee.json.
Dependency mapping → Lucee mapping behavior
When a dependency declares a mapping in lucee.json, LuCLI materializes that as a Lucee mapping in the effective .CFConfig.json used at server startup.
Example:
{
"dependencies": {
"my-framework": {
"type": "cfml",
"version": "4.3.0",
"installPath": "vendor/my-framework",
"mapping": "/framework"
}
}
}
Effective Lucee mapping behavior:
- Virtual mapping keys are normalized with a trailing slash (for example,
/frameworkbecomes/framework/). installPathis resolved to an absolute physical path relative to the project directory when it is not already absolute.- Mappings can come from both
dependenciesanddevDependencies. - If a dependency also exists in
lucee-lock.json, locked install metadata is used as the highest-precedence source when available.
To inspect the realized mappings:
# Preview effective Lucee config (including generated dependency mappings)
lucli server start --dry-run --include-lucee
# Output effective Lucee CFConfig JSON directly
lucli server config get configuration
Nested dependency projects
When a dependency's install directory contains its own lucee.json, LuCLI treats it as a nested project and can install its dependencies as well.
lucli deps installalways works from the current directory as the root project.- For each installed (or reused) dependency with a directory install path:
- If
<installPath>/lucee.jsonexists, that directory is treated as a nested LuCLI project. - LuCLI runs the same resolution/install logic in that directory, writing a separate
lucee-lock.jsonalongside the nestedlucee.json.
- If
- Nested installs are recursive but depth-limited to avoid cycles and infinite graphs.
- Each nested project manages its own
lucee-lock.json; the root project's lock file is not flattened. - Server startup still uses only the root project's
lucee-lock.jsonto computeLUCEE_EXTENSIONS.
Dry-run semantics for nested projects:
lucli deps install --dry-run- Shows only the root project's dependencies and realized
lucee.jsonconfiguration. - Does not list nested projects, to keep the output compact by default.
- Shows only the root project's dependencies and realized
lucli deps install --dry-run --include-nested-deps- Shows the root project first.
- Then prints an additional
"[Nested: <relative/path>] Would install:"section for each nested project discovered from the current dependency graph, up to the depth limit. - Still avoids any file system changes (no clones, no lock writes).
Environment handling for nested projects:
- The root project uses strict environment application:
--env foowill fail iffoois not defined underenvironmentsin the rootlucee.json. - Nested projects apply the same
--envleniently:- If the nested
lucee.jsondoes not define that environment, LuCLI logs a warning and continues with the base configuration for that project instead of failing the whole run.
- If the nested
How extensions are activated at server startup
When you start a server, LuCLI resolves extension dependencies from one of two sources:
- If
dependencySettings.useLockFileistrueand extension entries exist inlucee-lock.json, LuCLI uses those locked entries. - Otherwise, LuCLI falls back to extension declarations in
lucee.json(dependencies/devDependencies) and resolves provider IDs / local installed.lexpaths directly.
Then LuCLI:
- Builds
LUCEE_EXTENSIONSfrom provider-based extension entries. - Injects
LUCEE_EXTENSIONSinto the server process environment (along with any otherenvVars). - Deploys path/url-based
.lexfiles into the serverlucee-server/deploy/directory.
This means:
- You can run
lucli deps installand start the server without requiringuseLockFile. - Lock files remain supported for deterministic/pinned dependency metadata when desired.
- Server lock (
lucee-lock.json→serverLocks) coexists with dependency locking; updating dependencies and updating server locks are separate, explicit steps.
For more about dependency structure, see LuceeJsonConfig and DependencySettingsConfig in the codebase, and for lock format see LuceeLockFile.
Settings reference
This section expands on the basic configuration reference and documents every available setting.
Top-level settings
| Key | Type | Default | Description |
|---|---|---|---|
name |
string | project folder name | Human-friendly server name. Used as the directory name under ~/.lucli/servers/ and in CLI output. |
version |
string | 6.2.2.91 |
Lucee Express version to download and use for this server. |
port |
integer | 8080 (auto-adjusted to avoid conflicts) |
Primary HTTP port for Tomcat. |
shutdownPort |
integer | port + 1000 |
Tomcat shutdown port. When omitted, LuCLI derives this from port. |
webroot |
string | "./" |
Webroot/docBase for the Tomcat context. May be relative to the project directory or absolute. |
host |
string | localhost |
Hostname used when constructing default URLs and (when HTTPS is enabled) generating a self-signed cert SAN. |
openBrowser |
boolean | true |
When true, LuCLI tries to open a browser after the server starts. |
openBrowserURL |
string | (computed) | Optional custom URL to open instead of the default computed URL. Empty string means "use the default". |
enableLucee |
boolean | true |
When false, Lucee servlets and CFML mappings are removed from web.xml and Tomcat acts as a static HTTP file server (HTML/HTM, assets, etc.). |
enableRest |
boolean | false |
When true, Rest servlets are enabled. Requires enableLucee to be true. |
configurationFile |
string | not set | Path to an external CFConfig JSON file (base config). Relative paths are resolved against the project directory. This is loaded first as the foundation for Lucee CFConfig. |
configuration |
object | null |
Inline Lucee CFConfig JSON that overrides/extends values from configurationFile. The final merged config is written to lucee-server/context/.CFConfig.json on server start. |
envFile |
string | .env |
Optional path to a project-specific env file used for ${VAR} substitution in lucee.json. Relative paths are resolved against the project directory. |
envVars |
object | {} |
Additional environment variables to inject into the Tomcat process. Values support ${VAR} / ${VAR:-default} and are resolved from envFile + system environment. |
monitoring |
object | see below | JMX/monitoring configuration. |
jvm |
object | see below | JVM memory and extra arguments. |
urlRewrite |
object | see below | URL rewriting / router configuration. |
admin |
object | see below | Lucee administrator exposure. |
https |
object | disabled | |
agents |
object | {} |
|
environments |
object | {} |
monitoring settings
"monitoring": {
"enabled": true,
"jmx": {
"port": 8999
}
}
| Key | Type | Default | Description |
|---|---|---|---|
monitoring.enabled |
boolean | true |
Enables JMX monitoring for this server. When false, no JMX system properties are added to the JVM. |
monitoring.jmx.port |
integer | 8999 |
JMX port used by the monitoring dashboard and external tools. Must not conflict with port or shutdownPort. |
jvm settings
"jvm": {
"maxMemory": "512m",
"minMemory": "128m",
"additionalArgs": []
}
| Key | Type | Default | Description |
|---|---|---|---|
jvm.maxMemory |
string | "512m" |
Maximum heap size, passed as -Xmx (e.g. "1024m", "2g"). |
jvm.minMemory |
string | "128m" |
Initial heap size, passed as -Xms. |
jvm.additionalArgs |
string[] | [] |
Extra JVM arguments appended to CATALINA_OPTS (e.g. GC tuning flags, -D system properties, or -javaagent: if you do not use agents). |
urlRewrite settings
"urlRewrite": {
"enabled": false,
"routerFile": "index.cfm"
}
| Key | Type | Default | Description |
|---|---|---|---|
urlRewrite.enabled |
boolean | false |
Enables framework-style URL rewriting using Tomcat RewriteValve (rewrite.config). When false, no rewrite rules are configured. |
urlRewrite.routerFile |
string | "index.cfm" |
Central router script used by the URL rewrite rules for extensionless URLs. In static-only sites you may want to set this to "index.html". |
admin settings
"admin": {
"enabled": true
}
| Key | Type | Default | Description |
|---|---|---|---|
admin.enabled |
boolean | true |
When true, Lucee administrator URLs are mapped under /lucee/ in web.xml. When false, those mappings are removed. |
agents settings
Agents let you define reusable sets of JVM arguments (typically -javaagent:... and related flags) under a stable identifier that can be toggled at lucli server start time.
"agents": {
"luceedebug": {
"enabled": false,
"jvmArgs": [
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=localhost:9999"
],
"description": "Lucee step debugger agent"
}
}
| Key | Type | Default | Description |
|---|---|---|---|
agents |
object | {} |
Map of agent IDs to agent configuration objects. |
agents.<id>.enabled |
boolean | false |
Whether this agent is active by default when starting the server without overrides. |
agents.<id>.jvmArgs |
string[] | [] |
JVM arguments to append when this agent is active (e.g. -javaagent:..., -agentlib:jdwp=...). Each entry is a full token as it should appear on the JVM command line. |
agents.<id>.description |
string | null |
Optional human-readable description used in documentation and diagnostics. |
Using lucli server start with configuration overrides
You can override any of the documented lucee.json keys at startup time using bare key=value arguments. When lucee.json does not exist yet, LuCLI will create a default configuration and then apply your overrides before starting the server.
Examples:
# Static HTML/HTM server (no Lucee, no URL rewrite, no monitoring)
lucli server start enableLucee=false monitoring.enabled=false urlRewrite.enabled=false
# Start with a custom port and disable browser auto-open
lucli server start port=8081 openBrowser=false
These overrides are runtime-only for that invocation and do not modify lucee.json.
To persist configuration changes, use lucli server config set ....