---
# System prepended metadata

title: FTL command-line interface
tags: [implemented]

---

# FTL command-line interface
**Author**: Wes
**Status**: ==draft== / in-review / accepted / rejected

## Description



## Motivation (optional)

We want to make the usage of `ftl` easy for people to understand and get help.

## Goals

- Ability to build without running the FTL server.
- Parallel builds.
- One shot builds - exit once all modules are built.
- Parallel deploys.

## Use cases

There's a bit of confusion now with `ftl serve` and `ftl dev` where things are still running from previous deploys or development cycles

### Development

This is the primary interactions most developers will have with FTL during day-to-day development

> Discussion: Should this replace other commands like `ftl serve`?

```
ftl dev <dirs>...
```

This will start the ftl cluster, build and deploy all modules in the specified dirs, and hot reload modules changes as needed. Long-term this will also include secrets, database migrations, etc..

### Testing/CI


Should be able to build and deploy everything and then run tests against the running cluster.

> Maybe this needs attention. Do we like this solution?
  
The current solution is something like this:
  ```sh
  ftl serve --background --recreate
  ftl deploy backend/modules
  
  # run tests here...
  
  ftl serve --stop
  ```

### Discrete commands

Primarily used to break down other commands into discrete steps for diagnosing issues when needed or interacting with production clusters.

#### ftl build
```
Usage: ftl build <dirs> ...

Build all modules found in the specified directories.

Arguments:
  <dirs> ...    Base directories containing modules.
```

#### ftl deploy

```
Usage: ftl deploy <dirs> ...

Build and deploy all modules found in the specified directories.

Arguments:
  <dirs> ...    Base directories containing modules.
```

#### ftl serve

> Maybe we get rid of this altogether?

```
ftl serve --background/--stop (maybe --recreate) (in separate process?)
```  

## Future use cases
- Developing single module locally while running against production cluster
  ```
  ftl fork/branch? <module(s)> //create new branch of production
  ```


## Non-Goals (optional)

- ...

## Design


#### Build

The `ftl build` command can be used to build an FTL module locally or in a CI environment.

This command will build a single module at a time. For building and deploying multiple modules, see `ftl deploy`.

```
ftl build --help

Usage: ftl build [<module-dir>]

Build an FTL module.

Arguments:
  [<module-dir>]    Directory containing ftl.toml
```

#### Deploy

Questions:
* Do we have a use case for deploying all modules? Seems like it would be useful for CI/testing to deploy everything. However, this will limit the options for a single deploy for things like replicas, etc. Maybe this would be different command? Or a flag on `ftl deploy`.
* 

```
ftl deploy --help
Usage: ftl deploy [<base-dir>]

Create a new deployment.

Arguments:
  [<base-dir>]    Directory to scan for FTL modules. All modules found, will be built and deployed until everything reconciles (or fails).

Flags:
      --replicas       
      --no-wait        Do not wait for the deploy to finish before exiting.
```

#### Dev

> specify list of directory

`ftl dev` should be a one-stop-shop for developing with FTL. Ideally, the typical developer workflow would be to open a terminal and run just `ftl dev .` which would do the following and more:
* Start the FTL cluster
* Build, deploy, and hot reload modules
* Provide infra such database, migrations, configuration, secrets, etc.

```
ftl dev --help

Develop on FTL. This command will start the FTL cluster, build, deploy, and hot reload modules with their dependencies.

Arguments:
  [<base-dir>]    Directory to watch for FTL modules
  
Flags:
  --watch=500ms            Watch template directory at this frequency and regenerate on change.
  --reconnect-delay=1s     Delay before attempting to reconnect to FTL.
 
```

Deprecated flags:

```bash!
# Do we need this failure delay? Seems like we would just not rebuild until something related to the failing module changes. Either a schema or filesystem change.
--failure-delay=500ms    Delay before retrying a failed deploy.

# It feels like `dev` is not the right place to have exit after deploy. Dev should be started and left running while developing. Similar to `npm run dev`.
--exit-after-deploy      Exit after all modules are deployed successfully.
```

> Notes

- Break the build system out into its own top level package in the FTL repo.
- (1) Add a system for building and optionally deploying a single module - extract schema, (optional?) build, (optional) deploy
- (2) Another system for repeatedly building in parallel until everything reconciles.
- (3) Live reloading system which will restart builds for individual modules.
- One of these systems needs to understand schema dependencies and which modules to rebuild when modules change.
- Must support "build and exit", probably just calling (2)?
- We will need a bounded work queue to avoid killing the users computer.
- We'll want a flag to control concurrency.
- Ideally each build system can feedback how concurrent it is. eg. if each Go build uses 10 cores, we don't want to build 10 Go modules at once.
- Alternatively, have a way to limit the concurrency of each build.

