If you've ever set up a self-hosted CI/CD agent on a fresh Ubuntu image and hit pip: command not found or No module named pip, your first instinct is probably to check the network. Mine was too. That instinct is wrong, and the real answer took me four attempts to actually land on.
Here's the full story, including the three dead ends, because I think the dead ends are the useful part.
The setup
A self-hosted CI agent (in my case, an Azure DevOps agent, but this applies just as much to a self-hosted GitHub Actions runner or a plain Jenkins box) running on a minimal Ubuntu image. No sudo access on the box that's deliberate, since the agent shouldn't need root just to run a pipeline. I needed to install a CLI tool that only ships via pip.
Reasonable first attempt:
- script: |
python3 -m pip install --user --quiet some-cli-tool
displayName: 'Install tool'
Result:
/usr/bin/python3: No module named pip
Fine, I thought pip module is missing, ensurepip should bootstrap it. Every tutorial says so.
Attempt 2: ensurepip
- script: |
python3 -m ensurepip --user --upgrade
python3 -m pip install --user --quiet some-cli-tool
Result:
/usr/bin/python3: No module named ensurepip
Not just pip missing ensurepip itself, the thing that's supposed to install pip, doesn't exist either. That's the first real clue something structural is going on, not just a missing package.
The actual reason (worth understanding, not just working around)
Debian and Ubuntu deliberately strip pip and ensurepip out of the base python3 package. This isn't a bug or an oversight it's a packaging policy decision. Both live in separate packages (python3-pip, python3-venv) that you're expected to install via apt. On a full desktop or dev-configured server, you've probably had these installed for so long you forgot they're not actually part of core Python.
On a minimal, no-sudo CI image, you don't have apt access at all so you can't just apt install python3-pip your way out of it.
Attempt 3: venv (also fails, same root cause)
My next thought: skip pip/ensurepip entirely, use a virtual environment instead, since venv normally bootstraps its own pip on creation.
- script: |
python3 -m venv /tmp/tool-venv
/tmp/tool-venv/bin/pip install --quiet some-cli-tool
Result: fails identically. venv's own pip-bootstrapping step depends on you guessed it the same missing ensurepip module. Same wall, different door.
Attempt 4: the actual fix get-pip.py
The real answer is a standalone bootstrapping script, maintained by the Python Packaging Authority specifically for situations like this environments where ensurepip isn't available:
- script: |
curl -sS https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py
python3 /tmp/get-pip.py --user --quiet
python3 -m pip install --user --quiet some-cli-tool
echo "##vso[task.prependpath]$HOME/.local/bin"
displayName: 'Install tool (get-pip, no sudo)'
get-pip.py doesn't depend on ensurepip at all it's a self-contained bootstrapper that installs pip directly. This worked immediately, no sudo, no apt, no venv complexity.
The one thing to double-check before you assume this is your problem
Before chasing this fix, rule out the boring explanation first: confirm your agent actually has network access to PyPI at all. A quick diagnostic step saved me from solving the wrong problem:
- script: |
curl -sS -o /dev/null -w "pypi.org: %{http_code}\n" https://pypi.org --max-time 5
If that comes back with anything other than a fast 200, you're dealing with a firewall/egress issue, not this one and no amount of get-pip.py will fix that.
The takeaway
If python3 -m pip and python3 -m ensurepip are both missing on a Debian/Ubuntu box, that's not a broken image it's the distro's actual, intentional packaging policy. apt install python3-pip is the "normal" fix, and it's simply not available to you on a locked-down, no-sudo CI agent. get-pip.py is the one workaround that doesn't need apt, doesn't need ensurepip, and doesn't need root.
Small thing, but it cost me three wrong turns before I found it hopefully this saves you those three.
Top comments (0)