guide · computers

Python Venv on This VPS: The Conventions

The canonical Python venv on this VPS is /opt/hermes/.venv/ with Python 3.11. Library surface today: openai 2.33.0, requests, pillow, ruff. Why we never call system python3 for agent scripts.

July 14, 2026 · By Alastair Fraser

A friendly retro robot at a sturdy lockable cabinet labeled VENV. The robot reaches in to grab labeled jars (OPENAI / REQUESTS / PILLOW / RUFF) and uses each to run a small labeled task on the workbench (image gen / HTTP call / PNG+WebP / linter). A small human figure on the right points at a PATH BAR showing /opt/hermes/.venv/bin/python.

The convention in one sentence

The canonical Python interpreter on this VPS for every Hermes script is /opt/hermes/.venv/bin/python (Python 3.11). Use that path explicitly; do not use python3, do not use system pip. The venv has openai, requests, pillow, ruff, and the related libraries the agent needs.

/opt/hermes/.venv/ is the venv’s root. /opt/hermes/.venv/bin/python is the executable. /opt/hermes/.venv/lib/python3.11/site-packages/ is where libraries live.

Why a single venv, not multiple

One venv, owned by Hermes, available to every script that runs in the agent’s name. The reasoning:

  • Deterministic dependency surface. When the agent runs /opt/hermes/.venv/bin/python script.py, the same imports resolve regardless of which script ran. No per-script venv drift.
  • One place to upgrade. pip install -U upgrades globally for the agent. If a script needs a different version, that script is the wrong place.
  • Predictable paths. Every script under /opt/data/scripts/ and /tmp/gen_*.py assumes the same venv. Adding a new venv means editing the scripts.

Multiple venvs would multiply complexity without proportional benefit. The only exception would be a script that needs a different Python version (3.12 vs 3.11); we don’t have that yet.

What the venv contains today

Five libraries that get regular use; the rest is incidental:

  • openai 2.33.0 — the canonical OpenAI client. Used by every image-gen script (/tmp/gen_batch*.py) and by the botsitter-review drafting path.
  • requests — for any HTTP call not covered by a higher-level library. Less used now than in 2024 because most HTTP surfaces are abstracted.
  • pillow — image manipulation. WebP encoding on every deployed PNG, PNG conversion for any unusual source.
  • ruff — linter. Run by hand before committing.
  • The Python stdlib — for everything else (json, re, os, subprocess, pathlib).

If you need a new library, pip install <lib> updates the venv. The cache location is ~/.cache/pip/.

The shebang convention

Scripts intended to be runnable directly carry the shebang:

#!/opt/hermes/.venv/bin/python3

Not #!/usr/bin/env python. The env path resolves differently across shells and produces hard-to-debug issues. The absolute path is unambiguous.

Failure shapes:

  • #!/usr/bin/env python — picks up the system Python; system lacks the libraries; runtime ImportError.
  • #!/usr/bin/python3 — picks up the system Python; same problem.
  • No shebang at all — script is only runnable via python script.py; not directly executable.

The correct convention is absolute path to the venv.

The 5-call pattern

For each of the common cases, the right call:

# 1. Run an agent script (e.g. image gen)
/opt/hermes/.venv/bin/python /tmp/gen_batch_N.py

# 2. Quick sanity test
/opt/hermes/.venv/bin/python -c "import openai; print(openai.__version__)"

# 3. Install a new library
/opt/hermes/.venv/bin/python -m pip install <lib>

# 4. Run a venv-located CLI (e.g. ruff)
/opt/hermes/.venv/bin/ruff check src/

# 5. Use the venv in a script with explicit path
python3="$(command -v /opt/hermes/.venv/bin/python || echo /opt/hermes/.venv/bin/python)"
"$python3" script.py

The fifth is the “fallback if venv missing” pattern; the command -v returns the path or empty, then the || echo provides a default. Rarely needed because the venv is always present, but useful in CI / new-environment scripts.

System Python: when it’s OK

System Python (/usr/bin/python3) is fine for scripts that need only stdlib. Examples:

  • ps aux | grep python | wc -l — bash, doesn’t invoke Python.
  • /usr/bin/python3 /usr/lib/python3/dist-tools/random_script.py — system utility; stdlib-only; nothing to import from the venv.

If a script imports openai or pillow or any third-party lib, use the venv. If a script imports only stdlib, system Python is faster and avoids venv overhead.

The verify

A one-liner that confirms the venv is set up correctly:

/opt/hermes/.venv/bin/python -c "
import sys, openai, requests, PIL, ruff
print(f'python {sys.version.split()[0]}')
print(f'openai {openai.__version__}')
print(f'requests {requests.__version__}')
print(f'pillow {PIL.__version__}')
print(f'ruff {ruff.__version__}')
"

Expected:

python 3.11.x
openai 2.33.0
requests 2.x.x
pillow 10.x.x
ruff 0.x.x

If any import fails, the venv is broken — recreate it with python3 -m venv /opt/hermes/.venv && /opt/hermes/.venv/bin/pip install -r /opt/data/requirements.txt.

What NOT to do

  • Don’t pip install against system Python; use python3 -m pip install against the venv.
  • Don’t run agent scripts via python3. The path is /opt/hermes/.venv/bin/python.
  • Don’t create a second venv for “separation of concerns.” One venv.
  • Don’t edit the venv in place. If a library is wrong, fix it in requirements.txt and reinstall.
  • Don’t use pip install --user. The venv is the target.

What this saves

The agent runs 100-300 Python invocations per day (image gen, regression tests, daily traffic cron, webhook handling). A consistent interpreter saves 50ms per invocation vs system Python’s slower startup, and zero-conflict imports. The hourly drift between scripts that share libraries is zero.

Sources

Last verified: 2026-07-14 against /opt/hermes/.venv/ on this VPS. Python 3.11.x; openai 2.33.0; pillow 10.x; ruff 0.x.x.

Sources

#python#venv#hermes#tools#operator

Submit a take

Have a different read on this? Drop a comment below — your email isn't published, and I read every one. Nothing leaves the site until I approve it.

Your email address will not be published. Required fields are marked.