Latest in branch 2
2.11.2
Released 14 Mar 2026
(2 months ago)
SoftwareApache Airflow
Version2
Environment
requirements
Python 3.7-3.11
PostgreSQL 12-16
MySQL 8.0, SQLite (dev)
Linux/macOS, Windows via WSL2
≥4GB RAM, ≥2 vCPU
Initial release2.0.0
17 Dec 2020
(5 years ago)
Latest release2.11.2
14 Mar 2026
(2 months ago)
Limited Maintenance22 Oct 2025
(Ended 7 months ago)
EOL/Terminated22 Apr 2026
(Ended 1 month ago)
Release noteshttps://github.com/apache/airflow/releases/tag/2.11.2
Source codehttps://github.com/apache/airflow/tree/2.11.2
Downloadhttps://github.com/apache/airflow/releases/tag/2.11.2
Apache Airflow 2 ReleasesView full list

What Is New in Apache Airflow 2

The Airflow 2.x series delivered a substantial set of new capabilities, reliability fixes, and architectural improvements across a long progression of minor and patch releases. The table below summarizes the major categories of change.

Category Highlights
New Features Multiple concurrent executors (hybrid execution); OpenTelemetry traces; Dataset aliases and conditional dataset logic; TaskFlow @skip_if / @run_if decorators; object storage XCom backend; dark mode UI; Python 3.12 support
Improvements Revised try_number lifecycle (no mid-flight increments); teardown tasks now run on manual DAG failure; fernet key rotation in batches; XCom rendered as interactive JSON; BashOperator templated scripts execute as temp files
Bug Fixes Teardown tasks skipped on manual DAG state change (fixed); endless sensor rescheduling; mapped task trigger-rule edge cases; gantt / grid UI flickering; priority_weight integer overflow; stuck-in-queued task instances
Breaking Changes MSSQL metadata database support removed (2.9.0); datasets no longer trigger inactive/paused DAGs; /logout now POST + CSRF protected; AirflowTimeoutError inherits BaseException; try_number no longer incremented mid-execution
Deprecations Session auth backend; --tree flag for tasks list; conf from Task Context; SMTP configs in airflow_local_settings; execution_date in TriggerDagRunOperator (replaced by logical_date)

Can Airflow 2 run tasks on multiple executors at the same time?

Yes -- starting with Airflow 2.10.0, Airflow can run tasks across multiple executors concurrently, a capability described in AIP-61 (Hybrid Execution). Individual tasks or entire DAGs can be pinned to a specific executor, so a single DAG can mix a KubernetesExecutor task with a CeleryExecutor task without any workarounds.

In practice this means teams running mixed workloads -- for example, CPU-bound ML training alongside lightweight Python scripts -- no longer need separate Airflow deployments or wrapper hacks. The executor is exposed as a first-class field on operators and in the database.

# Pin a task to a specific executor
@task(executor="KubernetesExecutor")
def train_model():
    ...

@task(executor="LocalExecutor")
def notify_slack():
    ...

Watch out for the "experimental" label on this feature in 2.10.0 -- while it works in production, some edge cases around metrics and executor-specific configs were still being ironed out in patch releases. The executor field is stored in the task_instance table, so a fresh airflow db migrate is required after upgrading.

How did Airflow 2 change the way dataset-based scheduling works?

Airflow 2.9 introduced a significant behavioral fix: datasets no longer trigger paused or inactive DAGs. Previously, events that accumulated while a DAG was paused would fire the DAG the moment it was unpaused, creating surprise runs that confused on-call engineers. The new behavior means a dataset schedule is only satisfiable by events that arrive while the consuming DAG is active.

The 2.9 release also introduced conditional (logical) dataset expressions, letting you combine datasets with AND / OR semantics to build more nuanced trigger conditions. The 2.10 release added Dataset Aliases, which allow dynamic emission -- a task can emit a dataset event to an alias and have it resolve to one or more actual datasets at runtime, decoupling producer and consumer definitions.

from airflow.datasets import Dataset, DatasetAlias

