aca-executor
An Airflow 3.x executor that runs each task as a throwaway Azure Container Apps Job. It is the Azure Container Apps analogue of Airflow's Kubernetes Executor: for every task instance it creates a dedicated Manual-trigger Job, starts one execution, polls it to completion, then deletes the Job.
How it works
task queued ──► _process_workloads ──► PUT Microsoft.App/jobs/{name} (create Manual job)
│
sync() heartbeat loop: ▼
CREATING ── GET provisioningState ──► POST .../start ──► RUNNING
RUNNING ── GET .../executions/{exec} ──► Succeeded → success()
Failed → fail()
│
▼
DELETE Microsoft.App/jobs/{name}
- Each task maps to a dedicated ACA Job, named deterministically from the
TaskInstanceKey(af-j-<sha1[:16]>— ACA job names are limited to 32 chars). - The executor talks to the Azure Resource Manager REST API
(
management.azure.com) directly withrequests. - Authentication uses
DefaultAzureCredential, so managed identity, an environment-variable service principal, or the Azure CLI login all work without extra config. - State is polled inside
sync()via a lightweight per-task state machine, so the scheduler heartbeat is never blocked waiting on Azure.
Requirements
- Apache Airflow >= 3.0 (uses the AIP-72 workload / Task Execution API interface).
- An existing Azure Container Apps managed environment.
- A worker container image that has Airflow installed and can reach the
Airflow Execution API server (
AIRFLOW__CORE__EXECUTION_API_SERVER_URL). - The credential's identity needs Contributor on the resource group (to create/start/delete jobs).
Installation
pip install . # or: uv pip install -e .
Configuration
Point Airflow at the executor and add an [aca_executor] section to
airflow.cfg (or use the equivalent AIRFLOW__ACA_EXECUTOR__* env vars):
[core]
executor = aca_executor.executor.AzureContainerAppsExecutor
[aca_executor]
# --- required ---
subscription_id = 00000000-0000-0000-0000-000000000000
resource_group = my-rg
managed_environment_id = /subscriptions/.../providers/Microsoft.App/managedEnvironments/my-env
location = eastus
container_image = myregistry.azurecr.io/airflow-worker:latest
# --- optional (defaults shown) ---
cpu = 0.5
memory = 1Gi
replica_timeout = 1800
replica_retry_limit = 0
api_version = 2024-03-01
delete_job_on_completion = True
# Extra env vars injected into every task container (JSON object)
env = {"MY_VAR": "value"}
# --- logging (optional, see below) ---
logs_volume_storage_name = # Azure Files storage registered on the env
logs_mount_path = # defaults to [logging] base_log_folder
Logging
This executor handles logs the same way CeleryExecutor does: it does not ship or upload logs itself. The Airflow task process running inside the ACA container uses the standard Airflow logging stack:
- Local filesystem, standard format. The task's
FileTaskHandlerwrites tobase_log_folderusing Airflow's standard path layout (dag_id/run_id/task_id/attempt=N.log), so any other component reads them exactly as it reads Celery/Kubernetes worker logs. - Remote logging on task conclusion. If you enable
[logging] remote_logging = Truewith an Azure Blob (WASB) remote handler, the standard handler uploads the finished log when it closes at task exit. No executor code is involved (make sure the WASB provider is installed in the worker image).
The executor's only logging responsibility is environment passthrough: it
forwards the relevant AIRFLOW__LOGGING__* options and
AIRFLOW__CORE__EXECUTION_API_SERVER_URL into the container so the in-container
task process is configured just like a Celery worker.
Because remote upload only happens on a clean handler close (task exit), set
logs_volume_storage_name to mount an Azure Files share at base_log_folder.
This preserves logs for tasks that are killed or whose Job is deleted before
completion. The share must already be registered on the managed environment as a
Microsoft.App/managedEnvironments/storages
resource; its name goes in logs_volume_storage_name.
Testing
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python -e ".[dev]"
.venv/bin/python -m pytest
Unit tests mock the ARM REST calls (requests-mock) and the hook, so no Azure
account is needed.
Not yet implemented / future work
get_task_logis intentionally not overridden — the stock file/remote log handlers already serve task logs. A future version could query Azure Log Analytics for in-container stdout.get_cli_commands(executor-specific CLI helpers).
Metadata
Release files for aca-executor 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| aca_executor-0.1.0.tar.gz | 16.6 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aca_executor-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 29.2 kB
Release files / aca_executor-0.1.0.tar.gz
| Download URL | aca_executor-0.1.0.tar.gz |
|---|---|
| Size | 16.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fbe234b058a64902819d5ca613ee72e4737c2d1dcf25bee76137561fcd3d2ad2
|
|
BLAKE2b-256 checksum How to use checksums |
96727d0613635dc076794e0eec3a55d63f38545656ecc203d56aa7b609bc296e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 6, 2026.
Transparency logRelease files / aca_executor-0.1.0-py3-none-any.whl
| Download URL | aca_executor-0.1.0-py3-none-any.whl |
|---|---|
| Size | 12.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dce85aaadb0ead98daba3658852c730cfc7713950552a1be00ec2941d08a7bb9
|
|
BLAKE2b-256 checksum How to use checksums |
d2c8937724b082943a9eb8e02a1ad6bd902d21a287522981b58b15385a77d8a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 6, 2026.
Transparency logSecurity for aca-executor 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for aca_executor-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 6, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.