Skip to main content
Flyte organizes execution around a small set of composable primitives. Understanding these four concepts gives you a complete mental model of how Flyte works.

Tasks

The fundamental unit of compute in Flyte. A task is a strongly typed, versioned, containerized Python function that runs on a Kubernetes Pod.

Workflows

A directed acyclic graph (DAG) that composes tasks and other workflows into a meaningful execution pipeline.

Launch Plans

Templates for workflow executions. Bind default or fixed inputs, define schedules, and configure notifications.

Projects and Domains

Multi-tenancy primitives. Projects namespace your workflows, and domains (development, staging, production) control execution environments.

How these concepts fit together

Here is how the four concepts relate to each other at execution time:
  1. You write tasks — Python functions decorated with @task. Each task has typed inputs and outputs.
  2. You compose tasks into workflows — Python functions decorated with @workflow. The workflow defines the DAG structure and the data flow between tasks.
  3. You create launch plans to trigger workflow executions. Every registered workflow automatically gets a default launch plan. You can create additional launch plans with different default inputs, fixed inputs, or schedules.
  4. You register all of this under a project and domain. The project is a logical namespace (e.g., my-ml-project). The domain controls the execution environment (e.g., development vs. production).
Project: my-ml-project
├── Domain: development
│   ├── Workflow: training_wf (v1, v2, v3)
│   └── Launch Plan: nightly_training_lp (cron schedule)
└── Domain: production
    ├── Workflow: training_wf (v3)
    └── Launch Plan: nightly_training_lp (cron schedule, fixed inputs)

A quick example

Here is a complete example showing all four concepts working together:
from typing import List
from flytekit import task, workflow, LaunchPlan, CronSchedule


# 1. Define tasks
@task
def mean(values: List[float]) -> float:
    return sum(values) / len(values)


@task
def standard_deviation(values: List[float], mu: float) -> float:
    from math import sqrt
    variance = sum([(x - mu) ** 2 for x in values])
    return sqrt(variance)


@task
def standard_scale(values: List[float], mu: float, sigma: float) -> List[float]:
    return [(x - mu) / sigma for x in values]


# 2. Compose tasks into a workflow
@workflow
def standard_scale_workflow(values: List[float]) -> List[float]:
    mu = mean(values=values)
    sigma = standard_deviation(values=values, mu=mu)
    return standard_scale(values=values, mu=mu, sigma=sigma)


# 3. Create a launch plan with default inputs and a schedule
nightly_lp = LaunchPlan.get_or_create(
    standard_scale_workflow,
    name="nightly_scale_lp",
    default_inputs={"values": [1.0, 2.0, 3.0, 4.0, 5.0]},
    schedule=CronSchedule(schedule="0 0 * * *"),  # daily at midnight
)
To run this workflow locally:
pyflyte run example.py standard_scale_workflow --values '[1.0,2.0,3.0,4.0,5.0]'
To register and run on a Flyte cluster:
pyflyte run --remote example.py standard_scale_workflow --values '[1.0,2.0,3.0,4.0,5.0]'

Versioning

Each task and workflow in Flyte is versioned and immutable. Once registered, a specific version of a task or workflow (identified by its project, domain, name, and version) cannot be changed. This is a deliberate design decision to guarantee reproducibility: you can always re-run an old workflow version with identical behavior. By default, pyflyte register uses the current Git SHA as the version string.

Explore each concept

Tasks in depth

Task types, resource allocation, caching, retries, timeouts, and plugins.

Workflows in depth

DAG construction, promises, subworkflows, and the >> dependency operator.

Launch Plans in depth

Default inputs, fixed inputs, cron schedules, fixed-rate schedules, and activation.

Projects and Domains in depth

Multi-tenancy, execution isolation, and promoting workflows across environments.

Build docs developers (and LLMs) love