# Flights
> How Flights run scheduled Python next to your MotherDuck data, and when to reach for one instead of SQL, a Dive, or an external orchestrator.
A **Flight** is a Python program that MotherDuck schedules and runs, with direct access to your databases. It can do whatever Python can: call external APIs, use any PyPI package, process files or run custom logic.

## Where Flights fit in MotherDuck

MotherDuck runs two compute planes against the same storage:

```mermaid
flowchart LR
    User{{"You"}}:::green

    subgraph MotherDuck["MotherDuck"]
        Runtime["Python runtime<br/>(per Flight run)"]:::yellow
        Duckling["Duckling<br/>(SQL compute)"]:::yellow
    end

    DB[("Your databases")]:::yellow

    User -->|"interactive SQL"| Duckling
    User -->|"create / schedule"| Runtime
    Runtime -->|"md:"| Duckling
    Duckling --> DB
```

**Ducklings** run your SQL. They're per-user (see [Hypertenancy](/concepts/hypertenancy)) and start in about a second.

The **Python runtime** runs your scheduled Python. You provide the Flight's source code, and each run gets its own isolated runtime that executes the source as a plain script and exits, so end the script with `if __name__ == "__main__": main()`. A run starts from the Flight's schedule or an `MD_RUN_FLIGHT` call.

A Flight reaches your data the same way any DuckDB client does: the Python runtime opens an `md:` connection, which routes through a Duckling that executes the SQL against your databases. MotherDuck injects an access token into the Flight's environment so the connection works without you embedding credentials.

## Using Flights with sensitive data while in preview

Flights run on shared compute infrastructure. Unlike MotherDuck databases, where each customer is served by an isolated instance, Flights workloads share underlying infrastructure across tenants. Do not assume a dedicated or isolated environment. During Preview, you should not process, store, or log electronic protected health information (ePHI), payment card data, or other regulated or sensitive personal data in Flights.

## The relationship to SQL

Flights and SQL talk to each other in both directions.

**From Python to SQL.** A Flight uses the DuckDB Python client to run any query you can express in SQL:

```python
import duckdb

def main():
    con = duckdb.connect("md:")
    con.execute("INSERT INTO sales.daily_totals SELECT * FROM read_parquet('s3://incoming/today.parquet')")

if __name__ == "__main__":
    main()
```

Anything in the SQL reference is available here: aggregations, MotherDuck table functions, attach commands, and so on.

**From SQL to Flights.** Flights also have a SQL surface. `MD_CREATE_FLIGHT`, `MD_LIST_FLIGHTS`, `MD_LIST_FLIGHT_RUNS`, and the rest of the [Flights SQL functions](/sql-reference/motherduck-sql-reference) let you create, schedule, list, and monitor Flights from anywhere you can run SQL: a DuckDB CLI, your BI tool, or another Flight.

**When to pick which.** A reasonable rule of thumb:

- **Stay in SQL** for a one-off query, or one you already schedule elsewhere.
- **Use a Flight** when you want MotherDuck to run the query on a schedule, when the job should retry and keep a run history, or when the work goes beyond SQL: calling an external service, installing a Python package, reading a file format SQL can't, or chaining steps that don't fit one query.

## What you can build with Flights

