Command Line

Flow features a clean and powerful interface for the command line which allows for automated and manual execution of low-level or application-specific tasks. The command line support is available on all platforms generally supported by Flow.

This chapter describes how to use the help system, how to run existing commands and how to implement your own custom commands.

Wrapper Script

Flow uses two platform specific wrapper scripts for running the actual commands:

  • flow.bat is used on Windows machines

  • flow is used on all other platforms

Both files are located and must be run from the main directory of the Flow installation. The command and further options are passed as arguments to the respective wrapper script.

In the following examples we refer to these wrapper scripts just as “the flow script”.


If you are a Windows user and use a shell like msysGit, you can mostly follow the Unix style examples and use the flow script instead of flow.bat.

Help System

Without specifying a command, the flow script responds by displaying the current version number and the current context:

$ ./flow
Flow 2.x.x ("Development" context)
usage: ./flow <command identifier>

See "./flow help" for a list of all available commands.

In addition to the packages delivered with the Flow core, third-party packages may provide any number of custom commands. A list of all currently available commands can be obtained with the help command:

$ ./flow help
Flow 2.x.x ("Development" context)
usage: ./flow <command identifier>

The following commands are currently available:

PACKAGE "Neos.Flow":
* flow:cache:flush                         Flush all caches
  cache:warmup                             Warm up caches

  configuration:show                       Show the active configuration
  configuration:validate                   Validate the given configuration
  configuration:generateschema             Generate a schema for the given
                                           configuration or YAML file.

A list of all commands in a specific package can be obtained by giving the package key part of the command to the help command:

$ ./flow help kickstart
5 commands match the command identifier "neos.kickstart":

kickstart:package                        Kickstart a new package
kickstart:actioncontroller               Kickstart a new action controller
kickstart:commandcontroller              Kickstart a new command controller
kickstart:model                          Kickstart a new domain model
kickstart:repository                     Kickstart a new domain repository

Further details about specific commands are available by specifying the respective command identifier:

$ ./flow help configuration:show

Show the active configuration settings


  ./flow configuration:show [<options>]

  --type               Configuration type to show
  --path               path to subconfiguration separated by "." like

  The command shows the configuration of the current context as it is used by Flow itself.
  You can specify the configuration type and path if you want to show parts of the configuration.

  ./flow configuration:show --type Settings --path Neos.Flow.persistence

Running a Command

Commands are uniquely identified by their command identifier. These come in two variants: a long and a short version.

Fully Qualified Command Identifier

A fully qualified command identifier is the combination of the package key, the command controller name and the actual command name, separated by colons:

The command “warmup” implemented by the “CacheCommandController” contained in the package “Neos.Flow” is referred to by the command identifier neos.flow:cache:warmup.

Short Command Identifier

In order to save some typing, most commands can be referred to by a shortened command identifier. The help command lists all commands by the shortest possible identifier which is still unique across all available commands.

For example, the command “warmup” implemented by the “CacheCommandController” contained in the package “Neos.Flow” can also be referred to by the command identifier cache:warmup as long as no other package provides a command with the same name.

Some special commands can only by referred to by their fully qualified identifier because they are invoked at a very early stage when the command resolution mechanism is not yet available. These Compile Time Commands are marked by an asterisk in the list of available commands (see Symfony/Console Methods for some background information).

Passing Arguments

Arguments and options can be specified for a command in the same manner they are passed to typical Unix-like commands. A list of required arguments and further options can be retrieved through the help command.


Options listed for a command are optional and only have to be specified if needed. Options must always be passed before any arguments by using their respective name:

./flow foo:bar --some-option BAZ --some-argument QUUX

If an option expects a boolean type (that is, yes/no, true/false, on/off would be typical states), just specifying the option name is sufficient to set the option to true:

./flow foo:bar --force

Alternatively the boolean value can be specified explicitly:

./flow foo:bar --force true
./flow foo:bar --force false

Possible values equivalent to true are: on, 1, y, yes, true. Possible values equivalent to false are: off, 0, n, no, false.


The arguments listed for a command are mandatory. They can either be specified by their name or without an argument name. If the argument name is omitted, the argument values must be provided in the same order like in the help screen of the respective command. The following two command lines are synonymic:

./flow kickstart:actioncontroller --force --package-key Foo.Bar --controller-name Baz
./flow kickstart:actioncontroller --force Foo.Bar Baz


If not configured differently by the server environment, the flow script is run in the Development context by default. It is recommended to set the FLOW_CONTEXT environment variable to Production on a production server – that way you don’t execute commands in an unintended context accidentally.

