---
question: Who schedules and starts a module's code, with NATS or with Dagster?
kind: overview
---

> **You are reading the v2.x docs (engine modules). New projects use v3.** [Read this page in v3](/capabilities/build/orchestrations/overview) · [Migrate from 2.x to 3.0](/capabilities/upgrade/migrate-to-3)

# Orchestrations

Orchestrating code in ABI means running a module's methods without anyone calling them: on a schedule, when something happens, or in sequence after another step. The unit is the **job**: a module method plus its triggers. The module declares its jobs, and the engine that loads it runs them. JetStream stores the schedules, fires them, and hands each trigger to one replica of the module.

There is no central scheduler. The module publishes its schedules when the engine loads it, and the broker fires them.

<!-- matrix -->

:::note ABI 3.x with NATS only
Jobs need ABI 3.x and NATS, the default of `abi new project` on 3.x. In a 3.x project without NATS (legacy mode), a declared job is logged and not run. ABI 2.x has no jobs at all. On both, scheduled work is a Dagster orchestration: see [Dagster](/v2/capabilities/build/applications/dagster), or [move to 3.x with NATS](/capabilities/upgrade/migrate-to-3).
:::

## Three ways a job starts

| Start | Declared with | Page |
|---|---|---|
| On a schedule | `Cron("0 30 9 * * mon-fri")`, `Cron("@daily")` or `Every("15m")` | [Schedule a job](/v2/capabilities/build/orchestrations/schedule-a-job) |
| On an event | `OnEvent(event_type=<IRI>, filter={...})` or `OnEvent("<subject>")` | [React to an event](/v2/capabilities/build/orchestrations/react-to-an-event) |
| By hand | No trigger needed: **Run now** in Nexus, `trigger_job` in code, or a REST call | [Run a job by hand](/v2/capabilities/build/orchestrations/run-a-job-by-hand) |

A job can combine them: up to 16 triggers, and it can always be started by hand. Steps that depend on each other are separate jobs, joined by events:

![A scheduled export job writes a CSV to object storage. Object storage emits ObjectPut. The load job's filter keeps the reports/ puts; the event wakes the job, which reads what it has not handled from its durable cursor in the event log, then publishes ReportLoaded, which wakes the notice job.](/diagrams/abi-job-chain.svg)

[Chain jobs with events](/v2/capabilities/build/orchestrations/chain-jobs-with-events) builds that chain step by step. When one run should own the whole sequence, a [durable workflow](/v2/capabilities/build/workflows/write-a-durable-workflow) calls each step with `ctx.call` instead.

## Key concepts

- **A job is code.** It is declared with `@job(triggers=(...))` on the module class, in Python. Nexus shows jobs and starts them, but no screen, CLI command or `config.yaml` key creates one.
- **One replica per trigger.** The replicas of a module share one durable consumer per job, so each tick, event or manual start runs once, on one replica. `max_concurrency` (default 1) bounds runs at once across replicas.
- **At least once.** After a crash or a lost acknowledgement a run can start again. Write each job so a second run does no harm.
- **Events are at most once.** An event published while no replica of the job is running is not delivered. A job that must not miss one reads the event log instead: see [the wake-up pattern](/v2/capabilities/build/orchestrations/chain-jobs-with-events#wake-up-then-read-the-log).
- **Run records.** Every run is recorded with its status, attempts, payload, logs and result, and shown in Nexus under System › Jobs. See [Monitor runs](/v2/capabilities/build/pipelines/monitor-runs).
- **Workflows are jobs too.** A `@workflow` is a job with a typed input and output whose steps are stored, so a retry skips the steps that finished. Inside one, `ctx.call` and `ctx.map_call` start other modules' workflows as child runs: a chain in one run, as an alternative to events. Workflow and pipeline classes are published the same way, as `workflow.<slug>` and `pipeline.<slug>`. See [Write a durable workflow](/v2/capabilities/build/workflows/write-a-durable-workflow) and [Schedule a workflow](/v2/capabilities/build/workflows/schedule-a-workflow).

The full reference (options, `JobContext`, retries, retention, the engine's own jobs and the move from Dagster) is on [Module jobs](/v2/capabilities/build/modules/module-jobs).

## On ABI 2.x, or 3.x without NATS

There are no jobs. A module schedules its code with a Dagster orchestration in its `orchestrations/` folder: `abi new orchestration <name> [path]` writes a `DagsterOrchestration` with empty `assets`, `schedules`, `jobs` and `sensors`, on both lines. Dagster loads the modules' orchestrations and runs them next to the API. On 3.x this is legacy mode: it needs the `dagster` extra and is removed in the next major. See [Dagster](/v2/capabilities/build/applications/dagster) and [Legacy mode: without NATS](/capabilities/get-started/legacy-mode).