- **Ingest.** Pull data from external sources (Postgres, BigQuery, Snowflake, S3, APIs) and write it to MotherDuck tables. [dlt](https://dlthub.com/) is the recommended ingest library: it gives you a declarative pipeline with schema evolution, incremental loading, and a MotherDuck destination.
- **Transform.** Read MotherDuck data, run Python-heavy transformations (machine-learning features, geospatial work), and write the result back. When the transformation is graph-shaped, [dbt](https://docs.getdbt.com/) with the `dbt-duckdb` adapter is the recommended approach.
- **AI enrichment.** Run LLM and embedding work over your data on a schedule. Call MotherDuck's [`prompt`](/sql-reference/motherduck-sql-reference/ai-functions/prompt/) and [`embedding`](/sql-reference/motherduck-sql-reference/ai-functions/embedding/) functions from SQL to classify, summarize, or extract structured fields row by row, or use a Python AI library in `main()` to generate embeddings, score records, or call a model API and write the results back to a table.
- **Export and delivery.** Write query results to object storage as Parquet or CSV — a scheduled drop in S3, Google Cloud Storage, or Azure Blob Storage for a partner or a downstream pipeline to pick up.
- **Reverse ETL and notifications.** Read from MotherDuck and push to a downstream operational system: post a daily summary to Slack, sync an aggregate to a SaaS tool, update a record in a CRM.

## Creating a Flight

You can create a Flight with the [MotherDuck MCP server](/key-tasks/ai-and-motherduck/mcp-setup/): describe the job in natural language, and the agent writes the Python, sets the schedule, and runs it for you with the MCP's Flight tools (`create_flight`, `edit_flight_source`, `run_flight`, and the rest).

You can also create and manage Flights directly with SQL through [`MD_CREATE_FLIGHT`](/sql-reference/motherduck-sql-reference/flights/) and the other Flight table functions, from a DuckDB CLI, your BI tool, or another Flight.

## Integrating with the tools you already use

- **dlt** for ingest. Generate or hand-write a dlt pipeline that uses MotherDuck as the destination, then deploy it as a Flight.
- **DuckDB Community Extensions**. Use [community extensions](https://duckdb.org/community_extensions/list_of_extensions) you trust that are not available on MotherDuck by default. Pull data from [BigQuery](https://duckdb.org/community_extensions/extensions/bigquery) or [Snowflake](https://duckdb.org/community_extensions/extensions/snowflake), [read YAML files](https://duckdb.org/community_extensions/extensions/yaml) and much more.
- **Anything on PyPI.** Pull in any pip-installable package through `requirements.txt`.
- **MCP and AI agents.** Agents create, edit, and run Flights through the [MotherDuck MCP Server](/sql-reference/mcp/). This is the marquee creation path: describe what you want in natural language, the agent writes the Python and wires up the schedule.

You are responsible for the code you run and the packages it installs. Flights does not scan customer code or dependencies for vulnerabilities or malicious intent. Avoid untrusted packages, pin dependency versions, and treat dependency installs as a supply-chain risk.

## Beyond Python

A Flight is a Linux process that runs your Python program, with shell access through `subprocess`. That opens up two patterns worth knowing.

### Install and call system binaries

Use `subprocess` to run `apt-get` and invoke command-line tools (git, ffmpeg, Playwright, anything available as a Debian package) from your Flight.

```python
import subprocess

def main():
    subprocess.run(["apt-get", "install", "-y", "git"], check=True)
    subprocess.run(["git", "clone", "https://github.com/example/repo"], check=True)

if __name__ == "__main__":
    main()
```

### Run a local DuckDB with community extensions

A Flight can open its own in-process DuckDB connection alongside the `md:` connection, which lets you load DuckDB [community extensions](https://duckdb.org/community_extensions/) that aren't supported on MotherDuck's server-side runtime. For example, the [`bigquery`](https://duckdb.org/community_extensions/extensions/bigquery) and [`snowflake`](https://duckdb.org/community_extensions/extensions/snowflake) extensions let a Flight read directly from another warehouse and write the result to MotherDuck.

```python
import duckdb

def main():
    local = duckdb.connect()  # local in-process DuckDB
    local.execute("INSTALL bigquery FROM community; LOAD bigquery;")
    local.execute("ATTACH 'project=my-project' AS bq (TYPE bigquery, READ_ONLY)")

    # read from BigQuery into the Flight, then write the result to MotherDuck
    events = local.sql("SELECT * FROM bq.analytics.events").df()

    md = duckdb.connect("md:")
    md.execute("INSERT INTO raw.events SELECT * FROM events")

if __name__ == "__main__":
    main()
```

Keep heavy compute outside the Flight runtime. A Flight is sized for orchestration and light processing, not for crunching large tables in memory. Let the source warehouse (BigQuery, Snowflake) and MotherDuck handle the heavy lifting, and let the Flight move data between them.

## How Flights are scoped

- **A Flight runs as a triggered job.** A run starts from its schedule or a `MD_RUN_FLIGHT` call, executes the script to completion, and exits; a single run can be long or short.
- **One Flight is one process.** A run isn't a managed worker pool with a built-in queue or distributed state. You can fan out by having a Flight call `MD_RUN_FLIGHT` to trigger others, or parallelize within a run with a Python thread pool — the coordination logic is yours to write. Flights are at their best for MotherDuck-centric work where the data, schedule, and compute live in one place; for workflows that span many external systems, a dedicated orchestrator like Airflow or Prefect is still the better tool.
- **Runs are unattended.** You read a run's output afterward from its [logs](/sql-reference/motherduck-sql-reference/flights/md-get-flight-logs) rather than stepping through it live. To explore data interactively, run queries in the SQL editor instead.

## Related resources

- [Flights SQL reference](/sql-reference/motherduck-sql-reference/flights/) — `MD_CREATE_FLIGHT`, `MD_RUN_FLIGHT`, and the rest of the Flight table functions.
- [Hypertenancy](/concepts/hypertenancy) — why Ducklings give every user dedicated SQL compute.
- [Creating visualizations with Dives](/key-tasks/ai-and-motherduck/dives) — interactive React apps over MotherDuck data.
- [MotherDuck MCP Server](/sql-reference/mcp/) — the MCP surface for AI agents that create and manage Flights.


---

## 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": "/concepts/flights/",
  "page_title": "Flights",
  "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.
