Moving Apache Airflow to a newer Python version

A planned Python upgrade for Airflow schedulers, workers and images, covering supported Python versions, constraint files, image rebuilds and dependencies that lag behind. Sequence it with your Airflow version, not on its own.

On this page

Upgrading Apache Airflow to a newer Python version: when and how

Python is tied to the Airflow release you run, so the Python upgrade is part of the Airflow decision, not a separate task. If your workers, schedulers or images run on an older interpreter, or if a move to Airflow 3 forces a change, start by matching the Airflow release to a Python version from the supported range. The 3.3.2 prerequisites list Python 3.10 to 3.14, so any target in that range is supported for that release. Check the prerequisites page for the exact Airflow release you plan to install, because the list changes between releases. Airflow 2 reached end of life on April 22, 2026, so a Python change made only to keep Airflow 2 running now also carries the risk of running without fixes. Confirm your target Airflow version before you choose the Python version.

Two release changes shape the timing. Airflow 3.1 removed Python 3.9 support, so any environment still on 3.9 must move before it can take 3.1 or later. Airflow 3.2 added Python 3.14 support, which gives teams on 3.10 to 3.13 a newer interpreter option once they reach that release. Each Airflow release publishes a constraints file per Python version, and a plain pip install apache-airflow can produce an unusable installation, so install with the constraints file that matches both the Airflow version and the interpreter. Check the Airflow release notes for any other Python-related changes in the releases between your current and target versions.

The practical work is the dependency and image check. Ask your team to list every provider package, custom operator, and third-party library your DAGs import, then confirm each one supports the target interpreter. Packages that lag behind often hold the upgrade back, so a dependency that has no build for the new Python version can block the whole plan. Rebuild worker and scheduler images from scratch against the new constraints file, run your DAG import tests and a staging run of representative tasks, and keep the previous image available for rollback. Confirm with your own engineers which Python version your base image and CI runners use, because a mismatch there causes failures that look like Airflow bugs.

Which Python changes break which Airflow releases

This table compares each Python-related change in the researched Airflow releases with its effect on your environment and the action to take. Read it by release: find the version you are moving to, then check the impact column against your workers, schedulers, images and any custom code. The main takeaway is that Python 3.9 support ends in 3.1, and that Airflow 3.2 changes image builds and Python-side database code, not only the interpreter.

ChangeImpactAction needed
Python 3.9 removed in Airflow 3.1.0 (September 25, 2025)Environments still on Python 3.9 cannot run Airflow 3.1 or laterMove interpreters to 3.10 or later before upgrading past 3.0
Python 3.13 added in Airflow 3.1.0Python 3.13 becomes a supported interpreter for Airflow 3.1Test your dependencies on 3.13 if you plan to use it
Structlog logging everywhere in Airflow 3.1.0Custom logging configuration written for the older logging setup may not apply as beforeReview custom log config, handlers and log shipping, then test log output in staging
Python 3.14 added in Airflow 3.2.0 (April 7, 2026)Python 3.14 becomes a supported interpreter for Airflow 3.2Confirm that each provider and custom dependency installs on 3.14 before switching
SQLAlchemy 2.0 upgrade in Airflow 3.2.0Python code that uses the database layer directly may need changesSearch custom operators, hooks and plugins for SQLAlchemy calls and test them against 2.0
MySQL client dropped from images in Airflow 3.2Image builds that install or rely on the MySQL client may fail or lose connectivityAdd the client to your own image if you need it, and verify MySQL connections after the rebuild
Python 3.10 to 3.14 supported in Airflow 3.3.2Sets the full supported interpreter range for the current patch releasePick a target interpreter from this range and match it to the constraints file for that Python version

What to inventory and back up before changing the Python version

A Python upgrade touches the metadata database, the dependency set, the container images and the test environment at once. The cards below cover the six items to pin down before anyone changes a Python version number, so that a failed upgrade can be rolled back and diagnosed.

Metadata database backup

Take a consistent backup of the metadata database before any image or interpreter change. The production guide warns that the default SQLite backend is for testing only and can lose data in production, so confirm you are on PostgreSQL or MySQL and that restore has been rehearsed. Record the backup location, the Airflow version and the Python version it was running on so a rollback has a known starting point.

DAG code and custom operators

List every DAG file, custom operator, hook and plugin that imports a third-party package with native extensions or a pinned version. These are the components most likely to fail on a new interpreter. Mark each one with its owning team and whether it has automated tests, because untested code is where Python-version regressions surface late.

Current and target constraints files

Identify the constraints file that matches your current Airflow release and Python version, and the one for the target pair. Airflow publishes one per release and Python version because a plain pip install apache-airflow can produce an unusable installation. Diff the two files to see which pinned packages change, and keep both in version control so the build is reproducible.

Custom images and base layers

