viper
Production-grade process manager for Python services on Linux servers. Start apps from the terminal, keep them alive, watch their memory, tail their logs — and never touch a process it didn't start itself.
Built for teams running Python/AI services (model servers, workers, APIs) who
are tired of tmux sessions, nohup, and 3 a.m. surprises.
viper start "uvicorn app:app --port 8000" --name api -i 2 --max-memory 2G
viper ls
viper logs api -f
Why not pm2?
pm2 is great — and written in Node.js. viper is pure Python (psutil +
asyncio), designed around what Python/AI services actually need: virtualenv
auto-detection, memory watchdogs for leaky inference processes, a daemon that
survives its own crash, and (on the roadmap) readiness probes for
slow-loading models and GPU awareness.
Install
# apt (Debian/Ubuntu servers) — one-shot, installs python3 + all deps automatically:
curl -fsSL https://raw.githubusercontent.com/ramakrishnan2808/viper-pm/main/install.sh | sudo sh
# or pip/pipx (any Linux):
pipx install viper-pm # or: pip install viper-pm
(The installer adds the project's signed apt repository once — apt can only
install from repositories it knows, and viper-pm isn't in the official
Debian/Ubuntu archive yet. After that, updates arrive via normal apt upgrade.)
From source:
git clone <repo> && cd viper && pip install -e .
Quickstart
# start anything: a command, a .py file, or a config file
viper start "uvicorn app:app --port 8000" --name api -i 2 --max-memory 1G
viper start worker.py --name worker
viper start viper.yml
viper ls # table of apps, workers, cpu, memory, uptime, restarts
viper logs api -f # follow logs (per-worker prefixes)
viper events api # audit trail: every start/exit/restart and *why*
viper reload api # rolling restart: workers restart one at a time
viper stop api # graceful stop (SIGTERM, grace period, then SIGKILL)
viper delete api # stop + remove from management
viper kill # stop everything and shut the daemon down
The daemon starts automatically on first use and runs per-user. If the daemon
itself is killed, your apps keep running — the next viper command
respawns it and it re-attaches to every live worker from its journal.
The team workflow: viper apply
Keep a viper.yml in each project repo; deploys become:
git pull && viper apply viper.yml
apply converges the server to the file: new apps start, changed apps
restart with the new config, unchanged apps are left alone
(--prune also removes apps missing from the file).
apps:
- name: api
cmd: uvicorn app:app --host 0.0.0.0 --port 8000
cwd: /srv/api # relative paths resolve against this file
venv: auto # finds .venv/ or venv/ in cwd (or give a path)
workers: 2 # each worker gets VIPER_WORKER_ID=0,1,...
max_memory: 2G # restart a worker whose process tree exceeds this
env_file: .env # loaded fresh at every (re)start; env: below wins
env:
MODEL_PATH: /models/base
stop_signal: SIGTERM
stop_grace: 30 # seconds before SIGKILL
autorestart: true
max_restarts: 10 # consecutive fast crashes before giving up
min_uptime: 10 # seconds that count as a "stable" run
- name: worker
cmd: celery -A tasks worker
cwd: /srv/pipeline
cmd supports $VAR / ${VAR} from the final environment (including
env_file), e.g. cmd: uvicorn app:app --port ${API_PORT} with API_PORT
in .env. Apps can be addressed by name or by the numeric id from
viper ls (e.g. viper restart 0).
Reboot persistence
viper startup # one-time: installs a systemd service for the daemon
That's all a server needs: after a reboot, systemd starts the viper daemon and the daemon automatically restores every app that was running, from its journal. Your apps never need individual systemd services.
viper save / viper resurrect also exist (pm2-style) for explicit
snapshots — e.g. save a known-good set before experimenting, resurrect to
return to it. viper unstartup removes the boot service.
Guarantees
- Never touches foreign processes. Every managed PID is stored with its process create-time and both are re-verified before any signal is sent — a recycled PID is never signalled. Workers run in their own process group, so signals reach the worker's own tree and nothing else.
- Daemon crashes are non-events. Workers write logs straight to files and keep running; a restarted daemon re-attaches from the journal.
- Honest restart behaviour. Exponential backoff (0.5s → 30s cap), a
circuit breaker after
max_restartsconsecutive fast crashes (stateerrored, visible inviper ls), and every restart's reason recorded inviper events.
Environment your app sees
| Variable | Meaning |
|---|---|
VIPER_APP_NAME |
the app's name |
VIPER_WORKER_ID |
worker index 0..N-1 (use it to fan out ports) |
PYTHONUNBUFFERED=1 |
set by default so logs stream live |
VIRTUAL_ENV, PATH |
pointed at the detected/configured virtualenv |
Files live under ~/.viper/ (override with VIPER_HOME): per-worker logs
in logs/, the state journal, the events audit log, and the daemon log.
Roadmap
Readiness/liveness health checks with startup_grace for slow model loads →
readiness-gated zero-downtime reload → viper monit live TUI → alert
webhooks (Slack) → Prometheus metrics → GPU awareness (CUDA_VISIBLE_DEVICES
assignment, GPU-memory watchdog) → viper startup systemd generation → apt
repo + snap. See PLAN.md for the full plan and docs/PACKAGING.md for the
apt/snap path.
Development
python3 -m venv .venv && .venv/bin/pip install -e .[dev]
.venv/bin/python -m pytest tests/
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file viper_pm-0.2.0.tar.gz.
File metadata
- Download URL: viper_pm-0.2.0.tar.gz
- Upload date:
- Size: 24.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd8a148823ffbc8c07af2c169f37c8edc8f12dc56454784c7a93ed226d05d969
|
|
| MD5 |
cf6835bcb5038d130056a42fffef1936
|
|
| BLAKE2b-256 |
11c0c5fe2d65230291c184552985e28f01598f18dfb20c88fd11105356c3c8ae
|
Provenance
The following attestation bundles were made for viper_pm-0.2.0.tar.gz:
Publisher:
release.yml on ramakrishnan2808/viper-pm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
viper_pm-0.2.0.tar.gz -
Subject digest:
fd8a148823ffbc8c07af2c169f37c8edc8f12dc56454784c7a93ed226d05d969 - Sigstore transparency entry: 2224465978
- Sigstore integration time:
-
Permalink:
ramakrishnan2808/viper-pm@3d71ef4f15d75f876bb3ccbbbdef10764cbf0464 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/ramakrishnan2808
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3d71ef4f15d75f876bb3ccbbbdef10764cbf0464 -
Trigger Event:
push
-
Statement type:
File details
Details for the file viper_pm-0.2.0-py3-none-any.whl.
File metadata
- Download URL: viper_pm-0.2.0-py3-none-any.whl
- Upload date:
- Size: 23.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7aaad4f8793b68e08dc0d0b6231cf3209a6ba882d521bf44f7ffb5004e9196c
|
|
| MD5 |
5d9782dbdefb3e42ab6bd4f3ed526098
|
|
| BLAKE2b-256 |
2756d2db3b3a71673368b3971a733f9a32162845d2f0ba8a45a8d514792303b2
|
Provenance
The following attestation bundles were made for viper_pm-0.2.0-py3-none-any.whl:
Publisher:
release.yml on ramakrishnan2808/viper-pm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
viper_pm-0.2.0-py3-none-any.whl -
Subject digest:
a7aaad4f8793b68e08dc0d0b6231cf3209a6ba882d521bf44f7ffb5004e9196c - Sigstore transparency entry: 2224466932
- Sigstore integration time:
-
Permalink:
ramakrishnan2808/viper-pm@3d71ef4f15d75f876bb3ccbbbdef10764cbf0464 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/ramakrishnan2808
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3d71ef4f15d75f876bb3ccbbbdef10764cbf0464 -
Trigger Event:
push
-
Statement type: