Many Python images contain two interpreters: one installed by the operating system and one built for the image. A scan reports each separately, with different packages and different fixes. The image Python is replaced by moving to a newer image tag, and the distro Python by refreshing the OS layer.
Where each one lives
The Docker official Python image documentation explains this for the non-slim variants. The image provides its own Python at /usr/local/bin/python, which is first on the path. Because the non-slim variants build on a larger base that includes development tooling, there is also a distribution provided python at /usr/bin/python or /usr/bin/python3. The docs say the distro one cannot be safely removed, since many distribution tools are written against it.
So your application runs the image Python, while the scan sees both.
How to read the paths in a finding
The file path or package location in a finding points to the owner.
| Path pattern | Installed by | Who fixes |
|---|---|---|
| /usr/local/bin/python and /usr/local/lib/pythonX.Y | The image build | Newer image tag or rebuild on a patched tag |
| /usr/lib/python3 and dist-packages under it | OS package manager | OS updates in the base layers |
| /usr/local/lib/pythonX.Y/site-packages | pip, in your build | Your requirements file |
Debian's Python policy says public Python 3 modules from the system are installed in /usr/lib/python3/dist-packages, and a separate directory under /usr/local is reserved for modules the local administrator installs. Python's own site documentation describes site-packages under the prefix, for example /usr/local/lib/pythonX.Y/site-packages on a typical Unix build.
Does the distro Python matter if my app never runs it?
It can still matter. It sits in the image, it can be run by anyone who gets a shell, and some OS tools call it. But the priority is lower than a flaw in the interpreter and packages your service actually loads. Reachability, meaning whether the vulnerable code runs in your application, is the right tiebreaker. See installed is not exploitable.
If you want the distro Python gone, the practical options are a slim variant or a multi-stage build that copies just what the app needs into a smaller final image.
Steps to sort a Python image scan
- Filter findings by path prefix: usr/local versus usr/lib.
- Mark each group with an owner: image maintainer, OS maintainer, app team.
- For the interpreter, check for a newer patch tag of the same minor version and rebuild with a fresh pull.
- For pip packages, update the pinned versions and regenerate hashes.
- Rescan the new digest and confirm both interpreters.
You can print what the image sees at run time.
docker run --rm myimage which -a python python3
docker run --rm myimage python -c "import sys; print(sys.version); print(sys.path)"
What to put in the ticket
Name the interpreter path in the ticket title, such as "Python at /usr/local/bin" or "dist-packages under /usr/lib". It saves the reader a guess about which of the two pythons is meant. Add the digest, the finding id and the fixed version, and say which owner group the path maps to.
If the finding is on the image Python, check whether the tag you build from has a newer patch release for the same minor version. If it does, bump the tag and rebuild with the pull flag. If it does not, check whether a newer minor release is acceptable for your code, and plan the upgrade with your test suite in front.
For the distro Python, the fix usually arrives with the next OS package update, so a rebuild with a fresh pull of the base tag is the lever. If the OS vendor has marked the issue as deferred or ignored, the question becomes whether you need that interpreter in the final image at all.
Quick check
Will pip install upgrade the OS Python packages?
No. Pip installs into the interpreter's own site-packages. OS packages under dist-packages are managed by the OS package manager.
Is a slim image free of the second Python?
Slim variants are smaller and do not use the same large base, but check your own image with the which command rather than assuming.
Sources
Last reviewed: 19 October 2026.