Skip to content

CLI Commands ​

The holo command line tool creates projects, generates files, runs migrations, starts workers, and runs your own app commands. This page lists every built-in command with its arguments and flags.

Running holo ​

holo ships with the @holo-js/cli package, which every Holo project installs. Run it through your package manager from the project folder:

bash
npx holo list
bash
pnpm dlx holo list
bash
yarn dlx holo list
bash
bunx holo list

The rest of the docs write every command with npx. Swap in your package manager's runner if you use a different one.

You can also run holo without a command, or with help, --help, or -h, to print the command list.

List all commands ​

bash
npx holo list

The output has two sections: Internal commands (built into Holo) and App commands (the ones you write in server/commands, plus any commands from plugins). See Custom Commands.

Help for one command ​

Add --help or -h to any command to print its usage line and description:

bash
npx holo make:model --help
bash
npx holo --version

-v works too.

How arguments and flags work ​

These rules apply to every command, including your own app commands.

FormMeaning
--name value or --name=valueA flag with a value.
--nameA boolean flag (true).
-mA short flag. -mfs sets -m, -f, and -s together.
--queue a --queue bRepeating a flag collects every value.
--queue a,bMost list flags also accept comma-separated values.
--Everything after -- is passed as plain arguments.

Number flags such as --step and --tries must be non-negative whole numbers.

Pick the environment ​

Every command accepts --env to pick the environment for that run. It wins over APP_ENV from your shell or .env, and decides which .env files load. See Env files and loading order.

bash
npx holo migrate:fresh --env=test

Interactive prompts ​

When you run a command in a terminal and leave out a required argument, holo asks for it. Some commands, like new, install, and make:model, also ask follow-up questions.

Turn prompts off with --no-interactive:

bash
npx holo make:model Post --no-interactive

Prompts are also skipped automatically when the command doesn't run in a terminal (for example in CI). In that case, a missing required argument makes the command fail.

Project commands ​

new ​

Create a new Holo project in a new folder and install its dependencies.

bash
npx holo new <name> [options]
Argument / flagDescription
<name>Project folder name. You can pass --name <name> instead. The folder must be empty or not exist.
--framework <nuxt|next|sveltekit>Framework to scaffold. Default: nuxt.
--database <sqlite|mysql|postgres>Database driver. Default: sqlite.
--package-manager <npm|pnpm|yarn|bun>Package manager used to install dependencies. Default: npm.
--package <name>Optional package to include. Repeat the flag or pass a comma-separated list. Values: storage, events, queue, validation, forms, auth, authorization, notifications, mail, broadcast, realtime, security, cache, or none. Choosing forms also adds validation.
--storage-default-disk <local|public>Default disk when storage is included. Default: local.
bash
npx holo new blog --framework next --database postgres --package auth,mail,queue

TIP

Most people start with create holo-js instead. See Installation.

dev ​

Write the generated schema types from your default connection's database, refresh Holo's other generated files, then start your framework's dev server.

bash
npx holo dev [...frameworkArgs]

The dev server doesn't need the database. If holo dev can't read it, it prints a warning that names the connection and starts with the generated schema it already has, or an empty one. Run npx holo migrate once the database is reachable to write it. holo dev and your app never create a missing SQLite file: pages that don't query the database still work, and a query fails with an error that names the file and tells you to run npx holo migrate.

While it runs, holo dev watches your Holo folders (config/, .env files, server/models, server/jobs, and so on) and refreshes the generated files when they change, restarting the dev server when needed.

Press Ctrl+C to stop it. A second Ctrl+C while it shuts down force-stops the server and its child processes and exits with status 130.

Argument / flagDescription
...frameworkArgsAny extra arguments and flags are passed to the framework's dev server.

If you don't pass --port (or -p), Holo uses the PORT environment variable when it's set.

bash
npx holo dev --port 4000

If the port is already in use, holo dev exits with a non-zero status and names the port. If another holo dev for this project is already running, the message gives its PID. holo dev never stops a process for you, so stop the other server yourself and run it again.

Your scaffolded package.json already has "dev": "holo dev", so npm run dev does the same thing.

