Serverless Airflow Executor on Modal
Project description
Modalflow
A serverless Airflow executor that runs tasks as Modal Functions.
Modalflow replaces Airflow's built-in executors (Local, Celery, Kubernetes) with one that dispatches each task to a Modal Function. No worker pools, no Kubernetes cluster, no infrastructure to manage — tasks run on-demand and scale to zero when idle.
Prerequisites
- An existing Airflow 3.1+ deployment
- A Modal account with the CLI configured (
modal setup) - Python 3.10+
Setup
1. Install
Install modalflow on both your local machine (for the CLI) and your Airflow cluster (for the executor):
pip install modalflow
2. Deploy the Modal backend
The modalflow deploy command creates the Modal Function, Volume, and Dict that the executor needs. You must choose how DAG files are provided to the Modal task workers:
Local mode — bakes DAGs into the function image (simplest, but requires redeploying on every DAG change):
modalflow deploy --dags-source local --dags-path ./dags
Volume mode — stores DAGs on a Modal Volume (update DAGs without redeploying):
modalflow deploy --dags-source volume --dags-volume my-dags --dags-path ./dags
Cloud bucket mode — reads DAGs from an S3 bucket at runtime:
modalflow deploy --dags-source cloud-bucket \
--dags-bucket s3://my-bucket/dags \
--dags-bucket-secret my-aws-secret
To target a specific Modal environment, add --env <name> (default: main).
3. Configure Airflow
Set the executor class in airflow.cfg or via environment variable:
[core]
executor = modalflow.executor.ModalExecutor
export AIRFLOW__CORE__EXECUTOR=modalflow.executor.ModalExecutor
The executor reads MODALFLOW_ENV to find the right Modal app (default: main). Set it if you deployed with a custom --env.
Updating DAGs (volume mode)
With volume mode, use modalflow sync to push DAG changes without redeploying the function:
modalflow sync --dags-path ./dags --dags-volume my-dags
This does a full replace — files deleted locally are also removed from the volume.
Networking
Modal Functions run in Modal's cloud. To execute a task, the function must call back to your Airflow deployment's execution API. This means Airflow's API server must be reachable from the public internet.
The executor resolves the execution API URL in priority order:
AIRFLOW__CORE__EXECUTION_API_SERVER_URLenvironment variablecore.execution_api_server_urlinairflow.cfgmodal.forward()tunnel (local development only)
Production
Set the URL to your Airflow API's public endpoint:
export AIRFLOW__CORE__EXECUTION_API_SERVER_URL=https://airflow.example.com/execution/
Common ways to expose the API:
- Load balancer (ALB, NLB) in front of the Airflow API server
- API Gateway with auth
- Reverse tunnel (Cloudflare Tunnel, ngrok) if you can't expose a public endpoint directly
Local development
When running Airflow locally (e.g. airflow standalone), the executor falls back to modal.forward(), which tunnels traffic from Modal to localhost:8080. No configuration needed.
Development
uv sync --extra dev
uv run modalflow deploy --help
uv run pytest # unit tests
make system.setup && make system.test.e2e # E2E tests (requires Modal)
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file modalflow-0.2.3.tar.gz.
File metadata
- Download URL: modalflow-0.2.3.tar.gz
- Upload date:
- Size: 9.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b57c1cc46ec3f49d763654b43421898e15aecc7b465ae21b035572b1f3024360
|
|
| MD5 |
9fa97586620e9d4ded7969933beab264
|
|
| BLAKE2b-256 |
0813e3f8173621ed723327adfb280da48bdcedf73d6ab9e2f84a4228475bd631
|
Provenance
The following attestation bundles were made for modalflow-0.2.3.tar.gz:
Publisher:
publish.yml on agupta01/modalflow
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
modalflow-0.2.3.tar.gz -
Subject digest:
b57c1cc46ec3f49d763654b43421898e15aecc7b465ae21b035572b1f3024360 - Sigstore transparency entry: 1031791660
- Sigstore integration time:
-
Permalink:
agupta01/modalflow@9f4b9e7c153e941f14aa284d66f0b10f94cbdbd6 -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/agupta01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9f4b9e7c153e941f14aa284d66f0b10f94cbdbd6 -
Trigger Event:
release
-
Statement type:
File details
Details for the file modalflow-0.2.3-py3-none-any.whl.
File metadata
- Download URL: modalflow-0.2.3-py3-none-any.whl
- Upload date:
- Size: 11.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67fe4e21ff1cc336c6b8c6e8605f355a769c49751ae892e6547d7fa16ed467e4
|
|
| MD5 |
7d0630c9cc193510a10d3e1fd6701352
|
|
| BLAKE2b-256 |
d7b45c54c0023e351477de2c2e840b03a7e22295bf90532d404d142324ae41ce
|
Provenance
The following attestation bundles were made for modalflow-0.2.3-py3-none-any.whl:
Publisher:
publish.yml on agupta01/modalflow
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
modalflow-0.2.3-py3-none-any.whl -
Subject digest:
67fe4e21ff1cc336c6b8c6e8605f355a769c49751ae892e6547d7fa16ed467e4 - Sigstore transparency entry: 1031791712
- Sigstore integration time:
-
Permalink:
agupta01/modalflow@9f4b9e7c153e941f14aa284d66f0b10f94cbdbd6 -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/agupta01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9f4b9e7c153e941f14aa284d66f0b10f94cbdbd6 -
Trigger Event:
release
-
Statement type: