delivered for clients · names withheldupgrade Data and analytics

Apache Airflow 2 to Airflow 3

Cohash upgrades self-managed Airflow 2 deployments to Airflow 3. We bring the cluster to the latest 2.x release, lint DAGs with Ruff's AIR rules, and fix what Airflow 3 removed: SubDAGs, direct metadata database access, old context keys and the v1 REST API. DAGs then move in batches to a parallel Airflow 3 environment, with scheduling behavior checked so no job runs twice or gets skipped.

  1. 01Get to the latest 2.x
  2. 02Lint and fix DAGs
  3. 03Remove direct database access
  4. 04Config, auth and plugins
  5. 05Parallel environment
  6. 06Verify scheduling and SSO
case-study / airflow-2-to-3 delivered
beforeApache Airflow 2.x afterApache Airflow 3
01 · CALLERS 02 · AIRFLOW 3 03 · STORAGE AND TARGETS CI pipelines: API token CI pipelines API token Monitoring tools: read-only token Monitoring tools read-only token Airflow 3 api-server: /api/v2 · auth manager Airflow 3api-server /api/v2 · authmanager Scheduler: TaskGroups · Assets Scheduler TaskGroups ·Assets dag-processor: separate service dag-processor separateservice Custom operators: Airflow Python client Customoperators Airflow Pythonclient Metadata database: migrated copy Metadata database migrated copy Data platforms: Snowflake · Databricks Data platforms Snowflake · Databricks
case file

The story

Told without the client's name or anything that could identify them.

what happened

A retailer's data engineering team was still running self-managed Airflow 2 after its end of life on 22 April 2026. Custom operators opened SQLAlchemy sessions against the metadata database. Old DAGs still used SubDAGs and execution_date, and CI pipelines triggered runs through /api/v1. We got the cluster onto the latest 2.x release and ran Ruff's AIR rules, which turned the work into a list. Custom code moved onto the Airflow Python client with API tokens. DAGs then moved in batches to a parallel Airflow 3 environment. For each batch we paused the schedules on the old side and checked the catchup and cron timetable changes. Once the last batch checked out, the Airflow 2 environment was retired.

why it was urgent

Airflow 2 is out of support. It went into limited maintenance on 22 October 2025 and reached end of life on 22 April 2026, so it gets no fixes now. Airflow 3 has been out since 22 April 2025, and the current patch is 3.3.2. If you're still below 2.7, plan an extra hop, because the official upgrade path starts there.

problem statement
  • openYour scheduler still runs Airflow 2.x on infrastructure you manage yourself.
  • recurringSomebody's custom operators open SQLAlchemy sessions straight against the Airflow metadata database.
  • blockedOld DAGs still use SubDAGs or read execution_date from the task context.
  • openOther systems trigger DAGs through the /api/v1 REST endpoints.
architecture

The estate, before and after

before
before

Before: self-managed Airflow 2 past end of life

The Airflow 2 webserver and scheduler shared one metadata database. Custom operators wrote to it directly too. External tools used the v1 REST API to trigger DAGs, and that API is gone in Airflow 3.

Before: self-managed Airflow 2 past end of life The Airflow 2 webserver and scheduler shared one metadata database. Custom operators wrote to it directly too. External tools used the v1 REST API to trigger DAGs, and that API is gone in Airflow 3. 01 · CALLERS 02 · AIRFLOW 2 03 · STORAGE AND TARGETS /api/v1 trigger /api/v1 status reads scheduling direct writes run tasks load jobs CI pipelines trigger DAG runs Monitoring tools poll run status Airflow 2 webserver /api/v1 · FAB plugins Airflow 2 scheduler SubDAGs · execution_date Custom operators SQLAlchemy sessions Metadata database PostgreSQL · MySQL Data platforms Snowflake · Databricks
Read the diagram as text
  1. CI pipelines → Airflow 2 webserver: /api/v1 trigger
  2. Monitoring tools → Airflow 2 webserver: /api/v1 status
  3. Airflow 2 webserver → Metadata database: reads
  4. Airflow 2 scheduler → Metadata database: scheduling
  5. Custom operators → Metadata database: direct writes
  6. Airflow 2 scheduler → Custom operators: run tasks
  7. Custom operators → Data platforms: load jobs
after
after

After: Airflow 3 with API-only access to metadata

Task code and external callers both go through the Airflow 3 api-server these days, and only Airflow's own services touch the metadata database. The dag-processor runs as its own service.

After: Airflow 3 with API-only access to metadata Task code and external callers both go through the Airflow 3 api-server these days, and only Airflow's own services touch the metadata database. The dag-processor runs as its own service. 01 · CALLERS 02 · AIRFLOW 3 03 · STORAGE AND TARGETS /api/v2 trigger /api/v2 status python client reads · writes scheduling serialized DAGs run tasks load jobs CI pipelines API token Monitoring tools read-only token Airflow 3 api-server /api/v2 · auth manager Scheduler TaskGroups · Assets dag-processor separate service Custom operators Airflow Python client Metadata database migrated copy Data platforms Snowflake · Databricks
Read the diagram as text
  1. CI pipelines → Airflow 3 api-server: /api/v2 trigger
  2. Monitoring tools → Airflow 3 api-server: /api/v2 status
  3. Custom operators → Airflow 3 api-server: python client
  4. Airflow 3 api-server → Metadata database: reads · writes
  5. Scheduler → Metadata database: scheduling
  6. dag-processor → Metadata database: serialized DAGs
  7. Scheduler → Custom operators: run tasks
  8. Custom operators → Data platforms: load jobs
