The subset, and nothing else
The problem. Bash gets painful past 100 lines: no real data structures, fragile error handling, no tests. Platform teams write their health checkers, cleanup jobs and report generators in Python. You already know JavaScript; this is the small part of Python those jobs need, and the one installation rule that protects the OS.
What you need to know already: bash scripts and exit codes (6.1-6.5), apt and sudo (1.11), PATH and which (1.9), from your frontend work: npm, package.json and node_modules.
Python in JavaScript terms, before anything else:
| Python | the npm world |
|---|---|
pip install X | npm install X - the package installer |
| a venv (virtual environment) | a project-local node_modules - packages for one project only |
requirements.txt with pinned versions | a lockfile (package-lock.json) |
a module / import | a module / import |
an exception (raise, try/except) | throw, try/catch |
You already program. Python for platform work is vocabulary: a CLI that parses arguments, logs properly, calls an HTTP API with timeouts, runs a command, reads some files, exits with a meaningful code, and has tests. That is the whole list:
venv, pip, requirements.txt / pyproject.toml environments
argparse (or click) CLIs
requests + Session + timeouts + retry adapters HTTP
subprocess.run([...], check=True) running commands
pathlib.Path files
dataclasses, json, type hints data
logging output that survives being unattended
custom exceptions + exit codes failure
pytest + fixtures tests
kubernetes client the SDK you will use most (lesson 21.34)
(An SDK is a library for talking to one system's API from code.)
Not: Django, Flask, pandas, notebooks, asyncio frameworks. None of it is the job.
About this box (simulator). Real Ubuntu has CPython. This trainer runs a restricted interpreter for exactly this subset - the standard modules above plus requests, pytest, click, kubernetes and cloud-SDK stubs from a small offline package index. Anything outside the subset fails loudly with a (simulator) message rather than pretending to work. Everything you type here is real Python and works the same on the VM.
System Python is not yours
$ python3 --version
Python 3.14.4
$ pip install requests
Command 'pip' not found, but can be installed with:
sudo apt install python3-pip
$ sudo apt install -y python3-pip && pip install requests
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
...
If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
That is PEP 668 (a PEP = Python Enhancement Proposal, a numbered design document; 668 is the "externally managed environment" rule), on by default since Ubuntu 23.04. /usr/lib/python3 belongs to apt: tools like unattended-upgrades and cloud-init are Python programs that depend on exact versions of those packages. A sudo pip install that upgrades one of them breaks the OS in ways that show up weeks later. --break-system-packages exists; the name is the documentation.
venv
$ mkdir -p ~/oncall-lab/labs/4a-runtime/python && cd ~/oncall-lab/labs/4a-runtime/python
$ python3 -m venv .venv # -m = run a module as a program; this one creates .venv/
The virtual environment was not created successfully because ensurepip is not
available. On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.
apt install python3.14-venv
...
$ sudo apt install -y python3-venv
$ python3 -m venv .venv
$ ls .venv
bin include lib lib64 pyvenv.cfg
$ source .venv/bin/activate
# the prompt now starts with (.venv)
$ which python pip
/home/learner/oncall-lab/labs/4a-runtime/python/.venv/bin/python
/home/learner/oncall-lab/labs/4a-runtime/python/.venv/bin/pip
$ pip install requests pytest
...
Successfully installed certifi-2026.8.14 charset-normalizer-3.4.3 idna-3.10 ... requests-2.34.2 urllib3-2.5.0
$ deactivate
- A venv is a directory: its own
bin/python(a link to the system interpreter) and its ownsite-packages. Nothing global changes. source .venv/bin/activateonly prepends.venv/bintoPATH. Scripts, systemd units and cron jobs do not activate anything - they call the venv's interpreter by path:ExecStart=/opt/tool/.venv/bin/python /opt/tool/check.py. A shebang of#!/usr/bin/env python3picks whateverpython3is first on PATH - inside an activated venv that is the venv's.- Never commit the venv. Recreate it from a lock of what you installed.
Pinning
(.venv) $ pip freeze > requirements.txt
(.venv) $ cat requirements.txt
certifi==2026.8.14
charset-normalizer==3.4.3
idna==3.10
requests==2.34.2
urllib3==2.5.0
(.venv) $ pip install -r requirements.txt # on the next machine, the same versions
pip freeze prints every installed package with its exact version; pip install -r FILE installs from such a list. requirements.txt with exact versions is the simple lock. A pyproject.toml (Python's package.json) declares the project and its (looser) dependencies; tools like pip-tools, uv or poetry produce locks from it. For an ops tool, a venv plus a pinned requirements.txt is enough.
pipx for tools you just want to run
# on the real VM (pipx is not simulated here)
sudo apt install -y pipx
pipx install httpie # its own venv, its command on your PATH
What you can now do
- Explain PEP 668 and why
sudo pip installis off-limits. - Create a venv, install into it, pin with
pip freeze, and point a systemd unit at the venv's python.