If you usually run the flow script in one context but need to call it in another context occasionally, you can do so by temporarily setting the respective environment variable for the single command run:

FLOW_CONTEXT=Production ./flow flow:cache:flush

In a Windows shell, you need to use the SET command:

flow.bat flow:cache:flush

Implementing Custom Commands

A lot of effort has been made to make the implementation of custom commands a breeze. Instead of writing configuration which registers commands or coming up with files which provide the help screens, creating a new command is only a matter of writing a simple PHP method.

A set of commands is bundled in a Command Controller. The individual commands are plain PHP methods with a name that ends with the word “Command”. The concrete command controller must be located in a “Command” namespace right below the package’s namespace.

The following example illustrates all the code necessary to introduce a new command:

namespace Acme\Demo\Command;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cli\CommandController;

class CoffeeCommandController extends CommandController

         * Brew some coffee
         * This command brews the specified type and amount of coffee.
         * Make sure to specify a type which best suits the kind of drink
         * you're aiming for. Some types are better suited for a Latte, while
         * others make a perfect Espresso.
         * @param string $type The type of coffee
         * @param integer $shots The number of shots
         * @param boolean $ristretto Make this coffee a ristretto
        public function brewCommand(string $type, int $shots = 1, bool $ristretto = false): void
                # implementation

The new controller and its command is detected automatically and the help screen is rendered by using the information provided by the method code and DocComment:

  • the first line of the DocComment contains the short description of the command

  • the second line must be empty

  • the the following lines contain the long description

  • the descriptions of the @param annotations are used for the argument descriptions

  • the type specified in the @param annotations is used for validation and to determine if the argument is a flag (boolean) or not

  • the parameters declared in the method set the parameter names and tell if they are arguments (mandatory) or options (optional). All arguments must be placed in front of the options.

The above example will result in a help screen similar to this:

$ ./flow help coffee:brew

Brew some coffee


  ./flow coffee:brew

  This command brews the specified type and amount of coffee.

  Make sure to specify a type which best suits the kind of drink
  you're aiming for. Some types are better suited for a Latte, while
  others make a perfect Espresso.

Handling Exceeding Arguments

Any arguments which are passed additionally to the mandatory arguments are considered to be exceeding arguments. These arguments are not parsed nor validated by Flow.

A command may use exceeding arguments in order to process an variable amount of parameters. The exceeding arguments can be retrieved through the Request object as in the following example:

 * Process words
 * This command processes the given words.
 * @param string $operation The operation to execute
 * @return string
