Configuration
Firepit is configured with a single YAML file named firepit.yml (or firepit.yaml) in your project directory. It defines the tasks you can run, along with their dependencies, variables, environment, and other settings.
The smallest possible configuration is a single task:
tasks:
hello:
command: echo "hello, firepit"Run it by name:
fire helloThe rest of this page builds up from here: defining tasks, parameterizing them with variables and environment variables, wiring them together with dependencies, running long-lived services, speeding up reruns with incremental builds and watch mode, and—for larger setups—multi-project monorepos and shared configuration.
Tasks
A task is a named command. Tasks are defined under the tasks key, where each key is the task name.
tasks:
build:
command: bun build src/index.ts --outfile dist/appBy default a command runs in your shell, in the project directory. You can override both:
shell: The shell command used to run the command, and its arguments.working_dir: The directory to run the command in, relative to the project directory (or an absolute path).
tasks:
build:
command: bun build src/index.ts --outfile dist/app
working_dir: app
shell:
command: bash
args: ["-c"]Tasks run to completion and exit. Long-running processes such as servers are modeled as services instead.
Description and Label
You can annotate a task with a description and a label.
description: A human-readable explanation of what the task does. It is shown in the task listing (fire --list) and help output. It may span multiple lines.label: A display name used instead of the task name in the TUI/CUI output (for example as the log prefix and pane title). When omitted, the task name is used. Unlikedescription,labelalso supports template variables (covered in the next section).
tasks:
dev:
description: Start the dev server with hot reload
label: "{{ project }}/dev"
command: bun run --hot src/index.ts
service: trueVariables
You can define template variables using the vars field, both at the project level and per task.
# Project level variables
vars:
aws_account_id: 123456789012
aws_region: ap-northeast-1
ecr_registry: "{{ aws_account_id }}.dkr.ecr.{{ aws_region }}.amazonaws.com"
tasks:
build:
# Task level variables
vars:
app_name: single
ecr_repository: "{{ ecr_registry }}/{{ app_name }}"
command: docker build -t {{ ecr_repository }}:latest .Firepit performs template processing using Tera. Check the documentation for details about the template syntax.
Template processing is supported in the following fields:
varslabelcommandenvenv_filesworking_dirdepends_on
There are also some built-in variables available for use in templates.
| Name | Type | Description |
|---|---|---|
root_dir | string | The absolute path of the root project dir. Multi-projects only. |
project_dirs | Map<string, string> | Map of all project names to their absolute paths. Multi-projects only. |
project_dir | string | The absolute path of the current project directory. |
project | string | The project name. Multi-projects only. |
task | string | The task name. |
watch | boolean | true if running in watch mode, false otherwise. |
Passing Arguments
The args variable is a convenient convention for forwarding command-line arguments to a task. Declare it with a default value, reference it in the command, and override it from the CLI using --:
tasks:
test:
vars:
args: ""
command: cargo test {{ args }}fire test -- --nocapture my_test # runs: cargo test --nocapture my_testEverything after -- is shell-escaped, joined with a space, and assigned to args. Embed without extra quotes so the shell can interpret the generated quoting correctly. Since -- is just an alias for args=..., specifying both at the same time is an error.
A dependent task can also set args—or any variable—on the task it depends on. See Parameterized Dependencies.
Dynamic Variables
The variables shown so far are static: their values are plain JSON (string, number, boolean, array, or map). A variable can instead be dynamic, taking its value from the output of a command. Write the variable in object form and specify a command:
vars:
git_sha:
command: git rev-parse --short HEAD
build_date:
command: date +%Y%m%d
# Optional fields
working_dir: .
shell:
command: bash
args: ["-c"]
tasks:
build:
command: docker build -t app:{{ git_sha }} .The command's standard output, trimmed of surrounding whitespace, becomes the variable's value; standard error is ignored. In addition to command, a dynamic variable accepts the optional shell, working_dir, env, and env_files fields.
Environment Variables
Environment variables can be defined in the env field. You can also specify dotenv files in the env_files field. The precedence of environment variables is as follows:
- Environment variables in the
envfield - Environment variables from each dotenv file listed in the
env_filesfield. If the same environment variable is defined in multiple files, the later file takes precedence. - OS environment variables
Note that dependency tasks do not inherit the environment variables of their parent task.
# Project level environment variables
env:
TZ: Asia/Tokyo
# Project level dotenv files
env_files:
- .env
tasks:
dev:
command: bun run --hot src/index.ts
# Task level environment variables
env:
PORT: 3000
REDIS_URL: redis://localhost:6379
# Task level dotenv files.
# .env.local has a higher priority than .env
env_files:
- .env.local
- .envDependencies
Tasks can depend on other tasks using the depends_on field. Dependency tasks are executed before the target task.
In this example, install and compile tasks are executed sequentially before the build task.
tasks:
install:
command: bun install
compile:
command: bun build src/index.ts --compile --outfile dist/app
depends_on:
- install
build:
command: docker build -t single:latest .
depends_on:
- compileParameterized Dependencies
Writing a dependency in object form lets you override its vars. This means you can define a single generic task and reuse it with different inputs, instead of duplicating near-identical tasks.
In this example, the generic migrate task is reused by two tasks with different database values:
tasks:
migrate:
vars:
database: ""
command: ./migrate.sh {{ database }}
setup-app:
command: echo "app is ready"
depends_on:
- task: migrate
vars:
database: app # runs: ./migrate.sh app
setup-analytics:
command: echo "analytics is ready"
depends_on:
- task: migrate
vars:
database: analytics # runs: ./migrate.sh analyticsEach dependent runs its own variant of migrate with the overridden variables. In the TUI/CUI, every variant is displayed with the original task name by default; set a label with template variables (for example label: "migrate ") to tell the variants apart. Note that only variables already declared in the dependency task can be overridden, so migrate must declare database in its vars. If the same variable is also injected globally via -- (see Passing Arguments), the value specified here on the dependency takes precedence.
Cascading Restarts
In watch mode, when a dependency task is re-run, the tasks that depend on it are re-run as well by default. This cascading behavior can be turned off per dependency by writing the dependency in object form and setting cascade: false. A dependency written as a plain string is equivalent to cascade: true.
In this example, build is re-run when install changes, but not when codegen is re-run.
tasks:
build:
command: bun build src/index.ts
depends_on:
- install # cascade: true (default)
- task: codegen
cascade: false # re-running codegen does not re-run buildServices
Most tasks run to completion and exit. A service is a long-running process that stays active until stopped—web servers, databases, file watchers, and the like. Mark a task as a service by setting service: true.
tasks:
dev:
command: bun run --hot src/index.ts
service: trueWhen another task depends on a service, the service is started first and kept running while the dependent task runs.
Readiness
When a service is added to the dependencies, the dependent task runs immediately after the service starts by default.
In this example, the dev service may start before the db service is ready to accept connections.
tasks:
dev:
command: bun run --hot src/index.ts
service: true
depends_on:
- install
- db
db:
command: redis-server
service: trueYou can configure the db service to signal its readiness by using the healthcheck field. There are two ways to define a healthcheck:
- Command: Runs a command periodically until it exits with a zero status.
- Log: Waits until log message appears that matches the given regex.
Most services become Ready when they start listening on a port, so you can easily check this with the nc (netcat) command. By default, healthcheck command is run every 5 seconds, with a timeout of 5 seconds, and up to 3 retries.
db:
command: redis-server
service:
healthcheck:
command: nc -z localhost 6379
# Default values
start_period: 0
interval: 5
timeout: 5
retries: 3Sometimes it is sufficient to wait for a specific log output. In such cases, you can configure the service to be considered Ready when a log message like Ready to accept connections tcp appears.
db:
command: redis-server
service:
healthcheck:
log: Ready to accept connections tcpRestart Policy
You can control whether a service is restarted when its process exits, using the restart field.
| Value | Description |
|---|---|
never | Never restart the service. This is the default. |
always | Always restart the service when it exits. |
always:N | Always restart, up to N times. |
on-failure | Restart only when the service exits with a non-zero status. |
on-failure:N | Restart on failure, up to N times. |
tasks:
db:
command: redis-server
service:
# Restart on failure, up to 5 times
restart: on-failure:5
healthcheck:
log: Ready to accept connections tcpIncremental Builds and Watch Mode
Incremental Builds
Firepit can skip tasks if there have been no changes since the last successful run that would produce different outputs. This is called incremental build.
To enable incremental build, specify the inputs and outputs fields for each task. You can use glob patterns to specify multiple files. Check the globset documentation for the supported syntax.
tasks:
compile:
command: bun build src/index.ts --compile --outfile dist/app
inputs:
- src/**
outputs:
- dist/app
depends_on:
- installThe task will be skipped if the following conditions are met:
- There is at least one file matching the patterns specified in the
inputsandoutputsfields - All files listed in
inputsare older than the files listed inoutputs.
Watch Mode
In watch mode, Firepit monitors the files specified in the inputs field and automatically re-runs the task and dependents when changes are detected. To enable watch mode, add -w or --watch option.
fire -w buildMulti-Project (Monorepo)
Firepit projects can be composed into a monorepo: a root firepit.yml plus a firepit.yml in each subproject.
.
├── firepit.yml
└── packages/
├── client/
│ └── firepit.yml
└── server/
└── firepit.ymlThe root firepit.yml declares the subprojects and any common tasks.
projects:
client: packages/client
server: packages/server
tasks:
install:
command: bun installEach firepit.yml in subprojects defines its own tasks.
tasks:
dev:
command: bun run dev
depends_on:
- "#install"
- server#dev
service: truetasks:
dev:
command: bun run dev
depends_on:
- "#install"
service: trueTasks can be referenced across projects using the form {project}#{task}. Note that the root project name is treated as an empty string, so you can reference root tasks with #{task}.
For example, to run client's dev task:
fire client#devMove to the client directory and run the dev task directly:
cd packages/client
fire devRun client & server dev tasks (because root project does not have dev task)
fire devThis is how Firepit resolves which task to run:
Reusing Configuration
As your configuration grows, two features help you avoid repetition: defaults applies common settings to many tasks at once, and includes merges shared files into your config.
Defaults
The defaults field lets you apply common settings to multiple tasks at once, instead of repeating them in every task. Each entry has an optional tasks selector and the settings to apply.
The tasks selector decides which tasks an entry applies to:
- A string is treated as a regular expression matched against the task name.
- An array is treated as an explicit list of task names.
- If omitted, the entry applies to all tasks. (Note that an empty string
""or empty list[]matches nothing.)
An entry can set shell, working_dir, vars, env, env_files, depends_on, service, inputs, and outputs.
defaults:
- tasks: "^(build|test)" # regex: applies to build and test
depends_on:
- install
env:
NODE_ENV: development
- tasks: [lint, test] # explicit list
shell:
command: bash
args: ["-c"]
tasks:
build:
command: bun run build
test:
command: bun test
lint:
command: bun run lintWhen multiple entries match the same task, they are merged in order: scalars (shell, working_dir, service) and map keys (vars, env) are overridden by later entries, while arrays (env_files, depends_on, inputs, outputs) are concatenated. The merged defaults act as a base layer, so any setting defined directly on the task itself takes precedence.
Merging Config Files
You can merge multiple configuration files using includes field. Starting from an empty YAML, files specified in includes are merged in order, followed by the original firepit.yml.
If the field name conflicts, merging strategy depends on the field type.
- number, string, boolean: the later one takes precedence.
- list: the later one is appended to the former one.
- map: merged recursively.
Assume we have the following files:
vars:
aws_account_id: 123456789012
aws_region: ap-northeast-1tasks:
install:
command: bun installincludes:
- common-vars.yml
- common-tasks.yml
vars:
ecr_registry: "{{ aws_account_id }}.dkr.ecr.{{ aws_region }}"
tasks:
dev:
command: bun run --hot src/index.ts
depends_on:
- installThen, the merged configuration is equivalent to:
vars:
aws_account_id: 123456789012
aws_region: ap-northeast-1
ecr_registry: "{{ aws_account_id }}.dkr.ecr.{{ aws_region }}"
tasks:
install:
command: bun install
dev:
command: bun run --hot src/index.ts
depends_on:
- installReference
This page covers the configuration you will reach for most often. For the complete list of every field and its type, see the Schema.
