Every CPython branch stops receiving fixes five years after its first release, and from that day any vulnerability found in it stays open for good. Python 3.9 reached end of life on 31 October 2025 and Python 3.10 on 1 October 2026, yet both are still common in container base images, serverless functions, CI runners and Linux distributions. This guide gives the official Python dates and lifecycle phases, explains how platform and distribution schedules differ from upstream, and shows how to find old interpreters across hosts, containers and serverless platforms, then how to plan, test and roll back the upgrade. It is for platform, application security and vulnerability management teams. For the cross-language view see the End-of-Life Language Runtimes guide; this page goes deeper on Python only.
This is an independent guide from NHI Mgmt Group, based on the Python project's public documentation as of November 2026. It is not sponsored or reviewed by the Python Software Foundation. Release dates can change, so confirm them against the project's current status page.
Key takeaways
- Python 3.11 to 3.15 are the supported branches. As checked on 13 November 2026, 3.14 and 3.15 are in bugfix, 3.11, 3.12 and 3.13 are security-only, and 3.10 and older are end of life.
- Security-only is not the same as fully supported. In the security phase the project issues source-only releases as needed and no new binary installers, so Windows and macOS users of those branches depend on other packagers.
- Three clocks apply, not one. Upstream CPython, your serverless platform and your Linux distribution each publish separate dates; a distribution can keep patching a branch after upstream stops, while a platform can block updates on its own timetable.
- Interpreters hide in many places. Pinned base images, virtual environments, CI setup steps, bundled vendor software and old function runtimes all keep an old interpreter alive after the application code has moved on.
- Removed modules are the classic blocker.
distutils,asyncore,asynchatandimpwent in 3.12, and 19 more legacy modules such ascgi,cryptandtelnetlibwent in 3.13, so jumps from 3.9 or 3.10 often fail on imports first.
How the Python lifecycle works
CPython has released a new feature version every October since 3.9, under PEP 602 "Annual Release Cycle for Python" (status: Active, process PEP). Each branch moves through phases described in the Python Developer's Guide:
| Phase | What is accepted | Releases | Duration |
|---|---|---|---|
| Feature | New features, bug fixes and security fixes | Alphas | Until the first beta |
| Prerelease | Fixes to new features, bug and security fixes; no new features | Betas and release candidates | Until the .0 release |
| Bugfix | Bug and security fixes | Full releases with binary installers roughly every two months | Two years from release (18 months for branches before 3.13) |
| Security | Security fixes only | Source-only releases as needed; no new binaries | From the end of bugfix until five years after release |
| End-of-life | Nothing; the branch is frozen | None | Permanent |
Two consequences matter for planning. First, the support period is predictable: a branch released in October of year N ends in October of year N+5. Second, the move from 18 to 24 months of bugfix support started with 3.13, so 3.11 and 3.12 spent longer in security-only mode than newer branches will. Teams that install Python from python.org binaries on Windows or macOS should treat entry to the security phase as the practical upgrade trigger, because no further installers are built for that branch.
Official dates and current status
Dates below are from the Python Developer's Guide "Status of Python versions" page, checked on 13 November 2026. The project marks scheduled end of life dates as subject to change.
| Branch | Schedule PEP | First release | End of life | Status on 13 Nov 2026 |
|---|---|---|---|---|
| 2.7 | PEP 373 | 2010-07-03 | 2020-01-01 | EOL |
| 3.6 | PEP 494 | 2016-12-23 | 2021-12-23 | EOL |
| 3.7 | PEP 537 | 2018-06-27 | 2023-06-27 | EOL |
| 3.8 | PEP 569 | 2019-10-14 | 2024-10-07 | EOL |
| 3.9 | PEP 596 | 2020-10-05 | 2025-10-31 | EOL |
| 3.10 | PEP 619 | 2021-10-04 | 2026-10-01 | EOL |
| 3.11 | PEP 664 | 2022-10-24 | October 2027 (scheduled) | Security |
| 3.12 | PEP 693 | 2023-10-02 | October 2028 (scheduled) | Security |
| 3.13 | PEP 719 | 2024-10-07 | October 2029 (scheduled) | Security |
| 3.14 | PEP 745 | 2025-10-07 | October 2030 (scheduled) | Bugfix |
| 3.15 | PEP 790 | October 2026 | October 2031 (scheduled) | Bugfix |
| 3.16 (main) | PEP 826 | 2027-10-06 (scheduled) | October 2032 (scheduled) | Feature development |
Practical reading: anything on 3.10 or older is unsupported upstream today, and 3.11 has under a year left. A team starting an upgrade from 3.9 or 3.10 now should target 3.13 or newer rather than 3.11, to avoid a second migration within twelve months.
Platform and distribution clocks
Managed platforms and Linux distributions publish their own Python schedules. They do not change the upstream date, but they decide when you are blocked and who supplies patches. Platforms are listed alphabetically.
| Platform | Python dates (as published, checked 13 Nov 2026) | What happens at end of support |
|---|---|---|
| AWS Lambda | python3.10 deprecated 31 Oct 2026; python3.9 deprecated 15 Dec 2025; both block create 29 Jul 2027 and block update 31 Aug 2027. python3.11 deprecates 30 Jun 2027; python3.12 31 Oct 2028; python3.13 and python3.14 30 Jun 2029 | At least 180 days' email notice to the account's primary contact; on deprecation AWS may stop applying security updates; functions keep running indefinitely; rolling back to a deprecated runtime may be blocked after the update block |
| Azure Functions | Python 3.11 expected end of support October 2027, 3.12 October 2028; the Linux Consumption plan supports up to 3.12 and retires on 30 September 2028, with Flex Consumption as the documented destination | Support ends on the earlier of the community end date and the base OS end date; apps keep running but receive no security patches, may be limited in scaling, and must upgrade before Microsoft provides support |
| Google Cloud Run functions | python310 deprecated 4 Oct 2026, decommission 4 Apr 2027; python39 decommissioned 5 Apr 2026; python311 deprecates 24 Oct 2027 | Notice 90 days before deprecation; after decommission you cannot create or update functions on the runtime and existing functions may be disabled |
| Red Hat Enterprise Linux | Python 3.9 is a Full Life stream on RHEL 9 and Python 3.12 on RHEL 10, lasting as long as the RHEL release; Application Streams have their own dates, for example Python 3.11 on RHEL 8 and 9 retired May 2026, Python 3.12 on RHEL 9 retires April 2027 | Fixes come from Red Hat backports, not upstream releases, so the version string stays old while patches continue |
For Lambda migration mechanics, see the AWS Lambda Runtime Deprecation guide; for Ubuntu's handling of its bundled Python, see Ubuntu LTS End of Standard Support. The distribution case is the most common source of false positives: a scanner that flags "Python 3.9 is EOL" on a RHEL 9 host is right about upstream but wrong about patch availability. Record the supplier of fixes for each interpreter, not only its version.
Finding Python versions across hosts, containers and serverless
Combine what code declares with what actually runs. Declared versions show intent; observed versions show exposure.
- Read declarations in every repository. Collect
requires-pythoninpyproject.toml,python_requiresinsetup.pyorsetup.cfg,.python-versionfiles used by version managers, tox and nox environment lists, thepython-versioninput in CI setup steps, and everyFROM python:line in Dockerfiles. The Python Packaging User Guide notes that theProgramming Language :: Python :: 3.xclassifiers are only for search and browsing on PyPI; installers enforcerequires-python, not classifiers. - Read container image metadata. The official
pythonimages set aPYTHON_VERSIONenvironment variable, which can be read from image configuration without running the container. For custom or distroless images, generate an SBOM and look for the interpreter package. Inventory images running in each cluster, not just the registry. See the Container Image Vulnerability Management Guide and the SBOM Guide. - Watch floating tags. In the official images,
latest,3,slimandalpinepoint to the newest release (3.15 as of November 2026), so a rebuild can change the major version without a code change. The reverse also happens:python:3.9-slimkeeps rebuilding cleanly on an EOL branch for years. Pin the minor version on purpose and change it on purpose. - Look for version fingerprints in artefacts. Bytecode caches name the interpreter (
module.cpython-39.pyc), compiled wheels carry an ABI tag such ascp310, and a virtual environment'spyvenv.cfgrecords the base interpreter it was created from. These reveal old runtimes inside packaged applications and vendor bundles where no Dockerfile exists. - Query hosts. Use endpoint or configuration management inventory to report every interpreter installed (system packages, version managers, conda environments, side-by-side installs on Windows) and which one each service unit, scheduled task or cron job executes. At runtime,
sys.version_infoandplatform.python_version()give the exact version, which applications can report to telemetry. - List serverless runtimes. Export the runtime identifier of every function across all accounts, subscriptions, projects and regions: Lambda
python3.x, Cloud Run functionspython3xx, and the Python version shown in each Azure function app's configuration. Compare against the platform tables above. - Ask vendors. Appliances, data tools and agents often bundle their own interpreter. Get the version and the vendor's upgrade date in writing and record it in the asset register. See the CIS Control 2 Inventory and Control of Software Assets Guide.
Where identity fits: every old function, image and runner found here also holds credentials: an execution role, a service account, a PyPI upload token or cloud keys. Old build images are a known leak path: in the PyPI Admin GitHub Token Leak 2024, compiled .pyc files carrying an admin token sat in public images for about 15 months. When you retire an EOL image or runner, rotate what it held and move publishing to short-lived credentials. See the PyPI API Tokens and Trusted Publishing Security Guide and the NHI Discovery Across Cloud, SaaS, CI/CD and On-Premises Environments guide.
Upgrade planning and decision guidance
| Option | When it fits | Trade-off |
|---|---|---|
| Upgrade to 3.14 (EOL October 2030) | Maintained services with good test coverage and current dependencies | Largest jump from 3.9 or 3.10; longest runway; still in bugfix with binary releases |
| Upgrade to 3.13 (EOL October 2029) | Large codebases where some dependencies are not yet tested on 3.14 | Smaller change; already security-only, so no new binaries from python.org |
| Upgrade to 3.12 or 3.11 | Only where a hard dependency or platform plan blocks newer versions | Another migration due within one or two years; 3.11 ends October 2027 |
| Stay on a distribution-supported branch | Host software using the OS interpreter, such as RHEL 9 with Python 3.9 | Patched by the distribution for the OS lifetime; keep application code off the system interpreter where possible |
| Retire or isolate | Abandoned functions, unused scripts, vendor software with no upgrade path | Needs an owner decision; isolation reduces exposure but does not remove it |
Decision rule: production should run a branch with at least 12 months of upstream support remaining, or a distribution-patched interpreter with a recorded supplier. Small teams should jump straight to the newest bugfix branch to cut the number of migrations. Large organisations should upgrade shared base images, CI templates and internal libraries first, because one change there moves many services, then give teams dated targets.
Common blockers and how to handle them:
- Removed standard library modules: 3.12 removed
distutils(PEP 632),asynchat,asyncoreandimp; 3.13 completed PEP 594 by removing 19 further modules includingcgi,cgitb,crypt,nntplib,pipes,telnetlibanduu. Search the codebase and dependencies for these imports before anything else. Setuptools still provides adistutilsshim for build code that needs it. - Changed environment defaults: from 3.12, virtual environments created with
venvno longer pre-installsetuptools, so scripts that assumepkg_resourcesorsetup.pytooling is present fail. Declare build dependencies explicitly. - Compiled extensions: packages with C extensions need wheels built for the new ABI tag. Check that every compiled dependency publishes wheels for the target version, and plan replacements for abandoned ones.
- Frozen dependency trees: projects stuck on an old interpreter are usually stuck on old library versions too, so the runtime upgrade and the dependency upgrade are one project. See the Dependency Update Automation Guide and the Python Dependency Supply Chain Guide.
- Upper bounds on
requires-python: the packaging guide advises thinking twice before setting an upper bound, because it can block installs on newer interpreters for reasons unrelated to compatibility.
Testing and rollback
- Upgrade dependencies first. Move libraries to versions that support both the current and target interpreter, deploy, and only then switch the runtime. Two small changes are safer than one large one.
- Add the target version to the CI matrix. Run the full test suite on both versions with warnings enabled (
python -W error::DeprecationWarningin CI) so deprecations fail builds before they become removals. - Rebuild, do not patch in place. Build new images and function packages from a pinned base, regenerate the SBOM and rescan before deployment. Rebuild virtual environments rather than copying them, since they are tied to the interpreter that created them.
- Roll out in rings. Non-production, then a canary slice of production or one function alias, comparing error rate, latency and memory against the old version, then the rest.
- Keep a rollback artefact. Retain the previous image digest or function version for a fixed window. On Lambda, AWS warns that rolling back to a deprecated runtime may be blocked once the update block date passes, so finish before 31 August 2027 for 3.9 and 3.10 functions.
- Close the loop. Delete old images, interpreters and virtual environments after the window, and record any remaining instance as a dated exception.
Risks, detection and measurement
- Permanent known vulnerabilities: once a branch is frozen, flaws fixed in supported branches stay open in it. Internet-facing services become candidates for MITRE ATT&CK T1190 Exploit Public-Facing Application.
- Supply chain drag: libraries drop old interpreters, so fixes in dependencies become unreachable, raising exposure to T1195.001 Compromise Software Dependencies and Development Tools. See the Python Packaging and pip Security Guide.
- Platform cut-offs: a function that cannot be updated after a block date cannot even receive a configuration fix during an incident.
- Audit findings: unsupported components are an explicit control requirement (NIST SP 800-53 SA-22).
| Framework | Reference | How it applies to Python EOL |
|---|---|---|
| NIST CSF 2.0 | ID.AM-02, ID.RA-01, PR.PS-02 | Inventory interpreters, record EOL branches as vulnerabilities, replace software by risk |
| NIST SP 800-53 Rev 5 | CM-8, SA-22, SI-2, RA-5 | Component inventory, unsupported components, flaw remediation, scanning that reports interpreter versions |
| CIS Controls v8.1 | 2.2 Ensure Authorized Software is Currently Supported | EOL interpreters flagged with a documented exception or removed |
| ISO/IEC 27001:2022 Annex A | 8.8 Management of technical vulnerabilities | EOL runtimes treated as a vulnerability with an owner and deadline |
Metrics that show progress: percentage of production workloads on a branch with at least 12 months of upstream support; count of images, functions and hosts on 3.10 or older, trending to zero; functions on deprecated platform runtimes, target zero before block dates; number and age of open SA-22 exceptions, split into distribution-patched and unpatched; percentage of repositories with an explicit, supported requires-python and pinned base image.
Common failure patterns
- Scanning code, not runtimes: dependency scanners report libraries but miss the interpreter in the base image or the function runtime setting.
- Treating every "3.9" alike: a RHEL 9 system interpreter with vendor backports and a
python:3.9-slimimage with no further upstream releases need different responses. - Unwatched notification contacts: platform deprecation emails go to account primary contacts nobody reads, so the first warning is a failed deployment.
- Upgrading to the next branch only: moving 3.10 to 3.11 in late 2026 buys under a year before the next migration.
- Leaving old artefacts in registries: superseded images and function versions stay deployable and keep the credentials baked into them.
Practitioner checklist
- Record upstream EOL dates for every Python branch in use, with the date you checked the devguide status page.
- Record the platform or distribution date and the fix supplier for every interpreter found.
- Scan repositories for
requires-python,.python-version, CI Python settings andFROM python:lines. - Read
PYTHON_VERSIONor SBOM data for every running image, and pin minor versions deliberately. - Export Python runtime identifiers for every function across all accounts and regions.
- Move 3.10 and older to 3.13 or 3.14; plan 3.11 exits before October 2027.
- Search code and dependencies for modules removed in 3.12 and 3.13 before switching.
- Run CI on the target version with deprecation warnings as errors before cutover.
- Rotate credentials held by retired images, runners and functions.
- Track vendor-bundled interpreters with written vendor dates and dated SA-22 exceptions.
Standards and references
- Python Developer's Guide: Status of Python versions
- Python: PEP 602 Annual Release Cycle for Python
- Python: PEP 594 Removing dead batteries from the standard library
- Python: What's New in Python 3.12
- AWS: Lambda runtimes
- Microsoft: Azure Functions language runtime support policy
- Google Cloud: Cloud Run functions runtime support
- Red Hat: RHEL Application Streams Release Life Cycle
Related NHI Mgmt Group resources: End-of-Life Language Runtimes · Node.js End of Life Versions · Unsupported PHP versions · Python Security Guide · AWS Lambda Runtime Deprecation