# Conditional: trigger only when BOTH datasets are updated
with DAG(
    dag_id="consume_both",
    schedule=(Dataset("s3://bucket/a") & Dataset("s3://bucket/b")),
):
    ...

# Alias: producer emits to a logical name
@task(outlets=[DatasetAlias("reporting.daily")])
def produce():
    ...

This matters if your pipelines rely on dataset-triggered DAGs with a lot of pause/unpause cycles -- audit your run history after upgrading to ensure no unexpected backlog of events exists. Use the new REST API endpoint (POST /datasets/events) or the UI button to manually emit dataset events during testing.

What changed about task try_number and execution lifecycle in Airflow 2.10?

In Airflow 2.10.0, the try_number is now fixed at scheduling time and never incremented during execution. Before this change, try_number was bumped at the start of each task run on the worker, which created confusing behavior when tasks resumed from deferral or rescheduled sensors -- the try count would be inflated without representing a genuine retry.

The new model is simpler: the scheduler assigns try_number before the task enters the queue, it stays constant for the duration of that attempt, and only a genuine new retry increments it. As a side effect, users who call ti.run() or airflow tasks run directly will no longer see try_number increment -- Airflow now assumes all executions are scheduler-driven.

A related improvement in 2.10 ensures that teardown tasks are no longer skipped when a DAG run is manually set to "failed" or "success" -- as long as the corresponding setup task had already started. This is critical if you use setup/teardown pairs to manage ephemeral cloud infrastructure. The DAG will temporarily remain in a running state while teardown executes before reaching the terminal state.

with DAG("infra_dag", ...):
    setup_task = create_cluster.as_setup()
    teardown_task = delete_cluster.as_teardown()

    setup_task >> run_job >> teardown_task
    # teardown_task now runs even if DAG is manually failed
    # as long as setup_task completed

What observability improvements ship with Airflow 2.10?

Airflow 2.10.0 graduated OpenTelemetry (OTel) traces from experimental to stable. The feature, introduced in preview during earlier 2.x releases, now emits structured trace data for the scheduler loop, triggerer, executor, and DAG file processor -- in addition to per-DAG-run traces that span the entire execution lifecycle.

Most teams already use OTel for metrics (stabilized in 2.9.3); the addition of traces means you can correlate a slow DAG run directly to scheduler contention or executor queue depth in your APM tooling (Datadog, Honeycomb, Jaeger, etc.) without writing custom instrumentation. Key configuration items to check:

  • Set [metrics] otel_on = True and configure otel_host / otel_port to enable trace export.
  • Override the service name via OTEL_SERVICE_NAME -- hardcoded defaults were removed in 2.10.3.
  • The OTEL_RESOURCE_ATTRIBUTES environment variable is now honored for enriching spans with deployment metadata.
  • Metrics for CPU and memory usage per task were added in 2.10.0, emitted via the existing statsd / OTel pipeline.

AIP-62 (Hook Lineage Instrumentation) also landed in 2.10.0, adding a HookLineageCollector that captures dataset-level lineage from hooks automatically, feeding into the dataset dependency graph in the UI.

What breaking changes and deprecations should teams plan for when upgrading to Airflow 2.9 or 2.10?

Several changes in the 2.9/2.10 line require deliberate migration work before upgrading. The most impactful are listed below grouped by effort level.

High effort -- requires code or infrastructure changes:

  • MSSQL removed (2.9.0): If your Airflow metadata database runs on SQL Server, you must migrate to PostgreSQL or MySQL before upgrading. A community migration script is available but carries no official support or warranty.
  • Dataset URI validation (2.9.0): Dataset identifiers that do not conform to AIP-60 URI rules will be rejected or silently normalized. Identifiers using the URI auth section, case-sensitive scheme names, or non-standard formats must be updated in DAG files.
  • Rendered Template Field length cap (2.9.0): Template fields exceeding 4096 characters are truncated. Use [core] max_template_field_length to raise the cap if your DAGs push large strings through template fields.

