Dependabot and Python 3.9: how to keep dependency updates after June 23

GitHubDependabotPythonSecurityDevOps

Migration diagram showing repositories moving from Python 3.9 to a supported version so Dependabot can keep opening update PRs

Hook

GitHub has put a date on it: on June 23, 2026, Dependabot will stop supporting Python 3.9. If a repository still depends on Python 3.9 in the Dependabot path, automated dependency update PRs may quietly stop arriving.

This is not a migration you want to leave in the “someday” bucket. The risk is subtle: production may not fail immediately, but your supply-chain hygiene gets weaker in the background.

Problem / Context

On May 19, 2026, GitHub announced the upcoming deprecation of Python 3.9 for Dependabot. The deadline is June 23, 2026. After that, repositories that still use Python 3.9 in their Dependabot setup risk losing automated dependency update PRs.

The timing matters because Python 3.9 is already EOL. The official Python Developer Guide lists Python 3.9 as unsupported, with EOL on October 31, 2025. So this is not an early warning about a future legacy runtime. It is a reminder that automation around an already unsupported runtime is also being retired.

For Dependabot, the Python ecosystem is not just one requirements.txt file. GitHub Docs cover pip, pip-compile, pyproject.toml with PEP 621, and other common manifests. Your audit should not ask only “where do we have Python?” It should ask “where is GitHub actually trying to update Python dependencies?”

Why it matters

Dependabot often acts as a quiet guardrail in a repository. It does not replace a real security program, but it does open steady PRs for small updates, CVE fixes, and compatible package versions. When that signal disappears, a team may notice only weeks later during a manual audit.

Typical outcomes look like this:

  • a package security fix ships, but no PR appears;
  • the lockfile slowly gets older;
  • CI stays green even though automation is incomplete;
  • the team assumes there are no updates, while Dependabot could not process them;
  • runtime migration keeps slipping because nobody owns it.

This affects backend services, data projects, internal tools, Django/FastAPI apps, automation scripts, and monorepos where Python is not always the main product. In those repositories, the old runtime may hide in a CI matrix, Dockerfile, or devcontainer rather than application code.

Illustration: where Python 3.9 hides

Think of a repository as a small warehouse. The sign at the door says “we are already on Python 3.12” because the production image was upgraded. But on the back shelf there is an old tox.ini, the CI matrix still includes 3.9, the devcontainer pulls python:3.9, and onboarding docs still tell people to run pyenv install 3.9.19.

Dependabot can trip over places you were not looking at. The audit needs to cover every location where the runtime influences dependency automation.

How to do it

1. Find repositories where Dependabot handles Python

Start with .github/dependabot.yml. For a single repository, open the file and inspect updates. For an organization, collect this centrally through the GitHub API or an internal script.

Look for ecosystems like this:

version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"

Also check repositories where Python dependencies live outside the root. In a monorepo, directory may point to services/api, tools/reports, infra/scripts, or another nested folder.

The first pass should produce a simple list:

  • repository;
  • package ecosystem;
  • directory;
  • manifests present there;
  • owning team or service;
  • when Dependabot last opened a PR.

2. Check every place where Python 3.9 is pinned

Next, do not stop at application manifests. Python 3.9 often lives in support files:

