All posts
supervisorsupervisordsystemdpythonprocess-managercomparisondevops

Supervisor vs Oxmgr vs systemd — The Python-World Process Manager Showdown

Supervisord has kept Python apps alive for over a decade. Here's how it compares to systemd and a modern Rust process manager on config, health checks, resource use, and reloads.

Oxmgr Team ·

Supervisor vs Oxmgr vs systemd

In the Python world, “how do I keep my app running?” has had the same answer for over a decade: Supervisor (supervisord). It’s reliable and everywhere. But it was designed in a different era, and both systemd and modern process managers have moved the baseline. Here’s an honest comparison for anyone running Python — Django, FastAPI, Flask, or Celery — in production.

The three contenders

  • Supervisord — a Python daemon that supervises processes, configured via INI files. The long-time default for Python apps.
  • systemd — the init system already running on your Linux box; it can supervise services via unit files.
  • Oxmgr — a modern process manager written in Rust, configured via a single TOML file, with health checks and an event bus built in.

Feature comparison

SupervisordsystemdOxmgr
Config formatINI filesUnit filesOne TOML file
Runtime dependencyPythonNone (built into OS)None (single binary)
Daemon memory~30–50 MB (Python)Shared with init~4 MB
HTTP health checksNo (process-alive only)No (needs helpers)Yes, built in
Health-gated reloadNoNoYes
Real-time eventsNo (poll supervisorctl)journalctl followEvent bus
Resource limitsNoYes (cgroups)Yes
Config in gitYesAwkward (scattered)Yes (one file)
Cross-platformLinux/UnixLinux onlyLinux/macOS/Windows

Where Supervisord shows its age

Supervisord still works, but three things date it:

  1. It’s a Python daemon. Ironically, the tool keeping your Python app alive is itself a Python app with its own interpreter and memory. On a small box that’s 30–50 MB of supervision overhead — and it means Supervisor’s health depends on a Python runtime.
  2. “Alive” is the only health signal. Supervisord knows whether the process exists, not whether it’s serving requests. An app that’s deadlocked but not crashed looks perfectly healthy to Supervisord. There’s no built-in HTTP health check and no readiness-gated reload.
  3. You poll it. Status comes from running supervisorctl status; there’s no push stream of lifecycle events to build alerting or dashboards on. Compare that with a real-time event bus.

None of these make it bad — they make it a 2010-era tool. If it’s running your app today, there’s no fire.

Where systemd is the right answer

systemd is genuinely great for system-level services, and it’s already installed:

  • Zero extra dependency — it’s PID 1; nothing to install.
  • cgroup resource control — real memory/CPU limits enforced by the kernel.
  • Boot integration is native — it’s literally the init system.

systemd’s weaknesses are ergonomic: config lives in scattered unit files (awkward to review as one thing in git), it has no HTTP health checks or health-gated reloads, and managing a fleet of app processes through it means a lot of .service files and systemctl incantations. It’s excellent for one service; it’s tedious for an app made of many processes you deploy constantly.

Where a modern manager fits

Oxmgr targets the application layer specifically: many related processes, deployed frequently, that you want described in one reviewable file with real health checks. The Python-relevant wins:

  • One TOML file describes your web tier, Celery workers, and beat scheduler together — versioned, reviewed in PRs.
  • HTTP health checks know whether Django is actually serving, not just whether the process exists — and gate zero-downtime reloads on it.
  • ~4 MB of overhead vs Supervisord’s Python daemon, with no interpreter dependency.
  • An event bus for real-time crash alerts and dashboards, instead of polling supervisorctl.
[processes.web]
command = "gunicorn app.wsgi -w 4 -b 127.0.0.1:8000"
restart_on_exit = true
[processes.web.health_check]
endpoint = "http://localhost:8000/healthz"
interval_secs = 30

[processes.worker]
command = "celery -A app worker --pool=gevent --concurrency=100"
restart_on_exit = true

How to choose

  • A single system service (a daemon, a database) → systemd. It’s already there and it’s the right tool.
  • A legacy Python stack already on Supervisord that’s happy → leave it, or migrate at your leisure. No emergency.
  • An app of several processes you deploy often and want health checks, one-file config, and real-time visibility → a modern process manager.

The honest summary: Supervisord walked so the current generation could run. It’s still dependable, but if you’re standing up a Python stack today — especially one with a web tier and workers and a scheduler that you deploy multiple times a week — the health checks, single-file config, and event stream of a modern manager save you the helper scripts you’d otherwise bolt onto Supervisord or systemd.