public function processWordCommand($operation = 'uppercase') {
        $words = $this->request->getExceedingArguments();
        foreach ($words as $word) {

A typical usage of the command above may look like this:

$ ./flow foo:processword --operation lowercase These Are The Words

these are the words

See Other and Deprecated Commands

A command’s help screen can contain additional information about relations to other commands. This information is triggered by specifying one or more @see annotations in the command’s doc comment block as follows:

 * Drink juice
 * This command provides some way of drinking juice.
 * @return string
 * @see acme.demo:drink:coffee
public function juiceCommand(): string

By adding a @deprecated annotation, the respective command will be marked as deprecated in all help screens and a warning will be displayed when executing the command. If a @see annotation is specified, the deprecation message additionally suggests to use the command mentioned there.

 * Drink tea
 * This command urges you to drink tea.
 * @return string
 * @deprecated since 2.8.18
 * @see acme.demo:drink:coffee
public function teaCommand(): string

Generating Styled Output

A limited number of tags are supported for brushing up the output. They have the following meaning:




Render the text in a bold / bright style


Render the text in a italics


Underline the given text


Emphasize the text, usually by inverting foreground and background colors


Display the text struck through


Display an error message, usually in red color


Display a success message, usually in green color

The respective styles are only rendered correctly if the console supports ANSI styles.


The tags supported by Flow can also be used to style the description of a command in its DocComment.

Symfony/Console Methods

The CommandController makes use of Symfony/Console internally and provides various methods directly from the CommandController’s output member:

  • TableHelper

    • outputTable(array $rows, array $headers = null): void

  • DialogHelper

    • select($question, array $choices, $default = null, bool $multiSelect = false, int $attempts = null)

    • ask($question, string $default = null)

    • askConfirmation($question, bool $default = true): bool

    • askHiddenResponse($question, bool $fallback = true)

    • askAndValidate($question, callable $validator, int $attempts = null, string $default = null)

    • askHiddenResponseAndValidate($question, callable $validator, int $attempts = null, bool $fallback = true)

  • ProgressHelper

    • progressStart(int $max = null): void

    • progressSet(int $current): void

    • progressAdvance(int $step = 1): void

    • progressFinish(): void

Here’s an example showing of some of those functions:

namespace Acme\Demo\Command;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cli\CommandController;

class MyCommandController extends CommandController {

        public function myCommand(): void
                // render a table
                                ['Bob', 34, 'm'],
                                ['Sally', 21, 'f'],
                                ['Blake', 56, 'm']
                        ], ['Name', 'Age', 'Gender']

                // select
                $selectedColor = $this->output->select('Please select one color', ['red', 'blue', 'yellow'], 'red');
                $this->outputLine('You choose the color %s.', [$selectedColor]);

                // ask
                $name = $this->output->ask('What is your name?' . PHP_EOL, 'Bob');
                $this->outputLine('Hello %s.', array($name));

                // prompt
                $likesDogs = $this->output->askConfirmation('Do you like dogs?');
                if ($likesDogs) {
                        $this->outputLine('You do like dogs!');

                // progress
                for ($i = 0; $i < 300; $i ++) {

Runtime and Compile Time

The majority of the commands are run at point when Flow is fully initialized and all of the framework features are available. However, for certain low-level operations it is desirable to execute code much earlier in the boot process – during compile time. Commands like neos.flow:cache:flush or the internal compilation commands which render the PHP proxy classes cannot rely on a fully initialized system.

It is possible – also for custom commands – to run commands run during compile time. The developer implementing such a command must have a good understanding of the inner workings of the bootstrap and parts of the proxy building, because compile time has several limitations, including but not limited to the following:

  • dependency injection does not support property injection

  • aspects are not yet active

  • persistence is not yet enabled

  • certain caches have not been built yet

In general, all functionality which relies on proxy classes will not be available during compile time.

If you are sure that compile time is the right choice for your command, you can register it as a compile time command by running an API method in the boot() method of your package’s Package class:

namespace Acme\Foo;
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\Package\Package as BasePackage;

 * Acme.Foo Package
class Package extends BasePackage

         * Invokes custom PHP code directly after the package manager has been initialized.
         * @param Bootstrap $bootstrap The current bootstrap
         * @return void
        public function boot(Bootstrap $bootstrap): void
                $bootstrap->registerRequestHandler(new \Acme\Foo\Command\MyCommandController($bootstrap));

For more details you are encouraged to study the implementation of Flow’s own compile time commands.

Executing Sub Commands

Most command methods are designed to be called exclusively through the command line and should not be invoked internally through a PHP method call. They may rely on a certain application state, some exceeding arguments provided through the Request object or simply are compile time commands which must not be run from runtime commands. Therefore, the safest way to let a command execute a second command is through a PHP sub process.

The PHP bootstrap mechanism provides a method for executing arbitrary commands through a sub process. This method is located in the Scripts class and can be used as follows:

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Core\Booting\Scripts;

#[Flow\InjectConfiguration(package: "Neos.Flow")]
protected array $flowSettings;

public function runCommand() {
        $success = Scripts::executeCommand('', $this->flowSettings);

Sometimes it can be useful to execute commands asynchronously, for example when triggering time-consuming tasks where the result is not instantly required (like sending confirmation emails, converting files, …). This can be done with the Scripts::executeCommandAsync()* method:

public function runCommand() {
        $commandArguments = ['some-argument' => 'some value'];
        Scripts::executeCommandAsync('', $this->flowSettings, $commandArguments);


Because asynchronous commands are invoked in a separate thread, potential exceptions or failures will not be reported. While this can be desired, it might require additional monitoring on the command-side (e.g. a failure log).

Quitting and Exit Code

Commands should not use PHP’s exit() or die() method but rather let Flow’s bootstrap perform a clean shutdown of the framework. The base CommandController provides two API methods for initiating a shutdown and optionally passing an exit code to the console:

  • $this->quit($exitCode) stops execution right after this command, performs a clean shutdown of Flow.

  • $this->sendAndExit($exitCode) sends any output buffered in the Response object and exits immediately, without shutting down Flow.

The quit() method is the recommended way to exit Flow. The other command, sendAndExit(), is reserved for special cases where Flow is not stable enough to continue even with the shutdown procedure. An example for such a case is the neos.flow:cache:flush command which removes all cache entries which requires an immediate exit because Flow relies on caches being intact.