.github/workflows/*.yml
Dockerfile
docker-compose.yml
.devcontainer/devcontainer.json
runtime.txt
.python-version
tox.ini
noxfile.py
pyproject.toml
README.md
docs/onboarding.md

Pay special attention to CI matrices:

strategy:
  matrix:
    python-version: ["3.9", "3.10", "3.11"]

If Python 3.9 is needed only for backward compatibility tests, that is a separate decision. But if it is the primary or only version for tests, lint, packaging, or lockfile generation, it needs to move.

3. Choose the target version

Do not upgrade “somewhere newer”. Pick a supported line and record that decision in the repository. For most teams, the minimum practical move is at least Python 3.10 or 3.11, and preferably a current supported line if dependencies allow it.

A practical decision tree:

  • if the service already passes tests on 3.10 or 3.11, remove 3.9 from the matrix and make the newer version the default;
  • if tests fail on the newer version, separate package compatibility from runtime configuration;
  • if a package blocks the upgrade, create an issue with an owner and deadline instead of hiding the problem in CI;
  • if old runtime support is required for users, do not tie the repository’s whole Dependabot automation to that old runtime.

The key is not to mix “we still support old users” with “our internal dependency automation must run on the old runtime”. Those are different choices.

4. Update CI, base images, and local development

A minimal PR usually touches several layers.

In GitHub Actions:

- uses: actions/setup-python@v5
  with:
    python-version: "3.12"

In a Dockerfile:

FROM python:3.12-slim

In a devcontainer:

{
  "image": "mcr.microsoft.com/devcontainers/python:3.12"
}

Remove old Python 3.9 install commands from onboarding docs as well. Otherwise, a new teammate can bring the old runtime back through local lockfiles or scripts a month later.

5. Treat tests as migration validation, not ceremony

After the runtime bump, check more than unit tests. Python projects often need these steps:

  • create a clean virtualenv;
  • run pip install -r requirements.txt;
  • generate the lockfile with pip-compile, if used;
  • run an import smoke test for the main app;
  • start Django/FastAPI without startup failures;
  • run the job that Dependabot normally triggers after its PR.

If dependencies fail during a build step, that does not mean the whole migration is complex. Often the fix is one package update, one stale constraint, or moving to a wheel that supports the newer Python version.

6. Verify Dependabot itself

The final success criterion is not just “CI is green”. It is “Dependabot can open update PRs again”.

Check that:

  • repository insights or the security/dependency graph show no fresh Dependabot errors;
  • the latest Dependabot run does not fail during Python setup;
  • manifests are detected in the intended directory;
  • a new dependency update PR opens or the scheduled run completes without error;
  • the team owns the next runtime deadlines.

If the GitHub UI shows a Dependabot error, do not close the task. That is a green dashboard with the sensor unplugged.

What NOT to do

Do not search only for python-version: 3.9. The old runtime may be in a Docker image, devcontainer, runtime.txt, docs, or scripts.

Do not remove 3.9 from the matrix without understanding the product contract. If a library officially supports Python 3.9 users, you need a separate plan. But the repository’s dependency automation still needs verification.

Do not leave pinning without an owner. A temporary pin becomes permanent quickly when there is no issue, deadline, and responsible person.

Do not treat missing PRs as good news. With Dependabot, silence sometimes means “no updates”, and sometimes it means “automation could not run correctly”.

Do not make one huge migration PR. Upgrade the runtime, fix package compatibility, and clean old docs separately where possible. Review and rollback are much easier that way.

Conclusion

The June 23, 2026 deadline is short, but workable. In one focused work block, you can find repositories with Dependabot, check Python 3.9 across CI and tooling, move the runtime, and confirm update PRs will keep arriving.

Action plan:

  1. Collect repositories with .github/dependabot.yml.
  2. Filter Python ecosystems and manifests.
  3. Find every Python 3.9 reference in CI, Docker, devcontainer, and docs.
  4. Move the default runtime to a supported version.
  5. Run tests and verify a Dependabot run or new update PR.

This migration is not about version aesthetics. It is about keeping the automated dependency signal alive when the old runtime is already outside normal support.

Sources

Quick checklist

  • Find every `.github/dependabot.yml` that uses the Python ecosystem.
  • Check CI matrix, Dockerfile, devcontainer, and runtime docs for Python 3.9.
  • Move the target version to a supported Python line.
  • Run tests and separately check package compatibility.
  • Run or wait for Dependabot and confirm new update PRs.

Prompt Pack: audit Python 3.9 before the Dependabot deadline

You are a platform engineer preparing an organization for Dependabot dropping Python 3.9 support. Input data: - repository list or GitHub organization; - `.github/dependabot.yml` snippets; - Python manifests: `requirements.txt`, `pyproject.toml`, `setup.py`, `setup.cfg`, `Pipfile`; - CI matrix and base images; - current target Python version for services; - deadline for completing the migration. Prepare a migration plan: 1. find repositories where Dependabot updates Python dependencies; 2. identify places where Python 3.9 is still pinned; 3. classify risks as simple runtime bump, package compatibility, or CI/tooling blockers; 4. propose minimal changes in CI, Docker/devcontainer, and docs; 5. define the verification sequence that proves Dependabot can open update PRs again. Output format: repository risk list, exact files to change, PR order, rollback plan.