Record the base image tag, the Python interpreter it provides, and every RUN step that installs system packages or compiles dependencies in each custom image. Confirm the base image publishes the target Python version and that its OS packages still provide the libraries your dependencies link against. Changing a base layer silently changes the interpreter, so pin tags by digest where your registry allows it.

Provider packages and lagging dependencies

List each installed provider package and the Python versions it supports, then note any third-party client library that has not yet released a build for the target interpreter. Those gaps decide whether the upgrade can proceed on schedule. Ask each provider's maintainers or check their release notes before committing to a date, and keep the old image available until every gap is closed.

Staging that mirrors production

Build a staging environment with the same executor, worker image, scheduler count and database engine as production, and run a representative set of DAGs against it. Compare task logs, runtimes and resource use with production baselines. A staging environment that uses a different executor or fewer workers will not reveal the issues that matter most for this change.

How the Python move differs for workers, schedulers and images

The scheduler, DAG processor and API server all run the Airflow code and the Python interpreter that imports your DAG files, so they change first. In an Airflow 3 deployment these are separate processes, which means each one needs the new interpreter and the same installed package set. Running them on different Python versions is a common source of confusing import failures, so treat them as one release.

  • Interpreter on every control-plane host: Confirm that each scheduler, DAG processor and API server host or pod runs a Python version listed in the Airflow 3.3.2 prerequisites, which currently covers 3.10 to 3.14.
  • Installed package set: Reinstall Airflow and providers against the constraints file for the target Airflow version and Python version, not against floating versions.
  • Database compatibility: Confirm that the PostgreSQL or MySQL metadata database versions in your environment appear in the prerequisites for the target release before you restart components.
  • Parsing behavior: Parse every DAG file under the new interpreter in a staging environment and compare the import errors and DAG counts with the current production baseline.
  • Rollout order: Confirm with your own change process whether the DAG processor, scheduler and API server can be restarted in a fixed order, and what the rollback path is if a component fails to start.

Before cutover, check that the control plane starts cleanly on the new interpreter and that parsing produces no new import errors.

The upgrade sequence: staging, constraints, image rebuild and cutover

This sequence moves Airflow workers, schedulers and images to a newer Python version in an order that keeps a known-good rollback point throughout, so each change can be tested before it reaches production.

  1. Confirm the Airflow and Python pair

    Check the target Airflow release against its prerequisites page, which lists the supported Python versions, metadata database versions and Kubernetes versions. For Airflow 3.3.2, the listed Python range is 3.10 to 3.14. Confirm that your current Airflow version can move to the target release, and note any Python version the release has dropped, since Airflow 3.1 removed Python 3.9. The output is a written pair (Airflow version and Python version) signed off by the platform owner.

  2. Pull the matching constraints file

    Download the constraints file for the exact Airflow release and Python minor version from the constraints path in the Airflow repository, rather than resolving dependencies from a plain install. Store the file in your build repository and pin its version alongside the Airflow pin. The output is a constraints file checked into source control, with the Airflow and Python versions recorded in its name and in the build configuration.

  3. Rebuild the images on the new Python base

    Update the base image and the build definition to the target Python version, install Airflow and your provider packages using the constraints file, and build a new image tag for each component that runs Airflow code, including schedulers, workers, the API server and the DAG processor. Confirm that the build fails on any conflicting dependency rather than silently overriding it. The output is a set of tagged images with a build log and a dependency list that can be compared against the previous image.

  4. Test dependencies that lag behind

    Install each custom package, private library and provider your DAGs import into the new image, and run their test suites under the target Python version. Compiled extensions, pinned libraries and vendor SDKs are the usual blockers, so check each one against its own release notes before deciding to upgrade or replace it. Record any package that has no compatible release for the target version, since that item will block cutover. The output is a dependency register with a pass or blocker status for every package.

  5. Run DAG and integration tests in staging

    Deploy the new images to a staging environment that uses a copy of the production metadata database or a representative restore, then parse every DAG, run representative tasks against non-production connections and compare task outcomes with production history. Include the connection types and operators your organization depends on most. The check is complete when DAG parsing shows no import errors and the test runs match the expected results for each critical pipeline.

  6. Define the rollback point before cutover

    Before any production change, record the image tag currently running in each component and take a consistent backup of the metadata database. Store both with a note of the Airflow version, Python version and constraints file in use. Confirm that the previous image can still be pulled and that the backup can be restored in staging. The output is a rollback record that a second engineer has reviewed and that the team can execute without rebuilding anything.

  7. Roll out components in a planned order

    Stop new DAG runs or pause the schedule for the affected pipelines, then update components in a fixed order defined in the runbook, normally starting with the components that do not hold state. Deploy the new image to schedulers and workers, confirm that each component reports healthy and that task instances start running, and only then move the next component. Operators should watch for parse errors and failed task starts during the rollout. The output is a completed rollout log with the time and image tag of each component.

  8. Verify production and keep the rollback path open

    Once all components run the new image, confirm that the scheduled pipelines complete on their next runs, that logs and metrics arrive as before, and that no task fails on the Python change. Keep the previous image and backup available until the agreed monitoring window closes, and record the decision to retire them. The output is a signed cutover record that names the final Airflow and Python versions in production.

