Skip to main content

Dagster: Data Orchestration with Assets, Jobs & Sensors

Dagster is the cloud-native data orchestration platform that treats data assets — not tasks — as first-class citizens. Get full data lineage, asset materialization tracking, and a rich observability UI out of the box.

✓ Asset-based orchestration ✓ Full data lineage ✓ Built-in Dagit UI ✓ Sensors & partitions

What is Dagster?

Dagster is an open-source data orchestration platform designed around the concept of Software-Defined Assets (SDAs) — Python functions that declare what data artifacts they produce and depend on. Unlike Airflow which orchestrates tasks, Dagster orchestrates data: it tracks the materialization state of every asset, shows the full lineage graph from raw sources to final models, and surfaces data freshness and quality at the platform level.

Dagster integrates natively with dbt (model assets), Airbyte (source assets), Spark, Snowflake, BigQuery, and most of the modern data stack. The Dagit web UI provides an interactive asset graph, run history with per-step logs, a partition viewer for backfill monitoring, and a sensor dashboard. Dagster Cloud is the managed offering; Dagster Open Source can be self-hosted with dagster dev for local development.

Software-Defined Assets

# pip install dagster dagster-webserver
from dagster import asset, AssetExecutionContext, Definitions
import pandas as pd

# Software-Defined Assets — define the data, not the task
@asset(
    group_name="raw",
    description="Raw orders from the API",
)
def raw_orders(context: AssetExecutionContext) -> pd.DataFrame:
    import httpx
    data = httpx.get("https://api.example.com/orders").json()
    df = pd.DataFrame(data)
    context.log.info(f"Fetched {len(df)} orders")
    return df

@asset(
    group_name="staging",
    deps=[raw_orders],
    description="Cleaned orders with USD amounts",
)
def stg_orders(raw_orders: pd.DataFrame) -> pd.DataFrame:
    df = raw_orders.copy()
    df["amount_usd"] = df["amount_cents"] / 100
    df = df[df["status"] != "cancelled"]
    df["created_at"] = pd.to_datetime(df["created_at"])
    return df

@asset(
    group_name="marts",
    deps=[stg_orders],
    description="Revenue aggregated by region and month",
)
def revenue_by_region(stg_orders: pd.DataFrame) -> pd.DataFrame:
    return (
        stg_orders
        .assign(month=lambda df: df["created_at"].dt.to_period("M"))
        .groupby(["region", "month"])
        .agg(revenue=("amount_usd", "sum"), orders=("order_id", "count"))
        .reset_index()
    )

# Definitions — registers all assets, jobs, schedules, resources
defs = Definitions(assets=[raw_orders, stg_orders, revenue_by_region])

Jobs, Schedules, Sensors & Resources

# Jobs, Schedules, Sensors & Resources
from dagster import (
    define_asset_job, ScheduleDefinition, sensor,
    RunRequest, SensorEvaluationContext, resource,
    Definitions, EnvVar,
)
from dagster_postgres import PostgresIOManager

# Job — subset of assets to materialize together
etl_job = define_asset_job(
    name="nightly_etl",
    selection=["raw_orders", "stg_orders", "revenue_by_region"],
)

# Schedule — run the job on a cron
nightly_schedule = ScheduleDefinition(
    job=etl_job,
    cron_schedule="0 2 * * *",  # 2am UTC daily
    execution_timezone="UTC",
)

# Sensor — trigger a run when a new S3 file appears
@sensor(job=etl_job, minimum_interval_seconds=60)
def new_file_sensor(context: SensorEvaluationContext):
    import boto3
    s3 = boto3.client("s3")
    paginator = s3.get_paginator("list_objects_v2")
    for page in paginator.paginate(Bucket="my-bucket", Prefix="inbox/"):
        for obj in page.get("Contents", []):
            key = obj["Key"]
            if key not in context.cursor:
                yield RunRequest(
                    run_key=key,
                    run_config={"ops": {"raw_orders": {"config": {"s3_key": key}}}},
                )

# Resource — shared database connection
postgres_resource = PostgresIOManager(
    host=EnvVar("POSTGRES_HOST"),
    database=EnvVar("POSTGRES_DB"),
)

defs = Definitions(
    assets=[raw_orders, stg_orders, revenue_by_region],
    jobs=[etl_job],
    schedules=[nightly_schedule],
    sensors=[new_file_sensor],
    resources={"io_manager": postgres_resource},
)

Partitioned Assets & Backfills

# Partitioned assets — process data day by day
from dagster import (
    asset, DailyPartitionsDefinition, AssetExecutionContext,
    build_asset_context, materialize,
)

daily_partitions = DailyPartitionsDefinition(start_date="2024-01-01")

@asset(
    partitions_def=daily_partitions,
    group_name="raw",
)
def daily_events(context: AssetExecutionContext) -> pd.DataFrame:
    date = context.partition_key  # "2024-01-15"
    url = f"https://api.example.com/events?date={date}"
    return pd.DataFrame(httpx.get(url).json())

