Joomla Command-Line Interface
|
This content was generated with the assistance of AI and should be verified against the official Joomla documentation before being relied on in production. This sub-section’s Joomla bibliography lists the reference material consulted while preparing these pages. |
The Joomla command-line interface (CLI) is a single PHP script, cli/joomla.php, that boots the same framework, plugins
and database connection as the web site but without a web server, a session or a login. It is built on the Symfony
Console component through the Joomla Framework console package. It lets an administrator update the core, install and
remove extensions, rebuild the Smart Search index, run scheduled tasks, switch the site offline for maintenance, export
and import tables, and manage users from a terminal, from cron or from a CI job. This page was written against
Joomla 6.2.x on PHP 8.3 or newer and checked against the official documentation and the 6.2 source tree on
2026-10-30. Every command in this page was enumerated from the libraries/src/Console directory of the 6.2 source tree
and the Joomla Framework database package that supplies two more.
The running example is the Tallpine Hiking Club site: a Joomla 6.2 site served from a container, with a MariaDB
database, a staging copy and a production copy, a scheduled task plg_task_tallpine_trailsync that syncs trail closures,
and a custom tallpine:import command (see Console command plugins).
What the CLI is, and who uses it
-
Administrators use it for operations that must run unattended: nightly cache clean-up, session garbage collection, Smart Search indexing, core and extension update checks, database exports before an upgrade.
-
DevOps and CI engineers use it to build a site from a container image, install the extensions of a release, switch the site offline during a deploy and run the scheduled tasks from a real scheduler instead of web traffic.
-
Site builders on shared hosting use it from the cron section of the hosting panel when there is no terminal.
-
Extension developers add their own commands as plugins of the
consolegroup and test them from the shell.
The CLI never asks for a Joomla login. Whoever can run php cli/joomla.php as a user that can read configuration.php
and reach the database can do everything the Super User can do, including creating Super Users and changing the global
configuration. Treat shell access to the web root as equivalent to a Super User account (see Security of the CLI).
Running it
From the Joomla root:
cd /var/www/html
php cli/joomla.php list
php cli/joomla.php help cache:clean
php cli/joomla.php cache:clean --help
Source: Joomla User Guide: Using the CLI
The guide also shows running the script from inside the cli directory (cd cli; php joomla.php). Both forms work because
the script derives the Joomla root from its own location. Three things are worth knowing before the first run.
-
listandhelp.listprints every registered command, grouped by the part before the colon (cache,config,core,database,extension,finder,maintenance,scheduler,session,site,update,user). It also shows commands contributed by console plugins, so it is the quickest way to check that a plugin is enabled.help <command>and<command> --helpprint the arguments, options and usage of one command. The help text is hard-coded in the command classes and in third-party plugins, so it is the authority for the exact spelling on the installed version. -
The PHP binary is not necessarily the web server’s. The CLI runs with the command-line PHP, which has its own
php.ini, its own extensions and possibly another version. The script starts with a version guard: it prints that the command-line PHP must be 8.3.0 or newer and exits otherwise. Check it withphp -vandphp -m, and use the full path to the binary incron(for example/usr/local/bin/php), becausecronruns with a minimalPATH. -
Who runs it matters. Commands that write files (cache, logs,
tmp,configuration.php, installed extensions) create them owned by the invoking user. Running asrootand then browsing aswww-dataproduces unwritable cache and log files. Run the CLI as the web server’s user (sudo -u www-data php cli/joomla.php ..., ordocker compose exec -u www-data).
The bootstrap sequence
The script cli/joomla.php defines _JEXEC, applies the PHP version guard, loads defines.php (an optional
defines.php in the root can relocate paths) and includes/defines.php, checks that the Composer autoloader and
media/vendor exist (this check is what a git checkout without composer install and npm ci runs into), checks that
configuration.php exists and is larger than a few bytes, and then loads includes/framework.php and starts the
console application. Because configuration.php is required, a fresh tree has no list output: the script prints the
install hint instead. A fresh site can be installed from the shell with php installation/joomla.php install, which has
its own options (see its --help).
The console application is Joomla\CMS\Application\ConsoleApplication. Its execute() method registers the extension
namespaces, populates the HTTP host (so plugins that need a site URL do not fail), and imports the behaviour, system
and console plugin groups on the console dispatcher before delegating to the Framework application. Core commands are
registered lazily by a command loader from the Console service provider; commands from console plugins are added in
a BEFORE_EXECUTE listener. Section The console plugin group returns to that.
Global options and exit codes
The built-in Symfony options work on every command.
| Option | Effect |
|---|---|
|
Print the help of the command. |
|
Suppress output. Use it in |
|
Increase verbosity. |
|
Never ask. Prompting commands take the default or, for |
|
Force or disable coloured output. Disable it in CI logs. |
|
Print the application version. |
|
Joomla-specific. The public URL of the site, for example |
The --live-site option exists because code run from the shell has no HTTP_HOST or REQUEST_URI. Anything that builds
absolute URLs (mail templates, routing from a task, the automated-updates registration) needs it, or needs $live_site
in configuration.php. Without it, a command that sends e-mail with links can produce links with the wrong host.
Options accept a space or an equals sign. The exception is the positional option=value arguments of config:set,
which always use =:
php cli/joomla.php session:gc --application administrator
php cli/joomla.php session:gc --application=administrator
php cli/joomla.php config:set debug=true
Source: Joomla User Guide: Using the CLI
Commands return 0 on success and a non-zero code on failure, which is what shell scripts, cron wrappers and CI steps
test. Several commands define their own non-zero codes (for example core:update returns 1 when its checks fail,
including when no update is available, and 2 when the update itself fails; config:get returns 1, 2 or 3 for
a missing group, a missing option or a failed read; extension:remove has distinct codes for not found, locked, invalid
type and aborted). A script should test $? against zero rather than against one specific value, and print the output
on failure.
The core commands, one by one
Joomla 6.2 registers about 35 core commands. The table groups them; the sections that follow give arguments, options, behaviour and pitfalls. The "since" column gives the release in which the command appeared where it is newer than 4.0.
| Command | Purpose | Since |
|---|---|---|
|
Clear the system cache, or only expired entries |
4.0 |
|
Show one option or a group of options of |
4.0 |
|
Set one or more options in |
4.0 |
|
Refresh and report available Joomla core updates |
4.0 |
|
Update the Joomla core |
4.0 |
|
Show or set the core update channel |
5.1 |
|
Register or unregister the site for the Automated Core Updates service |
5.4 |
|
Export or import tables as XML (optionally zipped) |
4.0 |
|
Check the database schema of core and extensions, optionally fix it |
5.1 |
|
List installed extensions |
4.0 |
|
Install from a path or URL, or remove by ID |
4.0 |
|
Enable or disable an extension by ID |
6.1 |
|
Find, list and install extensions copied to the file system |
4.0 |
|
Build, or purge and rebuild, the Smart Search index |
4.0 |
|
List, enable/disable/trash and run scheduled tasks |
4.1 |
|
Garbage-collect sessions and session metadata |
4.0 |
|
Put the site offline or online |
4.0 |
|
Create a public folder for the web server’s document root |
5.0 |
|
Refresh and report extension updates |
4.0 |
|
Remove files that earlier releases left behind |
4.0 |
|
Manage users |
4.0 |
The two database commands live in the Joomla Framework database package rather than in the CMS tree, but they are registered by the same service provider and behave like core commands.
Older books describe the CLI of their own release. Joomla! 4 Masterclass (2023) targets Joomla 4.x and Developing
Extensions for Joomla! 5 (2023) targets Joomla 5.0, so neither lists core:update:channel and maintenance:database
(5.1), core:autoupdate:* (5.4) or extension:enable/extension:disable (6.1). Run list on the installed version
rather than relying on a printed command table. The same applies to any tutorial that uses the Joomla 3 style cli/*.php
scripts extending JApplicationCli: those scripts no longer have a base class on Joomla 6 (see
Replacing legacy CLI scripts).
cache:clean
php cli/joomla.php cache:clean # clear all system cache entries
php cli/joomla.php cache:clean expired # clear only expired entries
The optional argument is the literal word expired. Without it, the command clears every entry of the system cache
through the com_cache model; with it, only entries past their lifetime. It reports Cache cleaned or Expired Cache
cleaned and returns failure when the model cannot clear the cache. It clears the cache that Joomla’s cache handler
manages (file, Redis, memcached, APCu depending on cache_handler); it does not clear the browser, the web server or a
CDN. For a Redis cache handler, the cache and its connection settings are covered in
Caching and performance and the server itself in
Redis. Run cache:clean after deploying a template, an override or a language file when
the cache is enabled, and cache:clean expired from cron when you use the file handler, because expired files are
otherwise only removed by the web request that notices them.
config:get and config:set
config:get shows options from configuration.php (the JConfig class), and config:set writes them back through the
com_config application model, so it validates keys and writes the file the same way the Global Configuration screen does.
php cli/joomla.php config:get sitename
php cli/joomla.php config:get offline
php cli/joomla.php config:get --group session
php cli/joomla.php config:get --group mail
php cli/joomla.php config:set debug=true
php cli/joomla.php config:set sitename="Tallpine Hiking Club" offline_message="Back in ten minutes"
php cli/joomla.php config:set session_handler=redis caching=2 cache_handler=redis
Source: Joomla User Guide: Using the CLI
-
config:get <option>prints a one-row table. An unknown name printsCan't find optionand returns2. Booleans are shown as the wordstrueandfalse. -
config:get --group <name>(short-g) prints a table for one of three groups:db(dbtype,host,user,password,dbprefix,db, thedbssl*options),session(session_handler,shared_session,session_metadata) andmail(mailonline,mailer,mailfrom,fromname,sendmail,smtpauth,smtpuser,smtppass,smtphost,smtpsecure,smtpport). An unknown group returns1. -
config:settakes one or moreoption=valuearguments. Every pair must contain=, and every key must already exist inJConfig, otherwise the command stops withCan't find optionand nothing is saved. Values containing spaces need shell quoting. Database settings are validated (type, prefix pattern, name length) before they are saved.
Two security consequences follow. First, config:get --group db and --group mail print the database password and the
SMTP password in clear text, which ends up in a terminal scrollback, a CI log or a cron mail. Never run them in a
pipeline whose log is shared. Second, values given to config:set are visible in the process list and shell history, so
do not pass password=..., smtppass=... or secret=... on the command line of a shared host. Provide secrets by
editing configuration.php from a deployment template or from environment variables, as shown in
configuration.php and .htaccess.
config:set needs a writable configuration.php. On a locked-down production server (file made read-only on purpose) the
command fails to save; change configuration in the deployment instead. After config:set the new values apply
immediately to the next request, but a cached page or an opcache that was told not to revalidate may keep serving old
values until cache:clean or an opcache reset.
site:down and site:up
php cli/joomla.php site:down
# ... deploy ...
php cli/joomla.php site:up
Source: Joomla source: SiteDownCommand.php and SiteUpCommand.php
site:down is a shortcut for config:set offline=true and site:up for offline=false. They run the configuration
command internally, print Website is now offline or online, and return 0 on success. The front end then shows the
offline page and message configured in Global Configuration; administrators who are logged in can still reach the site
and the back end (see Error, offline and component pages).
A deploy script should call site:up in a trap so that a failing step does not leave the site offline.
Core updates: core:update:check, core:update, core:update:channel
php cli/joomla.php core:update:check
php cli/joomla.php core:check-updates # same command, older name
php cli/joomla.php core:update:channel # show the channel
php cli/joomla.php core:update:channel default
php cli/joomla.php core:update:channel next
php cli/joomla.php core:update:channel custom --url=https://updates.tallpine.example/list.xml
php cli/joomla.php core:update
Source: Joomla User Guide: Using the CLI
-
core:update:checkpurges the cached update information, refreshes it from the update server and prints whether a newer Joomla version exists. Its original namecore:check-updatesis kept as an alias and is the spelling used in the User Guide; the source markscore:update:checkas the preferred name. -
core:updateperforms the update of the core. It runs these steps with a progress bar: it starts logging (and stops if the update log cannot be written), reads the update information, checks the database schema and refuses to continue when the schema has errors, runs the extra pre-conditions when the target is a new major version, and then downloads and installs the package. It printsJoomla core updated successfully!and returns0; it returns1when a check fails (including the caseYou already have the latest Joomla! version) and2when the update fails. -
core:update:channel(Joomla 5.1 and later) shows the current channel, or sets it todefault,nextorcustom.customrequires--url. Thedefaultchannel is the stable channel;nextis the channel used to move to the next major release (for example from 5.4 to 6.0 or from 6.2 to 7.0 when they exist).
The CLI update is the same update that the Joomla Update component performs, minus the web steps. It does not take a backup. Core has no backup feature (see Backups and recovery), so export the database and snapshot the files first (see database:export, database:import and maintenance:database). Automated Core Updates, available from Joomla 5.4, are a separate mechanism that updates minor, patch and security releases without anyone running a command; they are described in Updating Joomla and Automated Core Updates and in the next section.
When a major upgrade is involved (5.4 to 6.x), the pre-condition check inside core:update is not a substitute for the
procedure in Upgrading Joomla 5 to 6: update to the latest 5.4.x, disable the
old Behaviour backward-compatibility plugin, enable the Behaviour - Backward Compatibility 6 plugin (the two cannot both
be on), then switch the channel and update. The pre-update check is not a reliable source of truth on its own.
core:autoupdate:register and core:autoupdate:unregister
Joomla 5.4 introduced Automated Core Updates for minor, patch and security releases: a joomla.org service triggers the site through Joomla Update web-service endpoints, and the site performs the update itself. It is on by default for new installs (the installer offers an opt-out) and off for sites that were upgraded. The service needs a publicly reachable site, the Default update channel, "Stable" as the minimum stability, and Joomla 5.4 or newer; core packages have been verified with TUF (The Update Framework) since 5.1.
php cli/joomla.php core:autoupdate:register --live-site=https://www.tallpine.example
php cli/joomla.php core:autoupdate:unregister
Source: Joomla source: AutomatedUpdatesRegisterCommand.php and AutomatedUpdatesUnregisterCommand.php
The commands register or unregister the current site for the service. register requires --live-site with a scheme and
a host and exits with an error otherwise (Missing --live-site option, Incomplete --live-site provided). Use them to
switch the service on or off for a fleet of sites from a provisioning script, or to unregister a staging copy that was
cloned from production and would otherwise register a second time under the same address.
database:export, database:import and maintenance:database
php cli/joomla.php database:export --folder=/backups/tallpine/2026-10-30
php cli/joomla.php database:export --folder=/backups/tallpine --zip
php cli/joomla.php database:export --table=jos_content --folder=/tmp
php cli/joomla.php database:import --folder=/backups/tallpine/2026-10-30
php cli/joomla.php database:import --zip=/backups/tallpine/tallpine-db.zip
php cli/joomla.php database:import --table=jos_content --folder=/tmp
php cli/joomla.php maintenance:database
php cli/joomla.php maintenance:database --fix
Source: Joomla User Guide: Using the CLI
database:export writes one XML file per table (structure plus data) into a folder (default: the current directory),
or a single table with --table, and packs everything into a ZIP archive with --zip. It needs the joomla/filesystem
Composer package and, for --zip, joomla/archive; both ship with Joomla 6.2. database:import reads the XML files
of a folder, or a ZIP with --zip, or a single table with --table. For each table it drops the existing table,
recreates the structure and inserts the data, so importing is destructive for the tables it touches. The User Guide
warns against using these commands to import all tables of a multilingual site, because that can break Smart Search
(the Finder terms and taxonomy tables are rebuilt by indexing). Import the content tables you need and re-run
finder:index purge afterwards. The table names include the prefix of the site (jos_ here is a placeholder; read the
real prefix with config:get dbprefix).
These commands are a convenience for moving small sites and single tables, not a replacement for mysqldump,
mariadb-dump or pg_dump, which are faster, handle routines, triggers and large data and are what a real backup
strategy uses; the portable dump tools are covered in Backups and recovery.
The XML format is Joomla-specific and the commands cannot translate between MySQL/MariaDB and PostgreSQL: Joomla does
not support migrating between database engines.
maintenance:database (Joomla 5.1 and later) runs the same check as System > Maintenance > Database: for core and each
extension it compares the schema version and the update scripts with the database and prints a table of problems.
Without --fix it only reports; with --fix it applies the missing changes. Run it after every core or extension update
and in the pipeline, because a missing column from an update script is the usual reason for a white page after a deploy
(see Installer scripts and SQL updates).
Extensions
php cli/joomla.php extension:list
php cli/joomla.php extension:list --type=plugin
php cli/joomla.php extension:install --path=/tmp/pkg_tallpine_1.4.0.zip
php cli/joomla.php extension:install --url=https://downloads.tallpine.example/pkg_tallpine_1.4.0.zip
php cli/joomla.php extension:disable 10234
php cli/joomla.php extension:enable 10234
php cli/joomla.php extension:remove 10234
php cli/joomla.php extension:remove 10234 --no-interaction
php cli/joomla.php extension:discover
php cli/joomla.php extension:discover:list
php cli/joomla.php extension:discover:install --eid=10234
php cli/joomla.php extension:discover:install
Source: Joomla User Guide: Using the CLI
-
extension:listprints a table of installed extensions with their IDs.--typefilters by type (component,module,plugin,template,library,package,file,language). The numeric extension ID in the first column is the identifier every other extension command uses, and it differs between sites: the ID ofpkg_tallpineon staging is not its ID on production, so look it up by name in the script (for example by parsingextension:list --type=package) instead of hard-coding it. -
extension:install --path=installs from a ZIP or a folder on the server,--url=downloads a package first. Both go through the same installer as System > Install > Extensions, so manifests, install scripts and SQL run exactly as in the web installer (see Installing and updating extensions). Install from a URL only when you control the address and use HTTPS, as with any package from outside your own build. -
extension:enableandextension:disable(Joomla 6.1 and later) take the extension ID. Before 6.1 the only way to switch an extension from the shell was a SQL update on#__extensions. -
extension:removeasksAre you sure you want to remove this extension?and expectsyes; with--no-interactionit proceeds without asking. It refuses to remove locked core extensions and returns a different non-zero code for an unknown ID, a locked extension, an invalid type, a failed uninstall and a declined prompt. A script must never pass--no-interactiontogether with an ID it has not just looked up. -
The
discoverfamily handles extensions whose files were copied onto the server instead of installed (a Git deployment, a Composer-managedpluginsdirectory).extension:discoverscans the file system and records new extensions as discovered;extension:discover:listshows them;extension:discover:installinstalls one (with--eid=, as the command’s option name says) or, without it, all of them. This is how a CI deploy that rsyncsplugins/console/tallpineactivates the plugin without opening the back end.
A deployment that ships pkg_tallpine as a ZIP looks like this:
set -euo pipefail
php cli/joomla.php site:down
trap 'php cli/joomla.php site:up' EXIT
php cli/joomla.php database:export --zip --folder="/backups/tallpine/$(date +%F)"
php cli/joomla.php extension:install --path=/deploy/pkg_tallpine_1.4.0.zip
php cli/joomla.php maintenance:database --fix
php cli/joomla.php cache:clean
Source: Joomla User Guide: Using the CLI
update:extensions:check and update:joomla:remove-old-files
php cli/joomla.php update:extensions:check
php cli/joomla.php update:joomla:remove-old-files --dry-run
php cli/joomla.php update:joomla:remove-old-files
update:extensions:check refreshes the update sites of every installed extension and prints a table with the columns
Extension ID, Name, Location, Type, Installed, Available and Folder, or There are no updates available.. It only
reports; there is no command that applies extension updates in 6.2, so installing the update is done in System >
Update > Extensions or with extension:install of the downloaded package. From Joomla 6.2 extension updates carry a
security-severity flag in the update information, which the back end shows; a monitoring script can combine this
command with the web view to alert on security updates. Extension update sites and the JED are covered in
Update servers and the JED.
update:joomla:remove-old-files removes the files and folders that the update script lists as obsolete. The core
update script keeps a list of files and folders that earlier releases shipped and later ones dropped; this command
removes the entries of that list from the shell. Run it with --dry-run first: it reports how many files and folders it checked and how many
it would delete, and -vv prints each match. Do not run it on a tree that holds your own files in a core directory.
Smart Search: finder:index
php cli/joomla.php finder:index
php cli/joomla.php finder:index purge
php cli/joomla.php finder:index --minproctime=2 --pause=3 --divisor=5
finder:index builds the Smart Search index for all enabled Smart Search content plugins. The argument purge empties
the index (keeping search filters) and then rebuilds it. Indexing a large site in one pass can saturate the database, so
the command throttles itself: --minproctime is the minimum processing time in seconds of a batch before a pause
is applied (default 1), --pause is either the word division (default) or a fixed number of seconds, and --divisor
(default 5) divides the batch time to compute the pause. Use it from cron nightly, because the Smart Search - Content
plugin only indexes an item when it is saved in the back end, and items imported by tallpine:import or changed in SQL
are missing from the index until it runs. See Smart Search.
Scheduled tasks: scheduler:list, scheduler:state and scheduler:run
php cli/joomla.php scheduler:list
php cli/joomla.php scheduler:run # run the next due task
php cli/joomla.php scheduler:run --all # run every due task
php cli/joomla.php scheduler:run --id=7 # run task 7 now
php cli/joomla.php scheduler:state --id=7 --state=disable
php cli/joomla.php scheduler:state -i 7 -s 1
php cli/joomla.php scheduler:state # interactive
Source: Joomla User Guide: Using the CLI
Scheduled tasks (Joomla 4.1 and later) are rows in the Scheduled Tasks component whose routine is supplied by a Task plugin; the Tallpine trail-closure sync is one. The CLI is the way to run them from a real scheduler.
-
scheduler:listprints all tasks with ID, title, type, state and next execution. -
scheduler:runwithout options runs the single next due task (the queue limit is one). With--allit runs every due task in sequence. With--id(short-i) it runs that task regardless of whether it is due, and also if it is disabled or already running, which makes--idthe right tool for a manual run and a bad one for a schedule. The output per task isTask#07 'Sync trail closures' processed in 0.84 seconds., a notice when a task ran out of time and will resume, a warning when it could not start (Is it already running?), or an error for an orphaned task whose plugin is gone. Exit codes report the same:0for success and non-zero otherwise, with a dedicated code forNo tasks due!. Acronwrapper that treats every non-zero code as an alert will therefore reportNo tasks due!as a failure unless the script tolerates that code; runscheduler:run --all -qand check the log instead, or run it every minute and ignore the "nothing due" case. -
scheduler:statechanges the state of one task:enable(or1),disable(or0) ortrash(or-2). Without options it asks interactively. Disable a task before deploying a new version of its plugin to avoid a run in the middle of an upgrade.
By default Joomla runs tasks lazily: the System - Schedule Runner plugin triggers due tasks from web requests, or
the web cron endpoint is called by an external service. For production, set the mode to Web Cron or switch the lazy
scheduler off and use the CLI from cron, because lazy scheduling ties task latency to site traffic and runs task code
inside a visitor’s request. The scheduler concepts and the task plugin API are in
Scheduled tasks and Scheduled task plugins.
Sessions: session:gc and session:metadata:gc
php cli/joomla.php session:gc
php cli/joomla.php session:gc --application=administrator
php cli/joomla.php session:metadata:gc
session:gc runs garbage collection of the session store for one application: site (the default) or administrator;
any other value is rejected as not a valid option. The --application option has the single-dash shortcut -app.
The command is only useful for session handlers that do not expire sessions by themselves: it reports
Garbage collection was not completed when the handler does not support it (a Redis handler expires keys by TTL, for
example). session:metadata:gc cleans the session metadata table that tracks who is online when the session_metadata
option is on. Typical schedule: once an hour for the database handler.
site:create-public-folder
php cli/joomla.php site:create-public-folder --public-folder=/var/www/tallpine-public
Joomla 5.0 introduced the optional public folder: a document root that contains only the files a browser needs, with
configuration.php, libraries and administrator code outside it. The command creates such a folder through the public-folder generator (it needs PHP’s symlink()) and prints Adjust your server configuration to serve from the public
folder. It asks for the path when the option is omitted. It does not change
the web server; point the document root at the new folder yourself, as described in
Installation and the public folder.
Users: user:*
php cli/joomla.php user:list
php cli/joomla.php user:add --username=warden1 --name="Trail Warden One" --email=warden1@tallpine.example --password="$WARDEN_PASSWORD" --usergroup="Trail Warden"
php cli/joomla.php user:addtogroup --username=warden1 --group="Editor"
php cli/joomla.php user:removefromgroup --username=warden1 --group="Editor"
php cli/joomla.php user:reset-password --username=warden1 --password="$WARDEN_PASSWORD"
php cli/joomla.php user:delete --username=warden1
Source: Joomla User Guide: Using the CLI
-
user:listprints a table of all users. -
user:addaccepts--username,--name,--password,--emailand--usergroup(several groups separated by commas). Any value left out is asked interactively, and the password prompt is hidden. The new user is created through the user model, so password policy and duplicate checks apply. -
user:addtogroupanduser:removefromgrouptake--usernameand--group(a group title, for exampleTrail WardenorSuper Users); if the option is omitted they ask, offering a list of groups. -
user:reset-passwordtakes--usernameand--password. -
user:deletetakes--usernameand asks for confirmation.
This is also the break-glass path when the only Super User has lost the password and the e-mail reset is broken: run
user:reset-password for that account, or user:add with --usergroup="Super Users". It does not remove multi-factor
authentication (MFA, in core since Joomla 4.2) of the account; a Super User locked out by an MFA method has to follow the
procedure in Security hardening, MFA and passkeys. Passing the
password with --password places it in the command line, visible to other users through the process list; prefer the
hidden prompt on interactive sessions, and on automation read it from an environment variable or secret store as in the
example ($WARDEN_PASSWORD). Groups, access levels and the Trail Warden group are modelled in
Users, groups, access levels and ACL.
Running the CLI from cron
On a server with a shell:
# /etc/cron.d/tallpine (runs as the web user; PHP path is absolute)
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
CLI="/usr/local/bin/php /var/www/tallpine/cli/joomla.php"
# Run due scheduled tasks every minute
* * * * * www-data $CLI -q scheduler:run --all
# Clear expired cache entries every 15 minutes
*/15 * * * * www-data $CLI -q cache:clean expired
# Garbage-collect sessions hourly
7 * * * * www-data $CLI -q session:gc
17 * * * * www-data $CLI -q session:gc --application=administrator
# Rebuild the Smart Search index at night
30 2 * * * www-data $CLI -q finder:index
# Look for core and extension updates, mail the output to the operator
0 6 * * 1 www-data $CLI core:update:check ; $CLI update:extensions:check
Source: Joomla User Guide: Using the CLI
On shared hosting the panel’s cron screen takes one command per job. Use the hosting provider’s PHP binary and the
absolute path, including a sub-folder when the site is not in the web root, as the User Guide shows:
/usr/local/bin/php /home/username/public_html/cli/joomla.php -q cache:clean. A hosting panel that offers several PHP
versions usually names the CLI binary per version (php8.3); use the same major version as the site.
Pitfalls that cause silent failures:
-
Overlap. A job that takes longer than its interval starts a second copy. Wrap long jobs with
flock -n /run/lock/tallpine-finder.lock …, or rely on the scheduler’s own concurrency guard for tasks. -
Wrong user. A job in root’s crontab creates root-owned cache and log files. Use
/etc/cron.dwithwww-dataor the web user’s crontab. -
-qhides the reason. With-qa failing command prints nothing but exits non-zero. Write a wrapper that logs the command’s output to a file when the exit code is non-zero. -
Memory and time. The command-line PHP has no
max_execution_timebut hasmemory_limit.finder:indexon a large site needs more memory than a web request; passphp -d memory_limit=1G. -
The wrong URL in e-mails. Add
--live-site=https://www.tallpine.exampleto jobs that send mail (tasks that mail reports) or set$live_siteinconfiguration.php.
Docker and Compose
In a containerised site the CLI runs inside the application container, so the PHP version, extensions and the database host are exactly those of the web process. A Compose file for the Tallpine site with the official image, MariaDB and Mailpit (the full local environment is in Local development with Docker):
services:
joomla:
image: joomla:6.2-php8.3-apache
ports:
- "8080:80"
environment:
JOOMLA_DB_HOST: db
JOOMLA_DB_USER: tallpine
JOOMLA_DB_PASSWORD: ${TALLPINE_DB_PASSWORD:?set it in .env}
JOOMLA_DB_NAME: tallpine
volumes:
- joomla_data:/var/www/html
depends_on:
- db
db:
image: mariadb:11.4
environment:
MARIADB_DATABASE: tallpine
MARIADB_USER: tallpine
MARIADB_PASSWORD: ${TALLPINE_DB_PASSWORD:?set it in .env}
MARIADB_RANDOM_ROOT_PASSWORD: "1"
volumes:
- db_data:/var/lib/mysql
mailpit:
image: axllent/mailpit:latest
ports:
- "8025:8025"
volumes:
joomla_data:
db_data:
Source: Docker Official Image: joomla
Run commands with docker compose exec, as the web user, in the working directory /var/www/html of the image:
docker compose exec -u www-data joomla php cli/joomla.php list
docker compose exec -u www-data joomla php cli/joomla.php extension:list --type=plugin
docker compose exec -u www-data joomla php cli/joomla.php user:add \
--username=admin2 --name="Second Admin" --email=admin2@tallpine.example \
--usergroup="Super Users"
# one-off job container sharing the same volumes and network
docker compose run --rm -u www-data joomla php cli/joomla.php scheduler:run --all
Source: Docker Official Image: joomla
Do not run the scheduler by exec from the host’s cron on a multi-container deployment without thinking about where the
job runs: with several replicas behind a load balancer (see Docker Compose in
practice and Kubernetes), run exactly one scheduler process, for example a
Kubernetes CronJob that starts the same image with the command php cli/joomla.php scheduler:run --all, sharing
the database and the writable volumes of the site. Sessions, cache and media must then live in shared storage (database or
Redis sessions, a shared images volume), as discussed in Multi-site
options and scaling. The official image is built from the release package; installing extensions into the running
container writes into the joomla_data volume, not into the image, so rebuild or re-install after a container is recreated
without that volume.
Running the CLI in CI
A pipeline job that deploys a release to staging and checks the result:
name: deploy-staging
on:
push:
tags: ["v*"]
jobs:
deploy:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- name: Upload the package
run: scp build/pkg_tallpine.zip "$DEPLOY_USER@$DEPLOY_HOST:/deploy/"
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
- name: Install and verify through the CLI
run: |
ssh "$DEPLOY_USER@$DEPLOY_HOST" bash -s <<'REMOTE'
set -euo pipefail
cd /var/www/tallpine
CLI="php cli/joomla.php --no-ansi"
$CLI site:down
trap '$CLI site:up' EXIT
$CLI database:export --zip --folder=/backups/tallpine/pre-deploy
$CLI extension:install --path=/deploy/pkg_tallpine.zip
$CLI maintenance:database --fix
$CLI cache:clean
$CLI extension:list --type=package | grep -q pkg_tallpine
REMOTE
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
Source: Joomla User Guide: Using the CLI
Tips for pipelines: use --no-ansi so the log has no escape codes, use --no-interaction for any command that may
prompt (and never answer a destructive prompt blindly), keep secrets in the CI secret store and pass them through
environment variables, and fail the job on a non-zero exit code. For test jobs, build a throw-away site in a service
container with php installation/joomla.php install and run extension:install plus the extension’s tests against it;
the extension build and test setup is covered in Build and CI for extensions.
A smoke test that runs list and checks that tallpine:import appears proves that the console plugin is installed and enabled.
A maintenance flow in one picture
The console plugin group
Core commands are registered by a service provider, and everything else is contributed by plugins of the console group.
On every CLI start the console application imports the behaviour, system and console groups on its dispatcher,
so an enabled console plugin can subscribe to events. A console plugin registers its commands in a listener for the
Joomla Framework event ApplicationEvents::BEFORE_EXECUTE, which runs before the Symfony application resolves the
command, by calling addCommand() on the application. The application also dispatches Symfony Console events such as
BEFORE_COMMAND_EXECUTE and COMMAND_ERROR, which a plugin can use for logging or for blocking commands.
A minimal plugin for the Tallpine site; the full walk-through, the command class with arguments and options, and testing are in Console command plugins.
<?php
namespace Tallpine\Plugin\Console\Tallpine\Extension;
use Joomla\Application\ApplicationEvents;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Tallpine\Plugin\Console\Tallpine\Command\ImportTrailsCommand;
\defined('_JEXEC') or die;
final class Tallpine extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [ApplicationEvents::BEFORE_EXECUTE => 'registerCommands'];
}
public function registerCommands(): void
{
$this->getApplication()->addCommand(new ImportTrailsCommand());
}
}
Source: Joomla Manual: How plugins work
The command class extends Joomla\Console\Command\AbstractCommand and declares its name with $defaultName:
<?php
namespace Tallpine\Plugin\Console\Tallpine\Command;
use Joomla\Console\Command\AbstractCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
\defined('_JEXEC') or die;
final class ImportTrailsCommand extends AbstractCommand
{
protected static $defaultName = 'tallpine:import';
protected function configure(): void
{
$this->setDescription('Import trails from a GPX folder');
$this->addOption('folder', null, InputOption::VALUE_REQUIRED, 'Folder with GPX files');
}
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$folder = (string) $input->getOption('folder');
if ($folder === '' || !is_dir($folder)) {
$io->error('Pass an existing folder with --folder.');
return Command::FAILURE;
}
// ... create one trail per GPX file ...
$io->success('Trails imported.');
return Command::SUCCESS;
}
}
Concerns specific to console plugins:
-
The plugin must be installed and enabled in the
consolegroup;listis the check. A disabled plugin givesCommand "tallpine:import" is not defined. -
The CLI loads only the behaviour, system and console groups. Plugins of the
content,userortaskgroups are not imported automatically, so a command that triggers content events has to import them itself, and a command written for the web cannot assume a session, a logged-in user orHTTP_HOST. -
Name commands
vendor:action(tallpine:import) to avoid clashing with core namespaces. Core command classes arefinal. -
Joomla 6.0 removed the CMS
CliApplicationandBaseApplication; console plugins are the replacement (see the next section).
Replacing legacy CLI scripts
Joomla 3 style CLI scripts placed in cli/ (a class extending JApplicationCli) were
replaced by the Symfony Console in Joomla 4. On Joomla 5 the old CMS CliApplication and BaseApplication still exist for
compatibility; on Joomla 6.0 they are available only through the Behaviour - Backward Compatibility 6 plugin and are
scheduled for removal, together with \Joomla\CMS\Input (use \Joomla\Input). The migration is mechanical: turn the script
into a command class, register it from a console plugin, replace $this->out() with SymfonyStyle output and
$this->input->get() with InputInterface options. Books and blog posts that show a cli/myscript.php that bootstraps
by require and JFactory::getApplication('cli') describe a platform that is end of life; the same applies to uses of
the J* class aliases (JFactory, JText), which are about 450 entries of the BC6 class map and are off by default on
new 6.x installs. See Deprecations and preparing for Joomla 7
and Error handling and legacy APIs.
Security of the CLI
-
Authorisation is the file system and the shell. The CLI has no login, no ACL and no audit trail of its own. Anyone who can execute PHP as a user that can read
configuration.phpcan create Super Users, change the global configuration and read database credentials. Restrict shell and SSH access, and keepconfiguration.phpunreadable for other users. -
The web server must not serve
cli/. The script refuses to run outside the command line (the application closes when it is not executed from the CLI), but the directory should still not be web-reachable. With the public folder (Joomla 5.0 and later) it is not, because only the public assets are exposed (Installation and the public folder). -
Do not put secrets on command lines. Arguments appear in
ps, in shell history and in CI logs. Use the hidden prompts ofuser:addanduser:reset-passwordinteractively, read values from environment variables or files, and do not runconfig:get --group dbor--group mailin logged pipelines. -
Destructive commands.
database:importdrops tables,extension:remove --no-interactionremoves without asking,update:joomla:remove-old-filesdeletes files. Preview with--dry-runwhere it exists, back up first, and run against staging before production. -
Keep an eye on the log.
core:updatewrites the update log; the action log of the back end (see Privacy and action logs) records web actions, not CLI-created users, so record CLI operations in the deployment system.
Common errors and how to read them
| Message or symptom | Cause and remedy |
|---|---|
|
The command-line PHP is older than 8.3.0. Check |
|
|
|
|
|
A typo, a command that does not exist in this Joomla version ( |
|
|
|
|
|
|
|
|
|
The task’s plugin is disabled or uninstalled. Enable the plugin or trash the task with |
|
The session handler does not support garbage collection or the operation failed. |
|
Reported by |
Files owned by root, 403 or cache errors in the web |
The CLI was run as |
Links to |
No |
Version notes
-
Joomla 4.0 introduced the console application and replaced the Joomla 3
cli/*.phpscripts. -
4.1 added the scheduler commands, 5.0 added
site:create-public-folder, 5.1 addedcore:update:channelandmaintenance:database, 5.4 addedcore:autoupdate:registerandcore:autoupdate:unregister, and 6.1 addedextension:enableandextension:disable. -
The
--live-siteglobal option was added in 4.2.1. -
Joomla 6.0 removed the CMS-level CLI application classes from the default class map (BC6 plugin only); commands are written as console plugins.
-
Joomla 6.x requires PHP 8.3 or newer on the command line as well as on the web. The release cycle (a major every two years with four minors, 5.x security-only, 4.x end of life) is in Versions, release cycle and support.
-
The CLI help is the primary documentation for options and may change between minors: pin the Joomla version in scripts and re-read
help <command>after each upgrade.
Where to go next
-
Console command plugins writes the
tallpine:importcommand in full. -
Scheduled tasks and Scheduled task plugins cover what
scheduler:runexecutes. -
Updating Joomla and Automated Core Updates and Backups and recovery cover the processes the CLI automates.
-
System information, maintenance and troubleshooting covers the matching back-end screens.