Skip to main content

Supervice

License: Apache 2.0 Python 3.10+ Code style: ruff Type checked: mypy Documentation

A modern, lightweight, and fully async process supervisor for Unix-like systems. Zero dependencies. Pure Python. Production-ready.

Supervice manages long-running processes with automatic restart, health checking, process grouping, hot config reload, and a Unix socket RPC interface — all built on Python's asyncio with no external packages.


Features

  • Async-first — Built entirely on asyncio for efficient I/O multiplexing
  • Zero dependencies — Pure Python stdlib; nothing to install beyond Python 3.10+
  • Process groups — Organize related processes and control them as a unit
  • Health checks — TCP connectivity and script-based health monitoring with auto-restart
  • Hot reload — Add/remove programs without restarting the daemon (supervicectl reload)
  • Daemon mode — Proper double-fork daemonization with PID file locking
  • Graceful shutdown — SIGTERM/SIGINT triggers orderly stop of all child processes
  • Process group kill — Stops entire process trees, not just the main PID
  • Log rotation — Built-in RotatingFileHandler with configurable size and backup count
  • Uptime tracking — Per-process wall-clock uptime displayed in status output
  • Retry with backoff — Configurable start retries with automatic FATAL state on exhaustion
  • User switching — Run processes as a specific user (requires root)
  • Type-safe — Fully type-hinted, passes mypy --strict

Installation

Requirements: Python 3.10+ on a Unix-like OS (Linux, macOS).

# From source
git clone https://github.com/yourusername/supervice.git
cd supervice
pip install .

# Development install (includes docs dependencies)
pip install -e ".[docs]"

Quick Start

1. Create a configuration file

# supervisord.conf
[supervice]
loglevel = INFO
logfile = supervice.log
pidfile = supervice.pid

[program:webapp]
command = python3 -u app.py
autostart = true
autorestart = true
stdout_logfile = webapp.log
stderr_logfile = webapp_err.log

[program:worker]
command = python3 -u worker.py
numprocs = 4
autostart = true
autorestart = true
stdout_logfile = worker_%(process_num)s.log
stderr_logfile = worker_err_%(process_num)s.log

2. Start the daemon

# Foreground (development)
supervice -c supervisord.conf -n

# Background (production — default)
supervice -c supervisord.conf

3. Control processes

# Check status
supervicectl status

# Output:
# NAME                 STATE      PID        UPTIME
# --------------------------------------------------------
# webapp               RUNNING    12345      1:23:45
# worker:00            RUNNING    12346      1:23:44
# worker:01            RUNNING    12347      1:23:44
# worker:02            RUNNING    12348      1:23:44
# worker:03            RUNNING    12349      1:23:44

# Start / stop / restart individual processes
supervicectl stop worker:00
supervicectl start worker:00
supervicectl restart worker:00
supervicectl restart worker:00 --force   # SIGKILL instead of graceful

# Group operations
supervicectl stopgroup workers
supervicectl startgroup workers

# Hot reload (add/remove programs without restart)
supervicectl reload

# Use a custom socket path
supervicectl -s /var/run/supervice.sock status

Configuration Reference

[supervice] — Global settings

Option Default Description
logfile (stdout) Daemon log file; empty logs to stdout in foreground, supervice.log when daemonized
loglevel INFO Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL
pidfile supervice.pid Path to the PID/lock file; set to none (or empty) to disable
socket (runtime dir) RPC socket; defaults to $XDG_RUNTIME_DIR/supervice.sock (root: /run/supervice.sock, else ~/.supervice.sock)
shutdown_timeout 30 Seconds to wait for graceful shutdown
log_maxbytes 52428800 Max log file size before rotation (bytes, 0 = no rotation)
log_backups 10 Number of rotated log backup files to keep

[program:NAME] — Process definitions