@asset(
    partitions_def=daily_partitions,
    deps=[daily_events],
)
def daily_summary(daily_events: pd.DataFrame) -> dict:
    return {
        "total_events": len(daily_events),
        "unique_users": daily_events["user_id"].nunique(),
    }

# Backfill a date range from CLI:
# dagster asset backfill --select daily_events daily_summary \
#   --partition-range 2024-01-01...2024-01-31

Dagster vs Airflow vs Prefect

Feature Dagster Apache Airflow Prefect
Paradigm Asset-based Task DAG Flow/Task
Data lineage First-class (asset graph) Plugin required Limited
UI Dagit (self-hosted) Webserver (self-hosted) Prefect Cloud / self-hosted
dbt integration Native (dbt assets) BashOperator / dbt-airflow prefect-dbt
Learning curve Medium–High Medium Low
Best for Data platform / lineage Complex ETL, large teams Python-native quick start

Frequently Asked Questions

What is the difference between asset-based and task-based orchestration?▼

Task-based orchestration (Airflow, Prefect) thinks in terms of actions — run this task, then that task. You define the execution sequence and the tasks produce side effects (writing to a database). Asset-based orchestration (Dagster) thinks in terms of data artifacts — define the assets you want to exist and how to produce them. Dagster tracks which assets are materialized, shows lineage between assets, and can automatically determine which assets need to be re-run when upstream data changes. This shift makes it easier to answer 'what data do we have and how was it produced?'

What are Software-Defined Assets in Dagster?▼

Software-Defined Assets (SDAs) are Python functions decorated with @asset that declare what data artifact they produce and what upstream data they depend on. Unlike Airflow tasks which are operations, assets are descriptions of data. Dagster tracks the materialization status of each asset — when it was last materialized, with what code version, and whether it is stale relative to its upstream dependencies. The Dagit UI shows a live asset graph with materialization history, making it easy to see the state of your entire data platform at a glance.

What is the Dagit UI and what can I do with it?▼

Dagit is Dagster's web interface (dagster-webserver package). It provides: an Asset Catalog showing all defined assets with their lineage graph and materialization status, a Launchpad for running jobs with custom configuration, a Runs view with per-step logs and timing, a Schedules and Sensors tab for monitoring trigger status, and a Partitions tab for viewing backfill progress. Run dagster dev to start both the webserver and daemon locally. Dagit is the primary observability surface for a Dagster deployment.

How do Dagster Resources work?▼

Resources are reusable, configurable objects that provide shared infrastructure to assets and ops — database connections, API clients, cloud storage handles. Define a resource using the @resource decorator or by subclassing ConfigurableResource. Resources are injected into assets as function parameters by name. In the Definitions object, map resource keys to implementations. This allows you to swap implementations between environments: in development, inject a local SQLite resource; in production, inject a PostgreSQL resource. Resources eliminate the need to create clients inside each function.

When should I choose Dagster over Airflow?▼

Choose Dagster when data lineage and asset observability are important — knowing what data exists, when it was produced, and whether it is stale. Dagster's asset graph is a first-class concept, not a bolt-on. Choose Dagster when your team thinks in terms of data products rather than scheduled scripts. Choose Airflow when you have existing Airflow DAGs and a large team familiar with it, need specific Airflow operators not available in Dagster's integrations, or work in a data engineering context where Airflow is the established standard. Dagster has a higher learning curve than Prefect but more powerful data modeling concepts.

What is the difference between @op, @job, and @asset in Dagster?▼

@op is the lowest-level unit of computation — a function that takes inputs and returns outputs, similar to a Prefect task. @job is a graph of @ops that can be scheduled and run. @asset is a higher-level abstraction — an @op that produces a named data artifact tracked in the asset catalog. For new Dagster projects, prefer @asset over @op/@job — the SDA paradigm provides better observability and is the recommended approach. @op and @job are useful for computational pipelines that do not produce reusable data artifacts, or for migrating from Airflow with a task-centric mental model.

Share this tool: X / Twitter LinkedIn
✓ 60,000+ developers use DevPlaybook ★★★★★ 4.8 / 5 on Gumroad ✓ MIT licensed · Instant download ✓ No login required

Related Products

on Gumroad

You Might Also Need

More Free Tools

Enjoying this tool? Get more like it.

Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.

DevPlaybook Pro 7 days free

Unlock AI-Powered Dev Tools

  • ⚡ AI Code Review, Doc Generator & SQL Builder
  • ⚡ All premium templates & early access
  • ⚡ Member discounts on Gumroad products
Start Free Trial →

$9/mo after trial · Cancel anytime

Want the full toolkit?

Get DevPlaybook Pro

Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.

$79
$241 separately Save $162 (67% off)
Get DevPlaybook Pro — $79 →

MIT licensed · Instant download · No subscription

See what's included → Browse all deals →