results

What the client got

What changed for the client once the work was done. Described, not scored: we don't publish client figures.

  1. result 01

    The scheduler runs a supported Airflow 3 release

    We got production DAGs onto Airflow 3. The api-server and dag-processor run as separate services, and they're defined as code with the auth and plugin configuration.

  2. result 02

    Task code no longer touches the metadata database

    We moved custom operators and callbacks onto the Airflow Python client with API tokens, as Airflow 3 requires. None of them opens a SQLAlchemy session any more.

  3. result 03

    No job ran twice or got skipped at cut-over

    We checked every batch against the catchup_by_default change and the new CronTriggerTimetable default. Once a batch was live on 3, its Airflow 2 schedules got paused.

  4. result 04

    External callers use /api/v2

    We tracked down the CI pipelines, other schedulers and monitoring tools that called /api/v1 and repointed each one. There's a list of every caller that moved.

  5. result 05

    DAG code is current and linted

    SubDAGs became TaskGroups or Assets, context keys moved to logical_date, SLAs became Deadline Alerts and imports moved to airflow.sdk, all in version control.

what can go wrong

Risks we plan for

  1. Silent schedule changes

    catchup_by_default is now False, and bare cron strings now use CronTriggerTimetable unless create_cron_data_intervals is set. DAGs that depend on data_interval values can run with different dates.

  2. Integrations on the old API

    The /api/v1 REST API is gone. CI pipelines, other schedulers and monitoring tools that call it break on cut-over unless found first.

  3. xcom_pull behavior

    xcom_pull without task_ids now reads only from the current task. DAGs that relied on the old behavior pass the wrong values without failing.

  4. Managed service timelines

    MWAA, Cloud Composer and Astronomer publish their own version support dates. If part of the estate runs on one of them, its dates set the plan for that part.

ai in this move

Where AI helps, and where it needs guardrails

Airflow has no vendor AI features, so any AI help is around the code. Coding agents are good at the mechanical rewrites Ruff flags, like context keys, imports and SubDAG conversions, provided every change is a reviewed pull request with a test run behind it. Once you're upgraded, an agent can read run status through /api/v2 with a read-only token. Triggering or clearing a DAG run still needs a person to approve it.

forward deployed engineers →
you keep

What you're left with

We write these for your setup, and they're working documents. Your team should be able to keep things running from them long after we've gone.

  • inventory01

    DAG inventory with owners, run history and Ruff findings

    yours to keep
  • evidence02

    Fixed DAGs and custom operators in version control

    yours to keep
  • code03

    Airflow 3 environment as code, with auth and plugin configuration

    yours to keep
  • runbook04

    Batch cut-over plan with schedule checks per DAG

    yours to keep
  • inventory05

    List of external callers moved to /api/v2

    yours to keep
talent and delivery · apache airflow 3

Certified engineers, wherever you need them.

Some clients want a project team, some want a couple of engineers on theirs. Either works. Onsite, nearshore or offshore, with handovers across time zones that keep things moving.

Onsite

Engineers at your sites in the US

Nearshore

Overlapping hours with US teams

Offshore

Overnight progress on your backlog

All time zones

Coverage handed over across time zones

24-hour coverage · US Central time
OnsiteUS business hours
Nearshoreoverlapping US hours
Offshoreovernight progress
Combinedhandover, not hand-off
01Is staying on Airflow 2 a real risk?

For a self-managed install, yes. Airflow 2 reached end of life on 22 April 2026, so security fixes have stopped. Managed services set their own dates, which we check for your provider.

02Can we upgrade in place?

You can, once the database is backed up. We'd still rather stand up a parallel environment. DAGs move over in batches that way, and if one batch misbehaves you still have a working Airflow 2 to fall back on.

03How much DAG code has to change?

It depends on how many DAGs use removed features and how much custom code touches the metadata database. Ruff's AIR rules give a count early, which turns the estimate into a list.

04Should we move to a managed Airflow service at the same time?

It's possible, but two changes at once make problems harder to trace. We usually upgrade first, then move hosting if the business case holds.

sources 2 sources linked last checked 2026-10-20 content fingerprint 109df43e
  1. 01Apache Airflow: Supported versionsairflow.apache.org ↗
  2. 02Apache Airflow: Upgrading to Airflow 3airflow.apache.org ↗
Reviewed by the Cohash team Product names are trademarks of their owners. how we handle facts and data →
next step

Planning this move?

A 30-minute working session with a senior engineer. No slides.

useful to bring
  • The platform and the version you're on
  • Where you need to get to, and by when
  • What's custom: rules, plugins, integrations
  • Any audit finding or deadline driving it