Command Line Tools
Configuration
The wheels CLI is configured at two levels. Per-project behaviour — which Lucee version to run, which port to listen on, which dependencies to pull in, what changes between dev and prod — lives in a lucee.json at the project root. User- and machine-level state — installed modules, running servers, resolved deps, stored secrets — lives under a home directory that defaults to ~/.wheels when you invoke the binary as wheels. Between those two surfaces, a handful of environment variables and global flags let you redirect either one without editing files.
You’ll use this for:
- Pinning a Lucee version and server port for a project.
- Switching between
dev,test, andprodwithout maintaining separate config files. - Keeping a
wheelsinstall and a standalonelucliinstall side by side on the same machine. - Turning on verbose logging or command timing when something goes wrong.
- Setting
LUCLI_HOMEorJAVA_HOMEfor CI or container builds.
lucee.json
Section titled “lucee.json”lucee.json sits at the root of a Wheels project and configures the server, dependencies, and any environment-specific overrides. The file is optional — if none exists, the CLI falls back to defaults — but every non-trivial project ends up with one. It is read by wheels server start, wheels server run, and the dependency-install flow that resolves anything listed under dependencies.
The schema is permissive (unknown keys are ignored) and JSON-with-comments is accepted by the parser. A minimal file that pins a Lucee version and a port looks like this:
{ "name": "my-wheels-app", "lucee": { "version": "7.0.1.100" }, "port": 3000, "webroot": "./"}An annotated maximal example, showing every top-level key the LuCLI parser recognises today:
{ "name": "my-wheels-app", "lucee": { "version": "6.2.2.91", "variant": "standard" }, "port": 3456, "shutdownPort": 9010, "webroot": "./", "https": { "enabled": true }, "ajp": { "enabled": false }, "monitoring": { "enabled": false, "jmx": { "port": 8001 } }, "jvm": { "maxMemory": "5g", "minMemory": "128m", "additionalArgs": [] }, "urlRewrite": { "enabled": false, "routerFile": "index.cfm", "configFile": "urlrewrite.xml" }, "admin": { "enabled": false, "password": "" }, "enableLucee": true, "enableREST": false, "openBrowser": true, "configuration": { "datasources": {}, "mappings": {} }, "envVars": {},
// Dependencies resolved on server start (or `wheels deps install`). // Short-hand "name": "version" or a full object with source/url/ref. "dependencies": { "framework-one": "4.3.0", "fw1-git": { "source": "git", "url": "https://github.com/framework-one/fw1", "ref": "v4.3.0", "subPath": "framework", "installPath": "dependencies/fw1", "mapping": "/framework" } }, "devDependencies": {},
// How dependencies are installed (symlink vs copy, lock file behaviour). "dependencySettings": { "installLocation": "dependencies", "autoInstallOnServerStart": true, "verifyIntegrity": true, "pruneOnInstall": false, "installMethod": "symlink", "useLockFile": false, "installDevDependencies": false },
// Per-environment overrides — see "Profiles" below. "environments": {}}The file is JSON — comments above are for illustration only and will only parse because LuCLI enables Jackson’s ALLOW_COMMENTS feature. If you want to stay strictly compliant, drop them.
Profiles
Section titled “Profiles”LuCLI uses the word profile for two unrelated things, and keeping them separate avoids confusion.
Environment profiles (per-project)
Section titled “Environment profiles (per-project)”The environments object in lucee.json lets you override specific keys for a named environment. Keys inside an environment are merged over the base config — anything you omit is inherited.
{ "name": "my-app", "lucee": { "version": "7.0.1.100" }, "port": 8890, "jvm": { "maxMemory": "512m" }, "environments": { "dev": { "port": 8891, "openBrowser": true }, "prod": { "port": 8080, "jvm": { "maxMemory": "2048m" }, "admin": { "password": "secure_prod_password" }, "openBrowser": false } }}Select an environment with --env (or the short form -e) on any command that loads lucee.json:
wheels server start --env=prodwheels server run -e devIf the environment name you pass isn’t defined in lucee.json, the CLI fails fast with a message listing the environments that are defined. Environment overrides apply both to server-level keys (port, jvm, admin) and to dependencySettings (for example, installDevDependencies: true in dev and false in prod).
Binary-name profiles (CLI branding)
Section titled “Binary-name profiles (CLI branding)”Separate from environment profiles, LuCLI also has a small profile system that decides which branding and home directory the binary uses. When the binary is invoked as wheels, the WheelsProfile activates and the default home directory is ~/.wheels. When invoked as lucli (or any other name), DefaultProfile activates and the default is ~/.lucli. Profile selection is based on the binary’s filename — the wheels formula ships a launcher named wheels, which is what triggers the Wheels profile.
You shouldn’t need to touch this directly. It matters only as an explanation for why wheels stores everything under ~/.wheels/ while a standalone lucli install uses ~/.lucli/ — the two can coexist without stepping on each other.
Environment variables
Section titled “Environment variables”The CLI reads a small set of environment variables and JVM system properties. Values below are checked in order: JVM system property (passed via -D) wins over environment variable, which wins over the default.
| Name | Read by | Default | Purpose |
|---|---|---|---|
LUCLI_HOME | LuCLI core (LucliPaths) | ~/.wheels under the wheels binary; ~/.lucli under lucli | Base directory for all user-level state — modules, servers, deps, secrets, settings. |
LUCLI_BACKUPS_DIR | LuCLI core (LucliPaths) | <LUCLI_HOME>_backups alongside home | Where backups are written. Override to point backups at a different disk or directory. |
JAVA_HOME | JVM (read by the wheels wrapper) | Set to openjdk@21 by the Homebrew formula | The JDK the launcher invokes. The wrapper sets this explicitly so a missing shell-level JAVA_HOME doesn’t break wheels. |
LUCLI_LOCALE | LuCLI (StringOutput) | Falls back to LANG, then the JVM default | Locale for CLI message output. |
LUCLI_SECRETS_PASSPHRASE | LuCLI secrets store | (none) | Unlock the encrypted local secrets store non-interactively. Used by CI. |
EDITOR | LuCLI (CommandProcessor) | Platform default | External editor invoked by interactive edit flows. |
JVM system-property equivalents exist where relevant: -Dlucli.home=…, -Dlucli.backups.dir=…, -Dlucli.locale=…. These take precedence over the environment variable of the same name. There is no WHEELS_HOME — all user-level state uses LUCLI_HOME regardless of which binary name launched it.
Verbose and timing
Section titled “Verbose and timing”Four global flags are defined on the root wheels command and apply to every subcommand:
| Flag | Short | Effect |
|---|---|---|
--verbose | -v | Enables verbose output. Individual commands print extra detail (resolved paths, HTTP requests, dep resolution). |
--debug | -d | Enables debug output (stack traces, internal state dumps). |
--timing | -t | Times each command and prints a summary on exit. Useful when something feels slow. |
--whitespace | -w | Preserves whitespace in script output (for wheels cfml and similar). |
Global flags go before the subcommand:
wheels --verbose server startwheels -t generate model User name emailwheels -v -t testThe Wheels Module’s Module.cfc accepts verboseEnabled and timingEnabled arguments on init, so these flags propagate from LuCLI core into the module’s subcommands — a timed wheels generate reports the per-step timing the same way a timed wheels server start does.
Per-project vs global state
Section titled “Per-project vs global state”The CLI keeps two kinds of state in two distinct places.
Per-project. Everything that belongs to the app lives inside the project directory: lucee.json, the app source, the vendor/wheels/ framework copy, dependencies/ (or whatever dependencySettings.installLocation points at), and tests/. Move the directory, zip it, commit it — the project is self-contained.
User-level. Everything the CLI itself installs or caches lives under LUCLI_HOME (default ~/.wheels for wheels, ~/.lucli for lucli). That includes:
modules/— installed LuCLI modules, including the Wheels Module itself atmodules/wheels/.servers/— state for each Lucee server the CLI has started (lockfiles, PIDs, per-server config).deps/anddeps/git-cache/— resolved and cached dependencies.secrets/— encrypted local secrets store.ai/— AI helper settings and skills.settings.json— CLI-level settings.
<LUCLI_HOME>_backups/ (default ~/.wheels_backups/ or ~/.lucli_backups/) sits alongside the home directory and holds anything the CLI backed up before a destructive operation.
Running wheels and lucli side by side
Section titled “Running wheels and lucli side by side”Because wheels defaults to ~/.wheels and lucli defaults to ~/.lucli, the two installs are isolated by default — no action needed. If you want wheels to share state with a standalone LuCLI install anyway (unusual, but legitimate for debugging), point LUCLI_HOME at the shared directory:
LUCLI_HOME=$HOME/.lucli wheels server statusIf you want a single machine to juggle multiple Wheels installs — say, one for work and one for a personal project — override LUCLI_HOME per-shell:
# Project Aexport LUCLI_HOME=$HOME/.wheels-workwheels server start
# Project B (new shell)export LUCLI_HOME=$HOME/.wheels-personalwheels server startEach directory gets its own modules, servers, deps, and secrets.