Skip to content

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, and prod without maintaining separate config files.
  • Keeping a wheels install and a standalone lucli install side by side on the same machine.
  • Turning on verbose logging or command timing when something goes wrong.
  • Setting LUCLI_HOME or JAVA_HOME for CI or container builds.

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:

lucee.json — minimal example
{
"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:

lucee.json — all recognised keys
{
"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.

LuCLI uses the word profile for two unrelated things, and keeping them separate avoids confusion.

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.

lucee.json — environment profiles
{
"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:

selecting an environment profile
wheels server start --env=prod
wheels server run -e dev

If 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).

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.

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.

NameRead byDefaultPurpose
LUCLI_HOMELuCLI core (LucliPaths)~/.wheels under the wheels binary; ~/.lucli under lucliBase directory for all user-level state — modules, servers, deps, secrets, settings.
LUCLI_BACKUPS_DIRLuCLI core (LucliPaths)<LUCLI_HOME>_backups alongside homeWhere backups are written. Override to point backups at a different disk or directory.
JAVA_HOMEJVM (read by the wheels wrapper)Set to openjdk@21 by the Homebrew formulaThe JDK the launcher invokes. The wrapper sets this explicitly so a missing shell-level JAVA_HOME doesn’t break wheels.
LUCLI_LOCALELuCLI (StringOutput)Falls back to LANG, then the JVM defaultLocale for CLI message output.
LUCLI_SECRETS_PASSPHRASELuCLI secrets store(none)Unlock the encrypted local secrets store non-interactively. Used by CI.
EDITORLuCLI (CommandProcessor)Platform defaultExternal 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.

Four global flags are defined on the root wheels command and apply to every subcommand:

FlagShortEffect
--verbose-vEnables verbose output. Individual commands print extra detail (resolved paths, HTTP requests, dep resolution).
--debug-dEnables debug output (stack traces, internal state dumps).
--timing-tTimes each command and prints a summary on exit. Useful when something feels slow.
--whitespace-wPreserves whitespace in script output (for wheels cfml and similar).

Global flags go before the subcommand:

global flags before the subcommand
wheels --verbose server start
wheels -t generate model User name email
wheels -v -t test

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

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 at modules/wheels/.
  • servers/ — state for each Lucee server the CLI has started (lockfiles, PIDs, per-server config).
  • deps/ and deps/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.

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:

sharing home directories between wheels and lucli
LUCLI_HOME=$HOME/.lucli wheels server status

If 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:

juggling multiple Wheels installs on one machine
# Project A
export LUCLI_HOME=$HOME/.wheels-work
wheels server start
# Project B (new shell)
export LUCLI_HOME=$HOME/.wheels-personal
wheels server start

Each directory gets its own modules, servers, deps, and secrets.