Why dependencies that lag behind block a Python upgrade

The Python upgrade is only as fast as the slowest dependency. Airflow itself installs on each supported Python version, but the libraries beneath it do not always move at the same pace. Compiled extensions and packages that ship only source distributions need a prebuilt wheel or a working local build for the new interpreter. Pinned libraries in your DAG repositories, in internal shared packages and in the images your teams build can all lack a compatible release. Before changing the base image, confirm for each target Python version that every direct and transitive dependency installs cleanly from your package mirror, including any air-gapped mirror you run.

Provider packages are the dependency most specific to Airflow. Each provider must support both the Airflow release you run and the Python version you are moving to, so an upgrade can stall because a provider you depend on has not caught up. Check the minimum Airflow version and supported Python versions for every provider you install, not only for core. Where a provider lags, your options are to hold the Python version, pin an older provider that still works, or replace the operator or hook. Each option changes the test scope, so decide which one applies before the cutover date is set.

Find these blockers early by resolving against the constraints file for the target Airflow release and Python version, then running it in CI. A resolution run that fails or produces a surprising downgrade shows a conflict before any image is built. Run your DAG test suite, provider-specific operator tests and image build on each target Python version in parallel, not just once at the end. Ask your team to record which packages were added, removed or changed in the resolved set, and to confirm with the package maintainers where a wheel is missing. Those records become the blocker list that drives the rest of the plan.

What makes a Python upgrade take longer

This table compares the factors that change how much work a Python move needs for Airflow workers, schedulers and images. Read each row against your own estate: the more a driver applies, the more images, dependencies and environments you must rebuild and retest. The main takeaway is that the Python version itself is rarely the slow part; the dependency surface and the number of places Python is pinned usually are.

DriverWhy it changes effortHow to keep it down
Custom images and workersEach image built on the Airflow base must be rebuilt, scanned and redeployed on the new Python, and each worker pool needs a rollout plan.Consolidate images onto one base and one build pipeline, and confirm which worker queues must move together.
Pinned or compiled dependenciesPackages with C extensions or pinned versions may lack wheels for the target Python, forcing source builds or version changes.Inventory requirements files, check wheel availability for the target Python, and remove unused pins.
Custom operators and hooksPlugin code and provider-dependent operators must import and run on the new interpreter and the matching Airflow and provider versions.List custom code by owner, then test imports and operator execution in a staging environment.
Size of the DAG estateMore DAGs mean more parsing and more task runs to validate, and a wider set of libraries that DAG code imports.Group DAGs by the libraries they use and run a representative subset first, with parse checks on all of them.
Number of environments and air-gapped mirrorsEach environment needs its own image and constraint set, and air-gapped networks need the new packages mirrored before deployment.Confirm which environments move first and ask whether your package mirror already holds wheels for the target Python.
Combined Python and Airflow major version changeMoving Python together with an Airflow major upgrade doubles the variables, since the Airflow release also changes APIs, database migration and DAG code.Ask for the two changes to be sequenced in separate stages, with the Python move tested on the current Airflow version first where the release supports it.

Questions buyers ask before moving Airflow to a newer Python

What does a Python upgrade for Airflow cost, and what drives the quote?

A quote follows from a written scope, so the cost depends on what the engagement covers: how many schedulers, workers, API server and DAG processor images need rebuilding, how many DAG repositories and custom operators need dependency testing, and whether the work includes staging validation and a rollback plan. DeployOpen does not publish a price list. Ask for the scope items, the deliverables and the assumptions behind each line, and ask which costs are one-time (the migration and testing) and which recur (any retainer for ongoing support or upgrades). Ask your team how many environments and images are in scope, because each adds build and test work.

How long does a Python version upgrade take for Airflow?

Duration depends on the factors that set the work: the target Python version, the Airflow release you run, how many images and environments exist, how many dependencies lack wheels for the new Python, and how much DAG code has custom dependencies to test. Airflow 3.3.2 lists Python 3.10 to 3.14 as supported prerequisites, so a move within that range has a defined target, but the test effort is driven by your dependency list. Ask for a schedule that separates inventory, image rebuild, staging test and production cutover, with each phase tied to a written deliverable.

What does our team need to provide for a Python upgrade?