Option Default Description
command (required) Command to execute (supports shell-style quoting)
numprocs 1 Number of instances to run (creates NAME:00, NAME:01, ...)
autostart true Start automatically when daemon starts
autorestart true Restart automatically when process exits
startsecs 1 Seconds a process must run to be considered successfully started
startretries 3 Max consecutive start attempts before entering FATAL state
stopsignal TERM Signal to send when stopping (TERM, INT, QUIT, KILL, etc.)
stopwaitsecs 10 Seconds to wait after stop signal before sending SIGKILL
stdout_logfile (none) File for stdout (rotated by the daemon; supports %(process_num)s)
stderr_logfile (none) File for stderr (rotated by the daemon; supports %(process_num)s)
stdout_logfile_maxbytes / stderr_logfile_maxbytes 50MB Child log rotation threshold (0 disables)
stdout_logfile_backups / stderr_logfile_backups 10 Rotated child log backups to keep
pdeathsig true Linux/FreeBSD: SIGKILL the child if the supervisor dies
environment (none) Environment variables: KEY=VAL,KEY2="val with,comma"
env_file (none) Comma-separated KEY=VALUE secrets files (# comments, quotes stripped); read as the supervisor before the privilege drop. Later files win; environment overrides env_file
directory (none) Working directory for the process
user (none) Run as this user (requires root privileges)

[group:NAME] — Process groups

Option Default Description
programs (required) Comma-separated list of program names

Health check options (per program)

Option Default Description
healthcheck_type none Health check type: none, tcp, script
healthcheck_interval 30 Seconds between health checks
healthcheck_timeout 10 Seconds to wait for health check response
healthcheck_retries 3 Consecutive failures before marking unhealthy
healthcheck_start_period 10 Seconds to wait before starting health checks
healthcheck_port (none) TCP port to check (required for tcp type)
healthcheck_host 127.0.0.1 TCP host to check
healthcheck_command (none) Script to run (required for script type)

Example with health checks:

[program:api]
command = python3 -u api_server.py
autostart = true
autorestart = true
healthcheck_type = tcp
healthcheck_port = 8080
healthcheck_interval = 15
healthcheck_retries = 3
healthcheck_start_period = 5

Process States

STOPPED ──┐
EXITED  ──┼──> STARTING ──> RUNNING ──> STOPPING ──> STOPPED
FATAL   ──┤                    │                        │
BACKOFF ──┘                    │                     EXITED
                               ▼
                           UNHEALTHY (health check failures)
                               │
                               ▼
                         auto-restart (if autorestart=true)
State Description
STOPPED Process is not running (initial or manually stopped)
STARTING Process has been spawned, waiting for confirmation
RUNNING Process is running and healthy
BACKOFF Process exited too quickly, waiting before retry
STOPPING Stop signal sent, waiting for process to exit
EXITED Process has exited (normally or abnormally)
FATAL Process failed to start after exhausting retries
UNHEALTHY Process is running but health checks are failing

Architecture

┌─────────────────────────────────────────────────┐
│                   supervice                      │
│                                                  │
│  ┌──────────┐    ┌───────────┐    ┌──────────┐  │
│  │  Config   │───▶│ Supervisor │───▶│ Process  │  │
│  │  Parser   │    │   (core)   │    │ Manager  │  │
│  └──────────┘    └─────┬─────┘    └────┬─────┘  │
│                        │               │         │
│                   ┌────▼────┐    ┌─────▼─────┐  │
│                   │   RPC    │    │  EventBus  │  │
│                   │  Server  │    │  (pub/sub) │  │
│                   └────┬────┘    └───────────┘  │
│                        │                         │
└────────────────────────┼─────────────────────────┘
                         │ Unix Socket
                    ┌────▼────┐
                    │supervice│
                    │  ctl    │
                    └─────────┘

Platform Support

Platform Status
Linux First-class — full feature set, including pdeathsig via prctl(2)
FreeBSD Supported (15.x, 13.x) — all features, including pdeathsig via procctl(2); see the FreeBSD notes below
macOS Supported for supervision, without pdeathsig — no kernel equivalent exists; supervice logs a warning if you request it

Field notes from the first production FreeBSD deployment live in PORTABILITY-FREEBSD.md.

FreeBSD: rc.d integration

Run the supervisor itself as root from rc.d with no ${name}_user and no daemon -u — FreeBSD's rc.subr wraps the whole command in su -m when ${name}_user is set, so combining it with daemon -u <user> runs setuid twice and fails with EPERM (and daemon -f swallows the error). Let supervice drop privileges per program with its user = directive instead.

A worked /usr/local/etc/rc.d/supervice unit (adapted from production):

#!/bin/sh
# PROVIDE: supervice
# REQUIRE: LOGIN
# KEYWORD: shutdown

. /etc/rc.subr

name="supervice"
rcvar="supervice_enable"

load_rc_config $name

command="/usr/local/bin/supervice"
command_args="-c /usr/local/etc/supervice.ini"

run_rc_command "$1"
; /usr/local/etc/supervice.ini
[supervice]
logfile = /var/log/supervice/supervice.log
; daemon(8) is NOT used with -p here; let supervice own its pidfile.
; If an outer supervisor already owns it, use: pidfile = none
pidfile  = /var/run/supervice/supervice.pid
socket   = /var/run/supervice/supervice.sock

[program:api]
command = /usr/local/bin/myapp
directory = /usr/local/myapp
user = myapp
startsecs = 3
startretries = 3
stopsignal = TERM
stopwaitsecs = 10
healthcheck_type = tcp
healthcheck_port = 8080

Notes for FreeBSD operators:

  • If something else (e.g. daemon(8) -p) already writes the pidfile, set pidfile = nonedaemon(8) writes it as root before dropping privileges, so a second writer fails with EPERM/EACCES.
  • If you run supervice under daemon(8), start it with -r (restart on death) and -P (pidfile holds daemon's own pid, not the child's): pointing the pidfile at the child means service stop kills supervice and daemon immediately restarts it.
  • The pidfile and socket parent directories must exist and be writable before supervice starts; config load now fails with a clear message otherwise.
  • Export HOME if your program reads client certificates from $HOME/.postgresql or similar (asyncpg does; a wrapper script can set it).

macOS

macOS has no kernel pdeathsig equivalent. pdeathsig = true is accepted but inactive, and supervice logs a warning at config load. If orphan reaping is essential there, a launcher can reap before exec'ing the supervisor — anything alive at start is by definition an orphan:

pkill -u myuser -f 'myapp' || true
exec supervice -c /etc/supervice.ini -n

Documentation

Full documentation is available at supervice.readthedocs.io.

Development

# Run tests
python3 -m pytest tests/ -v

# Type checking (strict mode)
mypy --strict supervice/

# Linting
ruff check supervice/

# Formatting
ruff format supervice/

# Build documentation locally
pip install -e ".[docs]"
cd docs && make html

License

Apache License 2.0 — see LICENSE for details.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

supervice-0.3.0.tar.gz (56.0 kB view details)

Uploaded Source

Built Distribution

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

supervice-0.3.0-py3-none-any.whl (39.2 kB view details)

Uploaded Python 3

File details

Details for the file supervice-0.3.0.tar.gz.

File metadata

  • Download URL: supervice-0.3.0.tar.gz
  • Upload date:
  • Size: 56.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.3

File hashes

Hashes for supervice-0.3.0.tar.gz
Algorithm Hash digest
SHA256 53fffd7b85c09f8c310b44bca1e7b1ca9c943a56a4a768c2c0ffec097fd825e5
MD5 8d6a28aff7de41207afb394c027be070
BLAKE2b-256 b961c4b85242092d1fd060a62e900dcc05b30d65cdd64c87502bcab1b6fc2bab

See more details on using hashes here.

File details

Details for the file supervice-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: supervice-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 39.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.3

File hashes

Hashes for supervice-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 71da909defd24d96110853664d89b7c205c97ec3c30642c908353c0863fceb72
MD5 94aeeb1af5bd034a15af3bb3867fac19
BLAKE2b-256 625dcf970f16411f73874b0d8fa0087dd0ca1abb064cd9df66bbd6a446c41e4c

See more details on using hashes here.

Supported by

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