Skip to main content

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).

Cluster mode (pm2-style, one port, N workers)

Give the app a port: and viper creates the listening socket itself, then hands it to every worker — the kernel load-balances connections across them, and viper reload is truly zero-downtime because the port never closes:

apps:
  - name: api
    cmd: uvicorn app:app --fd ${VIPER_SOCKET_FD}   # uvicorn binds the shared socket
    port: 8000
    workers: 4
viper start "uvicorn app:app --fd \${VIPER_SOCKET_FD}" --name api -i 4 -p 8000

Works with anything that accepts an inherited socket fd (uvicorn --fd, hypercorn --fd, or socket.socket(fileno=...) in your own code). gunicorn users don't need this — gunicorn is its own master/worker cluster; run it as a single viper app (workers: 1) and size it with gunicorn -w N.

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_restarts consecutive fast crashes (state errored, visible in viper ls), and every restart's reason recorded in viper 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

viper_pm-0.3.1.tar.gz (26.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

viper_pm-0.3.1-py3-none-any.whl (24.7 kB view details)

Uploaded Python 3

File details

Details for the file viper_pm-0.3.1.tar.gz.

File metadata

  • Download URL: viper_pm-0.3.1.tar.gz
  • Upload date:
  • Size: 26.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for viper_pm-0.3.1.tar.gz
Algorithm Hash digest
SHA256 30a6f64f384e9a46f080c38e780398a87b264d5e72aafacbf96d856aa9be9c3e
MD5 9e1712daaa494e72d1a7c1491b48ce04
BLAKE2b-256 d1f6a8d2732f16f8253e9e9840747c8f3bca6c8fe039df144327d2fb2c525213

See more details on using hashes here.

Provenance

The following attestation bundles were made for viper_pm-0.3.1.tar.gz:

Publisher: release.yml on ramakrishnan2808/viper-pm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file viper_pm-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: viper_pm-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 24.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for viper_pm-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 758c66295cc2e2ffc4d7e095aec4c8c92802ddf93ad44546a5e5f86fd3804794
MD5 7fc9decadc1944dd3c20009984376744
BLAKE2b-256 1792787a42870a5d24b38f6f44caf44833011481ef13135ed423df724a24f1cd

See more details on using hashes here.

Provenance

The following attestation bundles were made for viper_pm-0.3.1-py3-none-any.whl:

Publisher: release.yml on ramakrishnan2808/viper-pm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page