build ​

Refresh Holo's generated files, then run your framework's production build. holo build reads the generated schema types from your default connection's database, so the database must be reachable; it doesn't load or run migrations. If the database can't be read, the build stops with an error that names the connection. A SQLite database file that doesn't exist counts as unreadable: holo build never creates it. Run npx holo migrate to create it. A SQLite :memory: database is new and empty in every process, so holo build can't read your tables from one and writes a generated schema with no tables.

bash
npx holo build [...frameworkArgs]
Argument / flagDescription
...frameworkArgsExtra arguments passed to the framework build.
bash
npx holo build

start ​

Start the production server from a finished build.

bash
npx holo start [...frameworkArgs]
Argument / flagDescription
...frameworkArgsExtra arguments passed to the production server.

Press Ctrl+C to stop it. A second Ctrl+C while it shuts down force-stops the server and its child processes and exits with status 130.

Like dev, start uses PORT when you don't pass --port. It loads your .env files and runs the server with NODE_ENV=production. The application environment still comes from APP_ENV, which is production when it isn't set. See Deployment.

bash
PORT=8080 npx holo start

prepare ​

Scan your project and regenerate the files in .holo-js/generated (model, migration, job, event, and command registries, plus generated types).

bash
npx holo prepare

dev and build run this for you, and your scaffolded package.json runs it on install. Run it yourself after pulling changes or when your editor's types look out of date. prepare doesn't read the database or load your migrations. It registers each migration by its file name.

If .holo-js/generated/schema.generated.ts fails to load, for example a file written by an earlier release, every command replaces it with an empty generated schema, prints a one-line notice and continues. The next migrate, build or dev writes it again from the database.

install ​

Add a first-party Holo feature to an existing project. It adds the package, creates config files and folders, updates .env and .env.example, then installs dependencies. Packages are added in your project's Holo version style: exact if every @holo-js/* dependency is pinned exactly, ~ if every one uses ~, and ^ otherwise. A setting it adds to .env or .env.example goes next to the related settings already in the file, such as other REDIS_* keys. Settings with no related ones in the file are appended at the end.

bash
npx holo install <target> [options]
Argument / flagDescription
<target>One of queue, events, auth, authorization, notifications, mail, broadcast, realtime, security, cache, media, schedule.
--driver <name>Only for queue (sync, redis, database; default sync) and cache (file, redis, database; default file). Other targets reject this flag.
--socialauth only. Add social login support.
--provider <name>auth only. Social provider to add: google, github, discord, facebook, apple, linkedin. Repeat or comma-separate. Implies --social.
--workosauth only. Add WorkOS support.
--clerkauth only. Add Clerk support.

What each target creates:

TargetCreates
queueconfig/queue.ts, server/jobs
eventsserver/events, server/listeners. Offers to set up queued listeners if queues aren't installed.
authconfig/auth.ts, config/session.ts, config/security.ts, config/cors.ts, server/models/User.ts
authorizationserver/policies, server/abilities
notificationsconfig/notifications.ts
mailconfig/mail.ts, server/mail
broadcastconfig/broadcast.ts, server/broadcast, server/channels, the /broadcasting/auth route, and client setup
realtimeserver/realtime and framework setup
securityconfig/security.ts, config/cors.ts
cacheconfig/cache.ts (and config/redis.ts for Redis, or the cache connection in an existing one)
mediaconfig/media.ts and the media table migration
scheduleserver/schedule.ts. Says when cache or queue is missing, since withoutOverlapping(), onOneServer() (with a store every server shares, such as Redis or the database), and scheduled jobs need them.
bash
npx holo install queue --driver redis
npx holo install auth --provider google,github

key:generate ​

Write a random APP_KEY to your project's .env file. If .env doesn't exist, it is created. If APP_KEY already has a value, nothing changes.

bash
npx holo key:generate

config:cache ​