The client team typically provides access to the repositories that build the Airflow images, the constraint and requirements files in use, a staging environment that mirrors production, a list of DAG owners who can confirm expected behavior, and access to the metadata database for a backup before any change. You also need someone who can approve the change window and the rollback approach. Confirm these inputs during scoping, because missing access or unclear DAG ownership is a common source of delay.

Can our internal team upgrade Python for Airflow without a partner?

Yes, many teams do this in-house, provided they have people who can rebuild images, read dependency resolution output and test DAGs in staging. The work is mostly disciplined testing rather than a single command. The main risks are installing Airflow without the matching constraints file, which Airflow's installation documentation warns can produce an unusable installation, and skipping tests for dependencies that lag behind the new Python. Decide based on who owns the images, whether anyone has done a Python upgrade on a scheduler, and how much staging capacity exists. A partner is useful when the team is short of time or experience, and the written scope makes the split of work explicit.

Should we upgrade Python and Airflow in the same change?

Separating the two changes is usually safer because each one can fail in different ways. A Python change affects the interpreter, compiled dependencies and images, while an Airflow change affects the scheduler, API server, DAG processing and removed features. If both change together, it becomes harder to tell which change caused a failure. If your Airflow version is moving to 3.x at the same time, check the upgrade guide first: the upgrade guide requires being on Airflow 2.7 or later and recommends moving to the latest 2.x first. Pair the two only when the combined test plan covers both sets of risks.

What happens if a dependency has no wheel for the target Python version?

The package may need to build from source, which requires compilers, system libraries and sometimes a newer version of the package, or it may be blocked until its maintainer publishes support. Your options are to upgrade the dependency to a release that supports the target Python, replace it, vendor a patched build, or delay the Python upgrade until the dependency is ready. Find these cases early by resolving your requirements against the target constraints file and reviewing the build output. Each blocked package should have a named owner and a decision recorded before the image rebuild starts.

Which Python versions does Airflow support, and does a newer version drop older ones?

The Airflow 3.3.2 prerequisites list Python 3.10 to 3.14 as supported. Airflow 3.1.0, released on September 25, 2025, added Python 3.13 and removed Python 3.9 support, and Airflow 3.2.0, released on April 7, 2026, added Python 3.14 support. Confirm the exact support matrix for your Airflow release in the prerequisites page before you pick a target, because a version that works on one Airflow line may not be listed on another.

Where the authoritative version and constraint details live

Before you pick a target Python version, confirm the supported range in the official Airflow prerequisites page for the exact Airflow release you plan to run. The prerequisites list the Python versions, database engines and Kubernetes versions that release supports. Use the version you are moving to, not the one in your current environment, and record the date you checked. Supported ranges change between releases, so a value copied from an older internal runbook can be wrong for the Airflow version you are deploying.

Next, read the release notes for every Airflow version between your current release and your target. They state which Python versions were added or dropped, and they flag other changes that will affect the same upgrade window, such as changes to the database layer or the API server. Check the Airflow release notes for the entries that mention Python, SQLAlchemy, container images or the Task SDK, and note which of those changes your DAGs, images or base operating system depend on. Confirm with your platform team which Python interpreter is installed in your worker and scheduler images, since that is what the release notes and prerequisites apply to.

Last, use the installation documentation on installing from PyPI to find the constraints file for your exact Airflow version and Python version. Pin your installs to that file rather than resolving the latest package versions, because a plain install of the core package can produce an unusable environment. Re-check the constraints URL during the upgrade itself, rebuild it for each Python version in your matrix, and keep the file under version control with the image build so that a later rebuild resolves the same dependency set.

How DeployOpen scopes and delivers an Airflow Python upgrade

DeployOpen plans a Python move for Airflow as a fixed-scope piece of work, starting from the Python versions your workers, schedulers and images run today. We check every dependency and DAG import against the target interpreter, then build, test and hand over the change inside infrastructure you control.

How we scope

  • Inventory every Airflow component, worker image and scheduler host with its current Python version and install method.
  • Confirm the target Python version against the Airflow prerequisites for your Airflow release before work starts.
  • Agree in writing which providers, custom operators and private packages are in and out of the Python move.

How we build

  • Rebuild container images and virtual environments using the constraints file that matches the target Airflow and Python versions.
  • Run dependency resolution and DAG import checks on the new interpreter, then fix or pin packages that lag behind.
  • Test representative DAG runs on staging workers before any production image or environment is switched over.

How we hand over

  • Document the target Python version, constraints file reference, image tags and rollback steps for each component.
  • Record every dependency that was pinned, replaced or removed, with the reason it could not move forward.
  • Walk your platform and data engineering teams through the rebuild steps so they can repeat it for future releases.

How we support

  • Offer ongoing support, upgrades and continuous testing as a retainer after the fixed-scope work is complete.
  • Track provider and dependency releases that affect the Python versions your Airflow line supports.
  • Confirm the support terms and response expectations in the written agreement before the retainer begins.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.