Low effort -- behavioral changes to be aware of:

  • AirflowTimeoutError now inherits from BaseException -- except Exception blocks will no longer catch task timeouts. Switch cleanup logic to finally blocks.
  • The /logout endpoint in FAB Auth Manager changed from GET to POST with CSRF protection. Any automation or health checks hitting this endpoint via GET will break.
  • Inactive/paused DAGs will no longer accumulate dataset events, changing run behavior after a DAG is re-enabled.
  • TaskFlow functions must not define context parameter defaults other than None. The scheduler now validates this at parse time and marks violating DAGs as broken.
# Before 2.9: could set non-None default on context var -- now invalid
@task
def my_task(execution_date="2024-01-01"):   # BROKEN in 2.9+
    ...

# Correct: always use None as default for context-injected params
@task
def my_task(execution_date=None):
    ...

Frequently Asked Questions about Apache Airflow 2

Does upgrading to Airflow 2.9 require changes to existing DAGs that use datasets?
Most DAGs will continue to work, but dataset identifiers that look like URIs must now conform to AIP-60 validation rules. Run a DAG parse in a staging environment first -- any non-conforming dataset URIs will surface as warnings or errors before they can affect production.

Is MSSQL still supported as an Airflow metadata database in Airflow 2?
No. Support for Microsoft SQL Server as the Airflow metadata database was removed in Airflow 2.9.0 following a community vote. Existing users must migrate to PostgreSQL or MySQL prior to upgrading using the community migration script available in the airflow-mssql-migration GitHub repository.

What is the fastest way to enable OpenTelemetry traces in Airflow 2.10?
Set otel_on to True under the metrics section in airflow.cfg, configure otel_host and otel_port to point to your OTLP collector, and restart all Airflow components. Traces for the scheduler loop and triggerer will begin emitting immediately; per-DAG-run traces appear once new DAG runs are triggered after the upgrade.

Can individual tasks in a single DAG be routed to different executors in Airflow 2.10?
Yes, this is exactly the use case for the new hybrid/multiple-executor feature introduced in 2.10.0. You pass the executor name as a string to the executor parameter on any operator or TaskFlow decorator, and the scheduler routes each task to the appropriate executor. The feature is still marked experimental but is usable in production with proper testing.

Does the try_number change in Airflow 2.10 affect existing retry logic or alert thresholds?
It can. Before 2.10, try_number was sometimes incremented for rescheduled sensors and deferred tasks even when no real retry occurred, inflating observed retry counts. After 2.10 the number is more accurate, so monitoring rules or custom code that compared try_number values may observe lower numbers for the same task behavior and need recalibration.

How do I use the new skip_if and run_if decorators introduced in Airflow 2.10?
Decorate a TaskFlow task with @task.run_if(lambda context: ...) or @task.skip_if(lambda context: ...) passing a callable that receives the task context and returns a boolean. These decorators replace common patterns of checking context variables inside task bodies and branching to a skip exception, making conditional task execution declarative. Note that these decorators are stripped before virtualenv tasks execute to avoid import issues in isolated environments.

Releases In Branch 2

