Explain.io FinOps Airflow Cost Agent
Project description
๐ธ Explain.io โ Airflow Cost Agent
Explain.io is a FinOps tool for Data Engineers. This lightweight Airflow plugin automatically attributes Google Cloud BigQuery compute costs to the exact DAG and Task that triggered them.
No more guessing which pipeline caused the bill spike. No code changes required.
โจ Features
- Zero Code Changes โ Uses Airflow's Listener interface to automatically intercept all BigQuery task completions. You don't need to modify any DAG or add any callbacks.
- Zero Blast Radius โ All network requests run asynchronously in a separate thread with strict timeouts. If the Explain.io API is unreachable, your DAG will still succeed. Exceptions are silently caught and logged.
- Instant Dashboard โ View your pipeline costs, projected monthly spend, and heaviest DAGs at explain.io.
๐ How It Works
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ Airflow โ โ Explain.io Agent โ โ Explain.io API โ
โ (Scheduler) โโโโโโถโ (Listener Plugin) โโโโโโถโ (Backend) โ
โ โ โ โ โ โ
โ Task succeedsโ โ 1. Intercepts event โ โ Fetches BQ cost โ
โ โ โ 2. Extracts job ID โ โ from GCP API โ
โ โ โ 3. POSTs to API โ โ and stores it โ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
The agent registers as an Airflow Listener plugin. On every task success, it checks if the task was a BigQuery operator, extracts the job ID via XCom, and sends it to the Explain.io API โ all in a background thread.
๐ Installation
Quick Start (Any Environment)
pip install explainio-airflow-agent
Then set two environment variables in your Airflow environment:
| Variable | Required | Description |
|---|---|---|
EXPLAIN_IO_API_KEY |
โ | Your Explain.io API key (get one from the dashboard) |
EXPLAIN_IO_API_URL |
โ | API endpoint. Defaults to https://api.explain.io/api/v1/ingest |
Verify it's working:
airflow plugins
You should see cost_agent_plugin in the output.
๐ฆ Installation by Platform
Self-Hosted: Docker (Recommended)
Don't use
_PIP_ADDITIONAL_REQUIREMENTSโ it reinstalls packages on every container start and is only meant for quick testing.
Extend the official Airflow image with a Dockerfile:
FROM apache/airflow:3.1.6
USER airflow
RUN pip install --no-cache-dir explainio-airflow-agent
Then in your docker-compose.yaml, replace image: with build::
x-airflow-common:
&airflow-common
build: ./airflow # instead of: image: apache/airflow:3.1.6
environment:
EXPLAIN_IO_API_KEY: ${EXPLAIN_IO_API_KEY}
EXPLAIN_IO_API_URL: ${EXPLAIN_IO_API_URL}
# ... other env vars
Build and start:
docker compose build
docker compose up -d
Important: All Airflow components (scheduler, webserver, workers, triggerer) must use the same image. The shared
x-airflow-commonanchor ensures this automatically.
Self-Hosted: Bare Metal / virtualenv
Install directly into the Python environment where Airflow runs:
pip install explainio-airflow-agent
Set the environment variables:
export EXPLAIN_IO_API_KEY="your-api-key"
export EXPLAIN_IO_API_URL="https://api.explain.io/api/v1/ingest" # optional
If you run a multi-node setup (e.g., separate scheduler and workers), the package must be installed on every node.
AWS MWAA (Managed Workflows for Apache Airflow)
-
Create or update your
requirements.txtwith the Airflow constraints file:--constraint "https://raw.githubusercontent.com/apache/airflow/constraints-2.10.1/constraints-3.11.txt" explainio-airflow-agent==0.1.2
Adjust the constraints URL to match your MWAA Airflow version and Python version. See MWAA docs.
-
Upload
requirements.txtto your MWAA environment's S3 bucket. -
In the AWS Console โ MWAA โ your environment โ Edit โ point to the new
requirements.txtpath โ Save. -
Set
EXPLAIN_IO_API_KEYand (optionally)EXPLAIN_IO_API_URLunder Environment variables โ Airflow configuration options, using the prefixAIRFLOW__:- Key:
EXPLAIN_IO_API_KEY, Value:your-api-key
- Key:
-
Wait for the environment update to complete (can take 20โ40 minutes).
Troubleshooting: Check CloudWatch Logs โ Log group: airflow-{env-name}-requirements_install for pip errors.
Google Cloud Composer
Option A โ Console UI
- Go to Environments โ click your environment โ PyPI Packages tab.
- Click Edit โ Add Package.
- Enter:
- Package name:
explainio-airflow-agent - Version:
>=0.1.2
- Package name:
- Save. Composer will restart workers and the scheduler.
Option B โ gcloud CLI
gcloud composer environments update YOUR_ENV_NAME \
--location YOUR_LOCATION \
--update-pypi-package "explainio-airflow-agent>=0.1.2"
Environment variables
Set via the Console UI (Environment Variables tab) or CLI:
gcloud composer environments update YOUR_ENV_NAME \
--location YOUR_LOCATION \
--update-env-variables EXPLAIN_IO_API_KEY=your-api-key
Astronomer (Astro CLI)
-
Add the package to
requirements.txtin your Astro project root:explainio-airflow-agent==0.1.2
-
Set environment variables in your
.envfile:EXPLAIN_IO_API_KEY=your-api-key -
Restart:
astro dev restart
-
Verify:
astro dev bash --scheduler "airflow plugins"
For Astro Cloud deployments, add the environment variables via the Astro UI under Deployments โ Environment Variables.
โ๏ธ Configuration Reference
| Variable | Required | Default | Description |
|---|---|---|---|
EXPLAIN_IO_API_KEY |
โ | โ | Your Explain.io API key. If not set, the plugin logs an info message and becomes a no-op. |
EXPLAIN_IO_API_URL |
โ | https://api.explain.io/api/v1/ingest |
Override the API endpoint (e.g., for self-hosted backends). |
๐ง Troubleshooting
Plugin not showing in airflow plugins
- Make sure the package is installed in the same Python environment as Airflow.
- Run
pip show explainio-airflow-agentto confirm it's installed. - Check Airflow scheduler logs for import errors.
Events not appearing in the dashboard
- Confirm
EXPLAIN_IO_API_KEYis set. Without it, the agent silently disables itself. - Check that
EXPLAIN_IO_API_URLpoints to a reachable endpoint. - Look for
[ExplainIO]log lines in the Airflow task logs.
AWS MWAA: Package fails to install
- Ensure the
--constraintline inrequirements.txtmatches your MWAA Airflow + Python version. - Check the
requirements_installlog stream in CloudWatch.
Supported Workloads
Explain.io perfectly isolates and captures BigQuery costs for the following workloads:
- Native Airflow Operators (e.g.,
BigQueryInsertJobOperator): Works instantly out-of-the-box. - dbt-core (via
BashOperator,KubernetesPodOperator, or Cosmos): Requires a 1-line configuration in yourprofiles.yml(see below). - Custom Scripts (e.g., raw Python/Bash): Supported if you manually inject the
airflow_dag_idandairflow_task_idlabels into your BigQuery job configuration.
dbt Support (Required Configuration)
Because dbt runs externally and does not natively report BigQuery job IDs back to Airflow, Explain.io requires you to configure dbt to pass context labels to BigQuery to guarantee 100% accurate cost attribution.
1. Configure dbt Labels
To prevent cost collisions when multiple DAGs run concurrently, inject the Airflow context variables into your BigQuery jobs by adding this to your profiles.yml:
# profiles.yml
my_bigquery_profile:
target: dev
outputs:
dev:
type: bigquery
# ... other config ...
labels:
airflow_dag_id: "{{ env_var('AIRFLOW_CTX_DAG_ID', '') }}"
airflow_task_id: "{{ env_var('AIRFLOW_CTX_TASK_ID', '') }}"
Explain.io perfectly isolates the costs to the correct DAG using these labels. If you skip this step, Explain.io will not be able to track your dbt costs.
2. Enable Per-Model Attribution (Optional)
For per-dbt-model cost breakdowns, add job labeling to your dbt_project.yml:
# dbt_project.yml
query-comment:
comment: "{{ query_comment(node) }}"
job-label: true
This tells dbt to label every BigQuery job with the model name (node_id). Explain.io reads these labels to show you exactly which dbt models are costing the most.
GCP Permissions
Your dedicated Explain.io service account needs the following roles:
- BigQuery Resource Viewer (
roles/bigquery.resourceViewer): To read BigQuery job metadata and labels (bigquery.jobs.listAll).
Security Note: Explain.io only requires metadata permissions. We cannot read your actual table data.
Grant it via:
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="serviceAccount:YOUR_SA@YOUR_PROJECT.iam.gserviceaccount.com" \
--role="roles/bigquery.resourceViewer"
๐ License
MIT
Project details
Release history Release notifications | RSS feed
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 explainio_airflow_agent-0.2.1.tar.gz.
File metadata
- Download URL: explainio_airflow_agent-0.2.1.tar.gz
- Upload date:
- Size: 11.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
152d4e920668195cac8c741379a3d96d29425209b78f83621304bcbbb2071d5b
|
|
| MD5 |
74e0380b7ebfedf70445d528c2f233eb
|
|
| BLAKE2b-256 |
04eef1a7e474942863331f7e1ef4645b45c5b0beda398d6b1d9cf65d820ad659
|
Provenance
The following attestation bundles were made for explainio_airflow_agent-0.2.1.tar.gz:
Publisher:
publish-pypi.yml on eskarimov/Explain.io
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
explainio_airflow_agent-0.2.1.tar.gz -
Subject digest:
152d4e920668195cac8c741379a3d96d29425209b78f83621304bcbbb2071d5b - Sigstore transparency entry: 1930671411
- Sigstore integration time:
-
Permalink:
eskarimov/Explain.io@0e51a33948c5181d2e0b136c0d682bdfea74d9cb -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/eskarimov
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@0e51a33948c5181d2e0b136c0d682bdfea74d9cb -
Trigger Event:
push
-
Statement type:
File details
Details for the file explainio_airflow_agent-0.2.1-py3-none-any.whl.
File metadata
- Download URL: explainio_airflow_agent-0.2.1-py3-none-any.whl
- Upload date:
- Size: 8.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ac5582ad5d9d146414cadb4e6df6ad76cad6c6fd4bdfa31e46a3e28ee6a882e
|
|
| MD5 |
1187e41aeec1581a84edfed6560bcb8a
|
|
| BLAKE2b-256 |
fbd6a38b114ba5ddce5d49fd494cbf10dfe3e8bc41c1b10b6c93e5df5acee6a4
|
Provenance
The following attestation bundles were made for explainio_airflow_agent-0.2.1-py3-none-any.whl:
Publisher:
publish-pypi.yml on eskarimov/Explain.io
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
explainio_airflow_agent-0.2.1-py3-none-any.whl -
Subject digest:
8ac5582ad5d9d146414cadb4e6df6ad76cad6c6fd4bdfa31e46a3e28ee6a882e - Sigstore transparency entry: 1930671563
- Sigstore integration time:
-
Permalink:
eskarimov/Explain.io@0e51a33948c5181d2e0b136c0d682bdfea74d9cb -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/eskarimov
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@0e51a33948c5181d2e0b136c0d682bdfea74d9cb -
Trigger Event:
push
-
Statement type: