Observed since 20 Oct, 17:51 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Airflow 3.x executor that runs each task as a throwaway Azure Container Apps Job
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.
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}
TaskInstanceKey (af-j-<sha1[:16]> — ACA job names are limited to 32 chars).management.azure.com) directly with requests.DefaultAzureCredential,
so managed identity, an environment-variable service principal, or the Azure
CLI login all work without extra config.sync() via a lightweight per-task state machine, so
the scheduler heartbeat is never blocked waiting on Azure.AIRFLOW__CORE__EXECUTION_API_SERVER_URL).pip install . # or: uv pip install -e .
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
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:
FileTaskHandler writes to
base_log_folder using 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.[logging] remote_logging = True with 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.
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.
get_task_log is 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).Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.1.0 | 6 Aug 2026 | 5 |
This describes our collected data; it is not a security assessment.
Shared repository data · GitHub
Observed since 20 Oct, 17:51 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:ce8bd0802213154fd57c5fd13dd7bfaaad74caabca0b7490d97106a9f2c921f6