Compile every file in config/ into one cached file at .holo-js/generated/config-cache.json. Each env() call is evaluated with your current env values, and the results, secrets included, are written into the file. Holo still reads .env to choose the environment. When the app runs in production (APP_ENV=production, from your shell or from .env) and the cache was built for production, Holo reads the cache instead of loading config/*.ts. Config modules that contain functions, such as app with an exception reporter or security with rate-limit callbacks, are loaded from their files, but the env() values they read while loading are recorded in the cache too. Code that calls env() after startup, such as a reporter's body, reads the runtime environment. Other environments ignore the cache.

bash
APP_ENV=production npx holo config:cache

WARNING

In production, changes to config/*.ts or .env have no effect after you cache config until you run config:cache again or config:clear. Rebuild the cache on every deploy, after your env variables are in place. The cache file contains your secrets, so keep it out of version control.

config:clear ​

Delete the config cache so Holo loads config/*.ts again.

bash
npx holo config:clear

agents:install ​

Install Holo-JS skill files for AI coding agents so they can look up Holo docs while they work. See Coding with AI Agents.

bash
npx holo agents:install [options]

Aliases: agent:install, ai:install.

FlagDescription
--agent <name>Agents to install for: all, codex, claude, cursor, windsurf, opencode, gemini, kiro. Repeat or comma-separate. In a terminal you get a picker; otherwise every agent is installed.
--globalInstall into your home folder instead of the current project.
--forceReplace skill folders that you changed by hand. Without it, the command refuses to overwrite them.
bash
npx holo agents:install --agent claude,cursor

auth:notifications:publish ​

Copy the built-in auth notifications (such as email verification and password reset) into your app so you can edit them. Existing files are skipped.

bash
npx holo auth:notifications:publish

Generators ​

Generators create a file from a template and then refresh the generated registries. Names can include folders, such as reports/send-digest. A generator fails if the file or name already exists.

make:model ​

Create a model, and optionally its migration, observer, seeder, and factory.

bash
npx holo make:model <name> [options]
Argument / flagDescription
<name>Model name, such as Post or blog/Post.
--table <table>Table name to use instead of the default.
-m, --migrationAlso create a migration.
-o, --observerAlso create an observer and attach it to the model.
-s, --seederAlso create a seeder.
-f, --factoryAlso create a factory.

In a terminal, if you pass none of -m, -o, -s, -f, you're asked which files to create.

bash
npx holo make:model Post -mfs

Creates server/models/Post.ts, plus files in server/db/migrations, server/db/factories, and server/db/seeders.

make:migration ​

Create a timestamped migration in server/db/migrations.

bash
npx holo make:migration <name> [--create <table> | --table <table>]
Argument / flagDescription
<name>Migration name, such as create_posts_table.
--create <table>Scaffold a migration that creates this table.
--table <table>Scaffold a migration that changes this existing table.

Use one of --create or --table, not both. Without either, Holo guesses from names like create_posts_table or add_status_to_posts_table.

bash
npx holo make:migration add_status_to_posts_table --table posts

make:seeder ​

Create a seeder in server/db/seeders.

bash
npx holo make:seeder <name>
bash
npx holo make:seeder UserSeeder

make:factory ​

Create a factory in server/db/factories.

bash
npx holo make:factory <name>
bash
npx holo make:factory PostFactory

make:observer ​

Create a model observer in server/db/observers. It doesn't attach the observer to an existing model; use make:model --observer for that.

bash
npx holo make:observer <name>
bash
npx holo make:observer PostObserver

make:job ​

Create a queue job in server/jobs. File names are converted to kebab-case, and the job name uses dots for folders.

bash
npx holo make:job <name>
bash
npx holo make:job reports/send-digest

Creates server/jobs/reports/send-digest.ts with the job name reports.send-digest.

make:event ​

Create an event in server/events.

bash
npx holo make:event <name>
bash
npx holo make:event user/registered

Creates server/events/user/registered.ts with the event name user.registered.

make:listener ​

Create an event listener in server/listeners.

bash
npx holo make:listener <name> --event <event-name>
Argument / flagDescription
<name>Listener name.
--event <event-name>Required. The event to listen to. Repeat or comma-separate to listen to several events.
bash
npx holo make:listener user/send-welcome-email --event user.registered

make:mail ​

Create a mail definition in server/mail. Mail files are created as Markdown mail.

bash
npx holo make:mail <name> [--markdown]
Argument / flagDescription
<name>Mail name, such as auth/verify-email.
--markdownCreate a Markdown mail. This is already the default.
bash
npx holo make:mail orders/order-shipped

make:broadcast ​

Create a broadcast event in server/broadcast.

bash
npx holo make:broadcast <name>
bash
npx holo make:broadcast orders/shipment-updated

make:channel ​

Create a channel authorization file in server/channels.

bash
npx holo make:channel <pattern>
ArgumentDescription
<pattern>Channel name pattern. Use {param} for dynamic parts.
bash
npx holo make:channel orders.{orderId}

Database commands ​

migrate ​

Run every migration that hasn't run yet, then write the generated schema types from the database. A skipped --isolated run leaves them unchanged. migrate:rollback and migrate:fresh write them too.

When the database of the default connection, or of another connection a migration uses, doesn't exist, migrate and migrate:fresh create it the first time they use it: the SQLite file and its directory, or the PostgreSQL or MySQL database. They are the only commands that create a database. holo seed and other commands fail on a missing one.

bash
npx holo migrate [--step <n>] [--isolated]
FlagDescription
--step <n>Run at most this many pending migrations.
--isolatedTake the migrations lock from the default cache store first. If another run holds it, this run skips and exits successfully. Use it when several replicas run migrate at deploy time; every server must share the store, such as redis or database. If @holo-js/cache isn't installed, the store can't be resolved, or the store can't give a lock, the run fails before migrating and the error names that cause. See Isolating migration runs.
bash
npx holo migrate

migrate:fresh ​

Drop every table on the default connection and run all migrations from scratch.

bash
npx holo migrate:fresh [--seed] [--only <names>] [--quietly] [--force]
FlagDescription
--seedRun seeders after migrating.
--only <names>With --seed, run only these seeders (comma-separated).
--quietlyWith --seed, run seeders without firing model events.
--forceSkip the production confirmation.
bash
npx holo migrate:fresh --seed

When APP_ENV is production, migrate:fresh asks you to confirm before it drops anything. Without a terminal, it stops with an error unless you pass --force.

DANGER

migrate:fresh deletes all your data. Never run it against a production database.

migrate:rollback ​

Undo migrations.

bash
npx holo migrate:rollback [--step <n>] [--batch <n>] [--force]
FlagDescription
--step <n>Roll back at most this many migrations from the batch.
--batch <n>Roll back this batch number instead of the latest one.
--forceSkip the production confirmation.

With no flags, every migration in the latest batch is rolled back. When APP_ENV is production, the command asks you to confirm first, and without a terminal it stops with an error unless you pass --force.

bash
npx holo migrate:rollback --step 1

seed ​

Run your seeders.

bash
npx holo seed [--only <names>] [--quietly] [--force]
FlagDescription
--only <names>Run only these seeders. Comma-separate or repeat the flag.
--quietlyRun seeders without firing model events (observers don't run).
--forceSkip the production confirmation.
bash
npx holo seed --only roles,admin

seed writes the generated schema types from the database before it runs your seeders, so models resolve against the current tables. If it can't read the database, it warns and runs them with the generated schema it has. When APP_ENV is production, seed asks you to confirm first. Without a terminal, it stops with an error unless you pass --force.

prune ​

Delete old records from models that define a prunable query.

bash
npx holo prune [ModelName ...]
ArgumentDescription
ModelName ...Prune only these models. Without names, every prunable model is pruned. Naming an unknown or non-prunable model is an error.
bash
npx holo prune Session AuditLog

Schedule commands ​

See Task Scheduling for how to declare tasks in server/schedule.ts.

schedule:run ​

Run every task that is due this minute, then exit. Run it from a system cron entry of * * * * *.

bash
npx holo schedule:run

A failing task is reported and the rest still run. SIGINT and SIGTERM are passed on to the command that is running; no further tasks start, and schedule:run exits once that command has.

schedule:work ​

Start schedule:run at the start of every minute, in the foreground. Use it instead of a cron entry.

bash
npx holo schedule:work

Ctrl+C, SIGINT, and SIGTERM stop it. The signal is passed on to any schedule:run still running, and schedule:work exits once they have. If schedule:run can't be started, it stops the same way and exits with the error.

schedule:list ​

List the tasks with their cron expressions and next due times in the app's timezone.

bash
npx holo schedule:list

Queue commands ​

See Running Workers and Failed Jobs for the full workflow.

queue:work ​

Process jobs from an async queue connection (redis or database). The sync driver doesn't need a worker.

bash
npx holo queue:work [options]
FlagDescription
-c, --connection <name>Queue connection from config/queue.ts. Default: the default connection.
-q, --queue <name>Queue names to process. Repeat or comma-separate.
--onceProcess one job and stop.
--stop-when-emptyStop when the queue has no more jobs.
--sleep <seconds>Seconds to wait when the queue is empty. Default: the connection's sleep setting, or 1.
--tries <n>Maximum attempts per job, overriding the job's setting.
--timeout <seconds>Maximum run time per job, overriding the job's setting.
--max-jobs <n>Stop after processing this many jobs.
--max-time <seconds>Stop after running this long.

The worker stops cleanly on SIGINT / SIGTERM and when queue:restart is run.

bash
npx holo queue:work --connection redis --queue emails,media --tries 3

queue:listen ​

Run a queue worker for development. It restarts the worker whenever your jobs, config, or related files change.

bash
npx holo queue:listen [options]

Accepts the same flags as queue:work except --once and --stop-when-empty.

bash
npx holo queue:listen --connection redis --queue media

Ctrl+C, SIGINT, and SIGTERM stop the worker after its current job and then stop queue:listen. A second Ctrl+C while it shuts down force-stops the worker and exits with status 130. A restart waits for the old worker to exit. If the old worker reports a process error while it stops, queue:listen exits with that error and doesn't start a replacement. On Windows the worker's process tree is ended at once, so a job in progress is interrupted and retried later.

queue:restart ​

Tell every running worker to stop after its current job. Your process manager (systemd, PM2, Docker, and so on) then starts fresh workers with your new code. queue:listen restarts its worker automatically.

bash
npx holo queue:restart

queue:clear ​

Delete pending jobs from a connection.

bash
npx holo queue:clear [--connection <name>] [--queue <name>]
FlagDescription
-c, --connection <name>Connection to clear. Default: the default connection.
-q, --queue <name>Only clear these queues. Repeat or comma-separate.
bash
npx holo queue:clear --connection redis --queue emails

queue:failed ​

List failed jobs.

bash
npx holo queue:failed

queue:retry ​

Push failed jobs back onto their queue.

bash
npx holo queue:retry <id|all>
ArgumentDescription
<id|all>A failed job ID from queue:failed, or all.
bash
npx holo queue:retry all

queue:forget ​

Delete one failed job record.

bash
npx holo queue:forget <id>

queue:flush ​

Delete every failed job record.

bash
npx holo queue:flush

queue:table ​

Create the migration for the database queue driver's jobs table.

bash
npx holo queue:table

queue:failed-table ​

Create the migration for the failed jobs table.

bash
npx holo queue:failed-table

Cache commands ​

cache:clear ​

Remove every item from a cache store, whatever its key prefix. For a Redis store this runs FLUSHDB on its database; see Cache drivers before pointing a store at shared data.

bash
npx holo cache:clear [--driver <name>]
FlagDescription
-d, --driver <name>Store name from config/cache.ts. Default: the default store.
bash
npx holo cache:clear --driver redis

cache:forget ​

Remove one key from a cache store.

bash
npx holo cache:forget <key> [--driver <name>]
Argument / flagDescription
<key>The cache key to remove.
-d, --driver <name>Store name. Default: the default store.
bash
npx holo cache:forget dashboard.stats

cache:table ​

Create the migration for the database cache driver's tables (cache entries and locks).

bash
npx holo cache:table

Security commands ​

rate-limit:clear ​

Clear rate limit counters. Works with the file and redis drivers. The memory driver lives inside your server process, so it can't be cleared from the CLI.

bash
npx holo rate-limit:clear [--limiter <name>] [--key <value>] [--all]
FlagDescription
--limiter <name>The limiter to clear. Required unless you pass --all.
--key <value>Only clear this key within the limiter, such as user:42.
--allClear every limiter.
bash
npx holo rate-limit:clear --limiter login --key "ip:203.0.113.7"

Media commands ​

media:table ​

Create the migration for the media table. holo install media already does this, so you only need it when you add @holo-js/media by hand.

bash
npx holo media:table

media:regenerate ​

Rebuild image conversions for stored media. Like seed, it writes the generated schema types from the database first.

bash
npx holo media:regenerate [ModelName ...] [--ids=<ids>] [--only=<names>] [--only-missing]
Argument / flagDescription
ModelName ...Only regenerate media attached to these models.
--ids=<ids>Only these media IDs (positive integers, comma-separated).
--only=<names>Only these conversions, such as thumb. Comma-separated.
--only-missingOnly create conversions that don't exist yet.
bash
npx holo media:regenerate Post --only=thumb

Storage commands ​

storage:decode-names ​

Rename files that Holo 0.3 stored under URL-encoded names on a local disk, such as May%20report.csv, to their real names, such as May report.csv. Run it once for each local disk after upgrading to 0.4.0.

bash
npx holo storage:decode-names --disk=local
FlagDescription
--disk=<name>The local disk to rename files on. Required.

It prints every rename and every file it skips, then the total. Running it again right away renames nothing. Run it before new uploads arrive: a file uploaded after the upgrade under a name that looks URL-encoded, such as May%20report.csv, would be renamed by a later run. It skips:

  • a name that still looks URL-encoded after decoding, such as 100%2520off.txt, because Holo can't tell what the original name was;
  • a name whose decoded form already exists, checked again right before each rename;
  • a name whose decoded form differs only by letter case or Unicode normalization from another decoded name or an existing name in the same folder, such as cafÉ.txt and café.txt, because some filesystems treat them as the same file;
  • a file whose decoded name ends in $, because local disks use that ending for metadata;
  • on Windows, a name Windows can't store, such as one with : or ?, one ending in a dot or space, or a device name such as CON;
  • a symbolic link, which is never followed, along with its metadata file.

A file and its metadata are checked together before either is renamed. If either name must be skipped, both are skipped. If the second rename fails, the command attempts to restore the first name and reports any failure.

It never follows a symlink inside the disk, so files outside the disk root are never renamed. The disk's root in config/storage.ts is used as the disk, including when that path is itself a symlink, as the rest of the disk does. Rename skipped files by hand. S3 disks are not supported; see S3 file names with special characters.

Broadcast commands ​

broadcast:work ​

Start the self-hosted broadcast WebSocket server. It reads config/broadcast.ts and your channel files, and prints the host and port it listens on. Stop it with Ctrl+C or SIGTERM.

bash
npx holo broadcast:work

See Scaling in Production for running it alongside your app.

Plugin commands ​

Plugins are npm packages that add drivers, config, or commands to Holo. See Writing Plugins.

Every plugin:* command also works with a plugins: prefix (for example plugins:add).

plugin:add ​

Install a plugin package, add any packages it needs, activate it in config/app.ts, and refresh generated files. If any step fails, your package.json, config/app.ts, and .holo-js folder are restored.

bash
npx holo plugin:add <package>
bash
npx holo plugin:add @acme/holo-plugin-sentry

plugin:remove ​

Deactivate a plugin in config/app.ts.

bash
npx holo plugin:remove <package> [--uninstall]
Argument / flagDescription
<package>Plugin package name.
--uninstallAlso remove the package from package.json and reinstall dependencies.
bash
npx holo plugin:remove @acme/holo-plugin-sentry --uninstall

plugin:list ​

List active plugins and whether each one loaded.

bash
npx holo plugin:list

plugin:info ​

Show a plugin's name, description, and what it adds (drivers, config files, env keys, commands, and so on).

bash
npx holo plugin:info <package>

plugin:doctor ​

Load every active plugin and report problems. Exits with an error if any plugin fails.

bash
npx holo plugin:doctor

Next steps ​

Released under the MIT License.