@task decorator or the PythonOperator, and deferrable operators. Async tasks run concurrent async code directly on workers, while deferrable operators offload long-running polling to the triggerer component, releasing the worker slot. Both approaches use Python’s asyncio library.
Assumed knowledge
To get the most out of this guide, you should have an understanding of:- Airflow operators. See Operators 101.
- Airflow sensors. See Sensors 101.
- Python’s asyncio library.
When to use async tasks vs deferrable operators
Airflow provides two distinct mechanisms for asynchronous execution. The right choice depends on what your task does while it waits.Async tasks
In Airflow 3.2+, workers can runasync def functions natively. You can define an async function as a task using the @task decorator or the PythonOperator. The worker executes the function in an asyncio event loop, allowing you to use await, asyncio.gather, and other asyncio patterns directly.
Async @task decorator
The following example Dag defines an async task that fetches data from two endpoints concurrently usingasyncio.gather. Both requests run in parallel, completing in roughly 10 seconds instead of the 15 seconds it would take to fetch them sequentially.
Async PythonOperator
You can also pass an async callable to the PythonOperator:
Deferrable operators
Deferrable operators use the Python asyncio library to efficiently run tasks waiting for an external resource to finish. When a task is deferred, it releases its worker slot and submits a trigger to the triggerer process. This frees up your workers and allows you to use resources more effectively.Terms and concepts
Review the following terms and concepts to gain a better understanding of deferrable operator functionality:- asyncio: A Python library used as the foundation for multiple asynchronous frameworks. This library is core to deferrable operator functionality, and is used when writing triggers.
- Triggers: Small, asynchronous sections of Python code. Due to their asynchronous nature, they coexist efficiently in a single process known as the triggerer.
- Triggerer: An Airflow service similar to a scheduler or a worker that runs an asyncio event loop in your Airflow environment. Running a triggerer is essential for using deferrable operators.
- Deferred: An Airflow task state indicating that a task has paused its execution, released the worker slot, and submitted a trigger to be picked up by the triggerer process.
How deferrable operators work
With traditional operators, a task submits a job to an external system such as a Spark cluster and then polls the job status until it is completed. Although the task isn’t doing significant work, it still occupies a worker slot during the polling process. As worker slots are occupied, tasks are queued and start times are delayed. The following image illustrates this process:

Some deferrable operators directly enter a deferred state without going to a worker first, see Triggering Deferral from Start.
Benefits
There are numerous benefits to using deferrable operators:- Reduced resource consumption: Depending on the available resources and the workload of your triggers, you can run hundreds to thousands of deferred tasks in a single triggerer process. This can lead to a reduction in the number of workers needed to run tasks during periods of high concurrency. With fewer workers needed, you can scale down the underlying infrastructure of your Airflow environment.
- Resiliency against restarts: Triggers are stateless by design. This means your deferred tasks aren’t set to a failure state if a triggerer needs to be restarted due to a deployment or infrastructure issue. When a triggerer is back up and running in your environment, your deferred tasks resume.
Use deferrable operators
Deferrable operators should be used whenever you have tasks that occupy a worker slot while polling for a condition in an external system. For example, using deferrable operators for sensor tasks can provide efficiency gains and reduce operational costs.Start a triggerer
To use deferrable operators, you must have a triggerer running in your Airflow environment. On Astro the triggerer is automatically configured for every Deployment. If you are using Astro Private Cloud, see Configure a Deployment on Astro Private Cloud - Triggerer. If you aren’t using Astro, runairflow triggerer to start a triggerer process in your Airflow environment. Your output should look similar to the following image:

default_capacity configuration setting in Airflow. This config can also be set with the AIRFLOW__TRIGGERER__DEFAULT_CAPACITY environment variable. The default value is 1000.
Use deferrable versions of operators
Many Airflow operators, such as theTriggerDagRunOperator and the WasbBlobSensor, can be set to run in deferrable mode using the deferrable parameter. You can check if the operator you want to use has a deferrable parameter in the Airflow Registry.
To always use the deferrable version of an operator if it’s available, set the Airflow config operators.default_deferrable to True. You can do so by defining the following environment variable in your Airflow environment:
deferrable parameter run as their deferrable version by default. You can override the config setting at the operator level using the deferrable parameter directly:
deferrable parameter was available in regular operators, deferrable operators were implemented as standalone operators, usually with an -Async suffix. Some of these operators are still available. For example, the DateTimeSensor doesn’t have a deferrable parameter, but has a deferrable version called DateTimeSensorAsync.
The Astronomer providers package, which contained many
-Async operators, is deprecated. The functionality from most of these operators is integrated into their original operator version in the relevant Airflow provider package.Example: Deferrable sensor
The following example Dag is scheduled to run every minute between itsstart_date and its end_date. Every Dag run contains one sensor task that will potentially take up to 20 minutes to complete.
DateTimeSensor, one worker slot is taken up by every sensor that runs. By using the deferrable version of this sensor, DateTimeSensorAsync, you can achieve full concurrency while freeing up your workers to complete additional tasks across your Airflow environment.
In the following image, running the Dag produces 16 running task instances, each containing one active DateTimeSensor taking up one worker slot.

DateTimeSensor for DateTimeSensorAsync creates 16 running Dag instances, but the tasks for these Dags are in a deferred state which doesn’t take up a worker slot. The only difference in the Dag code is using the deferrable operator DateTimeSensorAsync over DateTimeSensor:

High availability
Triggers are designed to be highly available. You can implement this by starting multiple triggerer processes. Similar to the HA scheduler, Airflow ensures that they co-exist with correct locking and high availability. See High Availability for more information on this topic.Create a deferrable operator
If you have an operator that would benefit from being asynchronous but doesn’t yet exist in OSS Airflow, you can create your own by writing a deferrable operator and trigger class. You can also defer a task several times if needed. Get a template for a custom deferrable operator and custom trigger class by clicking the dropdown. Make sure to adjust the classpath for your trigger’s.serialize method (currently include.deferrable_operator_template.MyTrigger) to match your file structure.
Note that when developing a custom trigger, you need to restart your triggerer to pick up any changes you make, since the triggerer caches the trigger classes. Additionally, all information you pass between the triggerer and the worker must be JSON serializable.
See Writing Deferrable Operators for more information.
You can implement direct deferral without the task ever being picked back up by a worker. The following code shows a deferrable operator circumventing the .execute() method. When using this template, make sure to adjust the classpath for your trigger (currently include.deferrable_operator_template.MyTrigger) in both the .serialize method and the StartTriggerArgs to match your file structure.
See Triggering Deferral from Start for more details and code examples.