# Scheduling Flights and managing runs
> Schedule a Flight with cron, trigger on-demand runs, watch the run lifecycle, and cancel an in-flight execution.
A Flight runs on demand, on a schedule, or both. This page covers cron syntax, manual triggers, the run lifecycle, and cancellation.

## Cron syntax

Schedules use a standard 5-field cron expression. **All times are in UTC.**

```text
* * * * *
│ │ │ │ │
│ │ │ │ └── day of week (0-6, Sunday is 0)
│ │ │ └──── month (1-12)
│ │ └────── day of month (1-31)
│ └──────── hour (0-23)
└────────── minute (0-59)
```

Common cadences:

| Cron | When it fires |
|---|---|
| `*/15 * * * *` | Every 15 minutes |
| `0 * * * *` | Hourly at :00 |
| `0 6 * * *` | Daily at 06:00 UTC |
| `0 6 * * 1` | Every Monday at 06:00 UTC |
| `0 0 1 * *` | First of the month at 00:00 UTC |

You can set the schedule in the UI under the **Schedule** panel, through the MCP `create_flight` or `update_flight` tools, or through SQL on `MD_CREATE_FLIGHT` / `MD_UPDATE_FLIGHT`. Omit `schedule_cron` to make the Flight on-demand only.

## Changing or clearing a schedule

Update the cron expression with `MD_UPDATE_FLIGHT` when the cadence changes:

```sql
CALL MD_UPDATE_FLIGHT(
    flight_id := '<flight_id>',
    schedule_cron := '0 7 * * *'
);
```

To stop the schedule from firing, pass `schedule_cron := ''` (empty string) to `update_flight` or `MD_UPDATE_FLIGHT`. The Flight reverts to on-demand only.

```sql
CALL MD_UPDATE_FLIGHT(
    flight_id := '<flight_id>',
    schedule_cron := ''
);
```

In the UI, clearing the cron expression has the same effect.

## Triggering an on-demand run

You can run any Flight manually, with or without a schedule.

### MCP / AI agent

Ask your AI agent to trigger the run. The MCP tool is `run_flight`:

> "Run the heartbeat Flight and show me the logs when it finishes."

The agent calls `run_flight`, then polls `list_flight_runs` and `get_flight_run_logs` until the run reaches a terminal state.

### SQL

```sql
CALL MD_RUN_FLIGHT(flight_id := '<flight_id>');
```

`run_flight` returns immediately with a new run in `RUN_STATUS_PENDING`. The run is asynchronous: poll for completion.

### Override config for a single run

Pass a `config` map to `MD_RUN_FLIGHT` to override stored config values for one run, without editing the Flight or creating a version. You can only set keys the Flight already defines:

```sql
CALL MD_RUN_FLIGHT(
    flight_id := '<flight_id>',
    config := MAP {'LOAD_PARTITION': '2024'}
);
```

Each run records the config it used, visible in the `config` column of [`MD_LIST_FLIGHT_RUNS`](/sql-reference/motherduck-sql-reference/flights/md-list-flight-runs). See [Authentication, config, and secrets](/key-tasks/flights/flights-authentication-config-and-secrets) for the full pattern.

## The run lifecycle

```mermaid
flowchart LR
    A["RUN_STATUS_PENDING"]:::yellow --> B["RUN_STATUS_RUNNING"]:::yellow
    B --> C["RUN_STATUS_SUCCEEDED"]:::green
    B --> D["RUN_STATUS_FAILED"]:::watermelon
    B --> E["RUN_STATUS_CANCELLED"]:::watermelon
```

| Status | Meaning |
|---|---|
| `RUN_STATUS_PENDING` | The run is queued and waiting for a runtime to start. |
| `RUN_STATUS_RUNNING` | The runtime is executing `main()`. |
| `RUN_STATUS_SUCCEEDED` | `main()` returned without raising. |
| `RUN_STATUS_FAILED` | `main()` raised or the runtime reported a failure. |
| `RUN_STATUS_CANCELLED` | The run was cancelled before completion. |

## Watching for completion

Runs are asynchronous, so triggering one doesn't block. Poll for the latest runs through the MCP `list_flight_runs` tool, through SQL with `MD_LIST_FLIGHT_RUNS`, or through the UI's Runs panel. Runs are returned newest first.

```sql
SELECT run_number, status, flight_version, created_at
FROM MD_LIST_FLIGHT_RUNS(flight_id := '<flight_id>')
ORDER BY run_number DESC
LIMIT 10;
```

To read combined stdout and stderr for a single run, use `get_flight_run_logs` (MCP) or `MD_GET_FLIGHT_LOGS` (SQL). When the log is large, the response is the tail.

## Cancelling a run

You can cancel a run that's still `RUN_STATUS_PENDING` or `RUN_STATUS_RUNNING`:

- UI: open the run, then click **Cancel run**.
- MCP: `cancel_flight_run(flight_id, run_number)`.
- SQL: `CALL MD_CANCEL_FLIGHT_RUN(flight_id := '<flight_id>', run_number := <n>);`

Cancelling a run that's already in a terminal status (`RUN_STATUS_SUCCEEDED`, `RUN_STATUS_FAILED`, `RUN_STATUS_CANCELLED`) returns an error.

## Time zones

Schedules are always in UTC. The MotherDuck UI shows a local time / UTC toggle on the Runs panel so you can read timestamps in your local zone, but the cron expression itself is interpreted in UTC.

If you need a Flight to fire at 09:00 in a non-UTC zone, do the conversion when you set the schedule. For example, 09:00 in Amsterdam during summer time (CEST, UTC+2) is `0 7 * * *` in cron.

## Versions and in-flight runs

When a run starts, it locks to the Flight version that was current at that moment. If you update the Flight while a run is in progress, the in-progress run finishes against the version it started with; only the next run picks up the updated source.

This means a long-running Flight is always reproducible against a specific version: you can find that version through `list_flight_versions` or `MD_LIST_FLIGHT_VERSIONS` and inspect the exact source code it ran.


---

## Docs feedback

MotherDuck accepts optional user-submitted feedback about this page at `POST https://motherduck.com/docs/api/feedback/agent`.
For agents and automated tools, feedback submission should be user-confirmed before sending.

Payload:

```json
{
  "page_path": "/key-tasks/flights/scheduling-and-runs/",
  "page_title": "Scheduling Flights and managing runs",
  "text": "<the user's feedback, max 2000 characters>",
  "source": "<optional identifier for your interface, for example 'claude.ai' or 'chatgpt'>"
}
```

`page_path` and `text` are required; `page_title` and `source` are optional. Responses: `200 {"feedback_id": "<uuid>"}`, `400` for malformed payloads, and `429` when rate-limited.