#### ftl serve

This command is used to start the FTL cluster. It can be used in local or production environments. For local environments, `ftl serve` will automatically start and remove both controllers and runners as needed. In production environments, this will be managed by k8s or something similar.

```bash
ftl serve --help

Start the FTL cluster

flags:
      --bind=http://localhost:8892         Starting endpoint to bind to and advertise to. Each controller and runner will
                                           increment the port by 1
      --allow-origins=ALLOW-ORIGINS,...    Allow CORS requests to ingress endpoints from these origins
                                           ($FTL_CONTROLLER_ALLOW_ORIGIN).
      --db-port=54320                      Port to use for the database.
      --recreate                           Recreate the database even if it already exists.
  -c, --controllers=1                      Number of controllers to start.
  -r, --runners=0                          Number of runners to start.
```


## Previous discussions...


#### ftl build

Build a single FTL module. Will look for and use the `ftl.toml` file in the specified `module-dir` to determine which build command to use.

```bash
ftl build --help

Build an FTL module.

Arguments:
[<module-dir>]    Directory containing ftl.toml
```

#### ftl deploy

Build and deploy 1 or more FTL modules. Will recursively scan the `base-dir` for modules (`ftl.toml` files) and leverage `ftl build` to build each module. This command will also use 

```bash
ftl deploy --help

Deploy all modules found within the `base-dir`

Arguments:
  [<base-dir>]    Directory to scan for FTL modules to deploy

flags:
  -n, --replicas=1    Number of replicas to deploy.
  -r, --retry=false   Retry failed builds with exponential backoff until they all succeed
  -w, --wait=false    Wait for modules to come online before exiting the command
```

#### ftl dev

Run FTL in development mode with hot reloading of modules, etc.

* Build and deploy all modules found. 
* Detect new modules being added or removed update deployments as necessary.
* Hot reload and rebuild, redeploy when changes are detected.
* Will exponentially back off if builds fail

```bash
ftl dev --help

Watch a directory for FTL modules. Build, deploy, and hot reload all found modules. 


Arguments:
  [<base-dir>]    Directory to watch for FTL modules
  
flags:
  --serve=true            Start the FTL cluster
  --watch=500ms           Watch base directory at this frequency and regenerate on change.
  --reconnect-delay=1s    Delay before attempting to reconnect to FTL.
  --exit-after-deploy     Exit after all modules are deployed successfully.
```

Ideas!

```bash!
ftl dev

// Could be scary to run without knowing which env
ftl db <>

// New dev command 
ftl-dev

// Flags
ftl dev --wipe-user-db

ftl dev --endpoint=<>

// Prompt if endpoint is not local
ftl db wipe --yes
<would prompt with "are you sure y/n">


Interactive FTL prompt (less useful in automation, etc.)

// Maybe other commands for non-local
ftl-prod
ftl-remote

```

#### Discussion:

`ftl dev` accepts a base directory argument, within which all modules are deployed and watched. Watching _only_ the directory provided in the most recent `ftl dev` run may create confusing experiences for users—
- Deployments from prior `ftl dev` runs may not be included in the most recent directory path. These would remain live in the cluster but not watched.
- Subsequent deployments via `ftl deploy …` may not be included in the most recent `ftl dev` directory path. These would also be live in the cluster but not watched.

Instead, we’re proposing that everything live in the cluster is watched and hot-reloaded, always. Users can consider hot-reloading an inherent feature of FTL and need not be responsible for toggling it across modules.

#### Alternatives:

1. With `<base-dir>` and `recreate` arguments:

- `ftl dev --recreate <base-dir>`
    - Cluster is recreated with clean state.
    - Everything in provided directory is deployed to cluster and watched.
    - Subsequent deploys performed discretely (`ftl deploy …`) are also watched.
- `ftl dev <base-dir>`
    - Cluster is not recreated. Existing deployments are re-provisioned and watched.
    - Everything in provided directory is deployed to cluster and watched.
    - Subsequent deploys (`ftl deploy …`) are also watched.

2. Without `<base-dir>` argument:

Deploys are explicitly managed by `ftl deploy` and not by `ftl dev`. The `--recreate` argument operates in the same way as above. All deployed modules are watched/hot-reloaded.

3. Without `recreate` argument:

`ftl dev <base-dir>` always recreates the cluster and deploys the modules contained in the specified path. Everything is watched and hot-reloaded. This approach is a true one-stop-shop.


## Rejected Alternatives (optional)