VersionRelease date
2.11.214 Mar 2026
(2 months ago)
2.11.2rc109 Mar 2026
(2 months ago)
2.11.121 Feb 2026
(3 months ago)
2.11.1rc216 Feb 2026
(3 months ago)
2.11.016 May 2025
(1 year ago)
2.11.0rc116 May 2025
(1 year ago)
2.10.510 Feb 2025
(1 year ago)
2.10.5rc105 Feb 2025
(1 year ago)
2.10.416 Dec 2024
(1 year ago)
2.10.4rc110 Dec 2024
(1 year ago)
2.10.305 Nov 2024
(1 year ago)
2.10.3rc201 Nov 2024
(1 year ago)
2.10.3rc128 Oct 2024
(1 year ago)
2.10.220 Sep 2024
(1 year ago)
2.10.2rc117 Sep 2024
(1 year ago)
2.10.106 Sep 2024
(1 year ago)
2.10.1rc102 Sep 2024
(1 year ago)
2.10.015 Aug 2024
(1 year ago)
2.10.0rc112 Aug 2024
(1 year ago)
2.10.0b201 Aug 2024
(1 year ago)
2.10.0b125 Jul 2024
(1 year ago)
2.9.316 Jul 2024
(1 year ago)
2.9.3rc112 Jul 2024
(1 year ago)
2.9.210 Jun 2024
(1 year ago)
2.9.2rc106 Jun 2024
(1 year ago)
2.9.106 May 2024
(2 years ago)
2.9.1rc202 May 2024
(2 years ago)
2.9.1rc130 Apr 2024
(2 years ago)
2.9.008 Apr 2024
(2 years ago)
2.9.0rc307 Apr 2024
(2 years ago)
2.9.0rc204 Apr 2024
(2 years ago)
2.9.0rc103 Apr 2024
(2 years ago)
2.9.0b227 Mar 2024
(2 years ago)
2.8.425 Mar 2024
(2 years ago)
2.9.0b121 Mar 2024
(2 years ago)
2.8.4rc120 Mar 2024
(2 years ago)
2.8.311 Mar 2024
(2 years ago)
2.8.3rc107 Mar 2024
(2 years ago)
2.8.226 Feb 2024
(2 years ago)
2.8.2rc324 Feb 2024
(2 years ago)
2.8.2rc223 Feb 2024
(2 years ago)
2.8.2rc122 Feb 2024
(2 years ago)
2.8.119 Jan 2024
(2 years ago)
2.8.1rc116 Jan 2024
(2 years ago)
2.8.018 Dec 2023
(2 years ago)
2.8.0rc416 Dec 2023
(2 years ago)
2.8.0rc313 Dec 2023
(2 years ago)
2.8.0rc213 Dec 2023
(2 years ago)
2.8.0rc111 Dec 2023
(2 years ago)
2.8.0b127 Nov 2023
(2 years ago)
2.7.306 Nov 2023
(2 years ago)
2.7.3rc102 Nov 2023
(2 years ago)
2.7.212 Oct 2023
(2 years ago)
2.7.2rc109 Oct 2023
(2 years ago)
2.7.107 Sep 2023
(2 years ago)
2.7.1rc204 Sep 2023
(2 years ago)
2.7.1rc104 Sep 2023
(2 years ago)
2.7.018 Aug 2023
(2 years ago)
2.7.0rc216 Aug 2023
(2 years ago)
2.7.0rc111 Aug 2023
(2 years ago)
2.7.0b104 Aug 2023
(2 years ago)
2.6.310 Jul 2023
(2 years ago)
2.6.3rc107 Jul 2023
(2 years ago)
2.6.217 Jun 2023
(2 years ago)
2.6.2rc214 Jun 2023
(2 years ago)
2.6.2rc112 Jun 2023
(2 years ago)
2.6.116 May 2023
(3 years ago)
2.6.1rc315 May 2023
(3 years ago)
2.6.1rc212 May 2023
(3 years ago)
2.6.1rc111 May 2023
(3 years ago)
2.6.030 Apr 2023
(3 years ago)
2.6.0rc529 Apr 2023
(3 years ago)
2.6.0rc428 Apr 2023
(3 years ago)
2.6.0rc327 Apr 2023
(3 years ago)
2.6.0rc226 Apr 2023
(3 years ago)
2.6.0rc124 Apr 2023
(3 years ago)
2.6.0b114 Apr 2023
(3 years ago)
2.5.301 Apr 2023
(3 years ago)
2.5.3rc229 Mar 2023
(3 years ago)
2.5.3rc127 Mar 2023
(3 years ago)
2.5.215 Mar 2023
(3 years ago)
2.5.2rc213 Mar 2023
(3 years ago)
2.5.2rc110 Mar 2023
(3 years ago)
2.5.120 Jan 2023
(3 years ago)
2.5.1rc218 Jan 2023
(3 years ago)
2.5.1rc114 Jan 2023
(3 years ago)
2.5.002 Dec 2022
(3 years ago)
2.5.0rc301 Dec 2022
(3 years ago)
2.5.0rc227 Nov 2022
(3 years ago)
2.5.0rc126 Nov 2022
(3 years ago)
2.4.314 Nov 2022
(3 years ago)
2.4.3rc111 Nov 2022
(3 years ago)
2.4.224 Oct 2022
(3 years ago)
2.4.2rc120 Oct 2022
(3 years ago)
2.4.130 Sep 2022
(3 years ago)
2.4.1rc127 Sep 2022
(3 years ago)
2.4.019 Sep 2022
(3 years ago)
2.4.0rc115 Sep 2022
(3 years ago)
2.4.0b108 Sep 2022
(3 years ago)
2.3.423 Aug 2022
(3 years ago)
2.3.4rc119 Aug 2022
(3 years ago)
2.3.309 Jul 2022
(3 years ago)
2.3.3rc306 Jul 2022
(3 years ago)
2.3.3rc206 Jul 2022
(3 years ago)
2.3.3rc102 Jul 2022
(3 years ago)
2.3.204 Jun 2022
(3 years ago)
2.3.2rc201 Jun 2022
(3 years ago)
2.3.2rc130 May 2022
(4 years ago)
2.3.125 May 2022
(4 years ago)
2.3.1rc121 May 2022
(4 years ago)
2.3.030 Apr 2022
(4 years ago)
2.3.0rc227 Apr 2022
(4 years ago)
2.3.0rc127 Apr 2022
(4 years ago)
2.3.0b114 Apr 2022
(4 years ago)
2.2.504 Apr 2022
(4 years ago)
2.2.5rc330 Mar 2022
(4 years ago)
2.2.5rc228 Mar 2022
(4 years ago)
2.2.5rc127 Mar 2022
(4 years ago)
2.2.422 Feb 2022
(4 years ago)
2.2.4rc118 Feb 2022
(4 years ago)
2.2.321 Dec 2021
(4 years ago)
2.2.3rc214 Dec 2021
(4 years ago)
2.2.3rc110 Dec 2021
(4 years ago)
2.2.215 Nov 2021
(4 years ago)
2.2.2rc211 Nov 2021
(4 years ago)
2.2.2rc109 Nov 2021
(4 years ago)
2.2.129 Oct 2021
(4 years ago)
2.2.1rc226 Oct 2021
(4 years ago)
2.2.1rc124 Oct 2021
(4 years ago)
2.2.011 Oct 2021
(4 years ago)
2.2.0rc108 Oct 2021
(4 years ago)
2.2.0b223 Sep 2021
(4 years ago)
2.1.418 Sep 2021
(4 years ago)
2.1.4rc215 Sep 2021
(4 years ago)
2.2.0b111 Sep 2021
(4 years ago)
2.1.4rc111 Sep 2021
(4 years ago)
2.1.323 Aug 2021
(4 years ago)
2.1.3rc118 Aug 2021
(4 years ago)
2.1.210 Jul 2021
(4 years ago)
2.1.2rc110 Jul 2021
(4 years ago)
2.1.102 Jul 2021
(4 years ago)
2.1.1rc126 Jun 2021
(4 years ago)
2.1.018 May 2021
(5 years ago)
2.1.0rc218 May 2021
(5 years ago)
2.1.0rc118 May 2021
(5 years ago)
2.0.219 Apr 2021
(5 years ago)
2.0.2rc116 Apr 2021
(5 years ago)
2.0.108 Feb 2021
(5 years ago)
2.0.1rc204 Feb 2021
(5 years ago)
2.0.1rc104 Feb 2021
(5 years ago)
2.0.017 Dec 2020
(5 years ago)
2.0.0rc314 Dec 2020
(5 years ago)
2.0.0rc212 Dec 2020
(5 years ago)
2.0.0rc109 Dec 2020
(5 years ago)
2.0.0b318 Nov 2020
(5 years ago)
2.0.0b210 Nov 2020
(5 years ago)
2.0.0b109 Nov 2020
(5 years ago)
2.0.0a226 Oct 2020
(5 years ago)
2.0.0a113 Oct 2020
(5 years ago)