migrating-airflow-2-to-3

Détection automatisée et migration de code pour la mise à niveau des DAGs Apache Airflow 2.x vers Airflow 3.x. Fournit des règles de correction automatique basées sur Ruff (AIR30/AIR301/AIR302/AIR31/AIR311/AIR312) pour détecter et résoudre les changements cassants dans les imports, opérateurs, hooks et variables de contexte. Couvre les changements architecturaux critiques : les workers n'accèdent plus directement à la base de données de métadonnées ; utilisez le client Python Airflow ou l'API REST au lieu des requêtes de session ORM. Inclut une liste de contrôle de migration manuelle pour les problèmes que Ruff ne peut pas corriger automatiquement : cron...

npx skills add https://github.com/astronomer/agents --skill migrating-airflow-2-to-3

Airflow 2 to 3 Migration

This skill helps migrate Airflow 2.x DAG code to Airflow 3.x, focusing on code changes (imports, operators, hooks, context, API usage).

Important: Before migrating to Airflow 3, strongly recommend upgrading to Airflow 2.11 first, then to at least Airflow 3.0.11 (ideally directly to 3.1). Other upgrade paths would make rollbacks impossible. See: https://www.astronomer.io/docs/astro/airflow3/upgrade-af3#upgrade-your-airflow-2-deployment-to-airflow-3. Additionally, early 3.0 versions have many bugs - 3.1 provides a much better experience.

Migration at a Glance

  1. Run Ruff's Airflow migration rules to auto-fix detectable issues (AIR30/AIR301/AIR302/AIR31/AIR311/AIR312).
    • ruff check --preview --select AIR --fix --unsafe-fixes .
  2. Scan for remaining issues using the manual search checklist in reference/migration-checklist.md.
    • Focus on: direct metadata DB access, legacy imports, scheduling/context keys, XCom pickling, datasets-to-assets, REST API/auth, plugins, and file paths.
    • Hard behavior/config gotchas to explicitly review:
      • Cron scheduling semantics: consider AIRFLOW__SCHEDULER__CREATE_CRON_DATA_INTERVAL=True if you need Airflow 2-style cron data intervals.
      • .airflowignore syntax changed from regexp to glob; set AIRFLOW__CORE__DAG_IGNORE_FILE_SYNTAX=regexp if you must keep regexp behavior.
      • OAuth callback URLs add an /auth/ prefix (e.g. /auth/oauth-authorized/google).
      • Shared utility imports: Bare imports like import common from dags/common/ no longer work on Astro. Use fully qualified imports: import dags.common.
  3. Plan changes per file and issue type:
    • Fix imports - update operators/hooks/providers - refactor metadata access to using the Airflow client instead of direct access - fix use of outdated context variables - fix scheduling logic.
  4. Implement changes incrementally, re-running Ruff and code searches after each major change.
  5. Explain changes to the user and caution them to test any updated logic such as refactored metadata, scheduling logic and use of the Airflow context.

Architecture & Metadata DB Access

Airflow 3 changes how components talk to the metadata database:

  • Workers no longer connect directly to the metadata DB.
  • Task code runs via the Task Execution API exposed by the API server.
  • The DAG processor runs as an independent process separate from the scheduler.
  • The Triggerer uses the task execution mechanism via an in-process API server.

Trigger implementation gotcha: If a trigger calls hooks synchronously inside the asyncio event loop, it may fail or block. Prefer calling hooks via sync_to_async(...) (or otherwise ensure hook calls are async-safe).

Key code impact: Task code can still import ORM sessions/models, but any attempt to use them to talk to the metadata DB will fail with:

RuntimeError: Direct database access via the ORM is not allowed in Airflow 3.x

Patterns to search for

When scanning DAGs, custom operators, and @task functions, look for:

  • Session helpers: provide_session, create_session, @provide_session
  • Sessions from settings: from airflow.settings import Session
  • Engine access: from airflow.settings import engine
  • ORM usage with models: session.query(DagModel)..., session.query(DagRun)...

Replacement: Airflow Python client

Preferred for rich metadata access patterns. Add to requirements.txt:

apache-airflow-client==<your-airflow-runtime-version>

Example usage:

import os
from airflow.sdk import BaseOperator
import airflow_client.client
from airflow_client.client.api.dag_api import DAGApi

_HOST = os.getenv("AIRFLOW__API__BASE_URL", "https://<your-org>.astronomer.run/<deployment>/")
_TOKEN = os.getenv("DEPLOYMENT_API_TOKEN")

class ListDagsOperator(BaseOperator):
    def execute(self, context):
        config = airflow_client.client.Configuration(host=_HOST, access_token=_TOKEN)
        with airflow_client.client.ApiClient(config) as api_client:
            dag_api = DAGApi(api_client)
            dags = dag_api.get_dags(limit=10)
            self.log.info("Found %d DAGs", len(dags.dags))

Replacement: Direct REST API calls

For simple cases, call the REST API directly using requests:

from airflow.sdk import task
import os
import requests

_HOST = os.getenv("AIRFLOW__API__BASE_URL", "https://<your-org>.astronomer.run/<deployment>/")
_TOKEN = os.getenv("DEPLOYMENT_API_TOKEN")

@task
def list_dags_via_api() -> None:
    response = requests.get(
        f"{_HOST}/api/v2/dags",
        headers={"Accept": "application/json", "Authorization": f"Bearer {_TOKEN}"},
        params={"limit": 10}
    )
    response.raise_for_status()
    print(response.json())

Ruff Airflow Migration Rules

Use Ruff's Airflow rules to detect and fix many breaking changes automatically.

  • AIR30 / AIR301 / AIR302: Removed code and imports in Airflow 3 - must be fixed.
  • AIR31 / AIR311 / AIR312: Deprecated code and imports - still work but will be removed in future versions; should be fixed.

