End-state doc describing devpi as a native LaunchAgent on indri, replacing the minikube StatefulSet. Covers layout, deploy, verify, and version bumps. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2.8 KiB
| title | modified | last-reviewed | tags | ||
|---|---|---|---|---|---|
| Devpi on Indri | 2026-04-29 | 2026-04-29 |
|
Devpi on Indri
How devpi (the PyPI caching mirror at pypi.ops.eblu.me) is deployed on indri as a launchd-managed native service. Replaces the prior minikube StatefulSet.
Why native, not Kubernetes
Devpi has no runtime dependencies beyond a Python interpreter, a writable directory, and outbound HTTPS to upstream PyPI. Running it on indri natively removes a layer of operational complexity, frees minikube resources, and decouples this critical-path tooling (used by every Python build, including mise run docs-mikado itself) from cluster health.
Layout
| Concern | Path / detail |
|---|---|
| Service binary | /Users/erichblume/devpi/venv/bin/devpi-server |
| Server-dir (data) | /Users/erichblume/devpi/server-dir/ |
| Logs | /Users/erichblume/Library/Logs/mcquack.devpi.{out,err}.log |
| LaunchAgent label | mcquack.eblume.devpi |
| LaunchAgent plist | ~/Library/LaunchAgents/mcquack.eblume.devpi.plist |
| Listen address | 127.0.0.1:3141 (loopback only) |
| Public URL | https://pypi.ops.eblu.me (via Caddy reverse proxy) |
| Root password secret | 1Password item devpi, field root password |
The venv is built fresh by ansible from a pinned devpi-server and devpi-web version; bumping versions is a config change in ansible/roles/devpi/defaults/main.yml.
Deploy
mise run provision-indri -- --tags devpi
Ansible will:
- Fetch the root password from 1Password (in playbook
pre_tasks) - Create the venv at
~/devpi/venvif absent and install/upgradedevpi-server+devpi-webto the pinned versions - Initialize the server-dir (only on first run, when
.serverversionis missing) - Render and load the LaunchAgent plist
- Restart the service if the plist or config changed
Caddy already proxies pypi.ops.eblu.me → 127.0.0.1:3141; nothing else routes traffic.
Verify
ssh indri 'launchctl list mcquack.eblume.devpi'
curl -fsS https://pypi.ops.eblu.me/+api | jq
uv pip install --index-url https://pypi.ops.eblu.me/root/pypi/+simple/ requests
Logs
ssh indri 'tail -f ~/Library/Logs/mcquack.devpi.err.log'
Bumping devpi versions
Edit devpi_server_version / devpi_web_version in ansible/roles/devpi/defaults/main.yml, then re-run the playbook with --tags devpi. The role rebuilds the venv in-place; the server-dir survives.
Backup
The server-dir is included in the borgmatic file backup that already covers ~erichblume/. Devpi indexes are recoverable from upstream PyPI on a fresh start; only the local index (root/dev) is unique state.
Related
- restart-indri — devpi is one of the LaunchAgents to stop on graceful shutdown
- connect-to-postgres — pattern for indri-native services (different stack, similar shape)