Commands to run (via uv) against the project root:

# Auto-fix all detectable Airflow issues (safe + unsafe)
ruff check --preview --select AIR --fix --unsafe-fixes .

# Check remaining Airflow issues without fixing
ruff check --preview --select AIR .

Reference Files

For detailed code examples and migration patterns, see:


Quick Reference Tables

Key Import Changes

Airflow 2.xAirflow 3
airflow.operators.dummy_operator.DummyOperatorairflow.providers.standard.operators.empty.EmptyOperator
airflow.operators.bash.BashOperatorairflow.providers.standard.operators.bash.BashOperator
airflow.operators.python.PythonOperatorairflow.providers.standard.operators.python.PythonOperator
airflow.decorators.dagairflow.sdk.dag
airflow.decorators.taskairflow.sdk.task
airflow.datasets.Datasetairflow.sdk.Asset

Context Key Changes

Removed KeyReplacement
execution_datecontext["dag_run"].logical_date
tomorrow_ds / yesterday_dsUse ds with date math: macros.ds_add(ds, 1) / macros.ds_add(ds, -1)
prev_ds / next_dsprev_start_date_success or timetable API
triggering_dataset_eventstriggering_asset_events
templates_dictcontext["params"]

Asset-triggered runs: logical_date may be None; use context["dag_run"].logical_date defensively.

Cannot trigger with future logical_date: Use logical_date=None and rely on run_id instead.

Cron note: for scheduled runs using cron, logical_date semantics differ under CronTriggerTimetable (aligning logical_date with run_after). If you need Airflow 2-style cron data intervals, consider AIRFLOW__SCHEDULER__CREATE_CRON_DATA_INTERVAL=True.

Default Behavior Changes

SettingAirflow 2 DefaultAirflow 3 Default
scheduletimedelta(days=1)None
catchupTrueFalse

Callback Behavior Changes

  • on_success_callback no longer runs on skip; use on_skipped_callback if needed.
  • @teardown with TriggerRule.ALWAYS not allowed; teardowns now execute even if DAG run terminated early.

Resources


Related Skills

  • testing-dags: For testing DAGs after migration
  • debugging-dags: For troubleshooting migration issues
  • deploying-airflow: For deploying migrated DAGs to production

Plus de skills de astronomer

airflow-state-store
astronomer
Persists task and asset state across retries and DAG runs using Airflow 3.3's AIP-103 key/value stores (`task_state_store`, `asset_state_store`) and the…
creating-openlineage-extractors
astronomer
Extracteurs OpenLineage personnalisés pour les opérateurs Airflow non pris en charge et les scénarios de lignage complexes. Deux approches : ajouter des méthodes OpenLineage directement aux opérateurs que vous possédez (recommandé), ou créer des extracteurs personnalisés pour les opérateurs tiers que vous ne pouvez pas modifier. Les extracteurs interceptent l'exécution des opérateurs à trois moments : avant l'exécution pour le lignage statique, après le succès pour les sorties déterminées à l'exécution, et optionnellement après l'échec pour un lignage partiel. Enregistrez les extracteurs via airflow.cfg ou l'environnement...
debugging-dags
astronomer
Analyse systématique des causes profondes et correction des DAG Airflow défaillants, avec des flux d'investigation structurés. Guide à travers un processus de diagnostic en quatre étapes : identifier l'échec, extraire les détails de l'erreur, rassembler les informations contextuelles et fournir des étapes de correction exploitables. Classe les échecs en quatre types (données, code, infrastructure, dépendance) pour cibler l'investigation et suggérer les correctifs appropriés. Fournit des commandes CLI prêtes à l'emploi pour la récupération des logs, la comparaison des exécutions, l'effacement des tâches et des DAG...
delegating-to-otto
astronomer
Drives Astronomer's Otto agent (`astro otto`) as a delegated sub-agent for Airflow, dbt, and data-engineering work. Use when the user explicitly asks to "use…
deploying-airflow
astronomer
Déployer des DAGs et projets Airflow. Utiliser lorsque l'utilisateur souhaite déployer du code, pousser des DAGs, configurer CI/CD, déployer en production, ou demande des stratégies de déploiement…
deploying-go-sdk-bundles
astronomer
Construit, empaquette et déploie des bundles compilés du SDK Go Airflow afin que l'ExecutableCoordinator puisse les exécuter. À utiliser lorsque l'utilisateur souhaite compiler un bundle de tâches Go, demande…
testing-dags
astronomer
Cycles itératifs de test-débogage-correction pour les DAGs Airflow avec diagnostic complet des échecs. Commencez par af runs trigger-wait <dag_id> pour exécuter un DAG et attendre son achèvement ; aucune vérification préalable nécessaire. En cas d'échec, utilisez af runs diagnose pour un résumé complet des échecs et af tasks logs pour inspecter les détails des erreurs de tâches spécifiques. Prend en charge la configuration personnalisée, les délais d'attente et les tentatives de réessai ; gère les scénarios de succès, d'échec et de dépassement de délai avec une interprétation claire des réponses. Validation rapide disponible...
tracing-downstream-lineage
astronomer
Tracer la lignée des données en aval pour évaluer l'impact des modifications avant de modifier des tables ou des DAG. Identifie les consommateurs directs d'une table ou d'un DAG cible via la recherche dans le code source, les dépendances de vues et les connexions aux outils BI. Construit un arbre de dépendances complet cartographiant tous les impacts en aval, des tables aux tableaux de bord en passant par les modèles ML. Catégorise les dépendances par criticité (critique, élevée, moyenne, faible) pour prioriser la communication avec les parties prenantes et les tests. Génère un rapport d'impact avec évaluation des risques, éléments affectés...