Migrating from supervisord
supervisord keeps your processes alive, and that’s about where it stops. The
web UI is an afterthought, the logs are flat files you tail by hand, and
finding out why something keeps restarting means SSHing in and reading
supervisord.log. RunWisp supervises the same processes and gives you per-run
history, streamed logs, and a notification when an instance flaps.
You don’t have to rewrite your config by hand. runwisp import supervisord
reads it — [include] files and all — and writes the runwisp.toml.
The one-liner
Section titled “The one-liner”Point it at your config, or pipe one in — both are first-class:
runwisp import supervisord /etc/supervisor/supervisord.conf -o runwisp.tomlcat /etc/supervisor/supervisord.conf | runwisp import supervisordrunwisp validateGiven a file, it follows any [include] directives relative to it, so a typical
conf.d/-style layout comes across in one shot. Each [program] becomes a
RunWisp service. Given:
[program:web]command=/usr/bin/gunicorn app:appdirectory=/srv/appuser=www-dataautostart=truestartsecs=10startretries=5stopsignal=INTenvironment=DJANGO_SETTINGS="prod",LOG_LEVEL=infostdout_logfile=/var/log/web.log
[program:worker]command=/srv/app/worker --idx %(process_num)snumprocs=4you get:
[services.web]run = "/usr/bin/gunicorn app:app"working_dir = "/srv/app"user = "www-data"healthy_after = "10s"restart_attempts = 5stop_signal = "SIGINT"
[services.web.env]DJANGO_SETTINGS = "prod"LOG_LEVEL = "info"
[services.worker]run = "/srv/app/worker --idx %(process_num)s"instances = 4On stderr you get an account of every program, so you know which ones came across untouched and which ones want a look:
Imported supervisord config → 2 services ~ web service /usr/bin/gunicorn app:app • log file settings were dropped — RunWisp captures stdout and stderr per run automatically (see log_max_size / keep_runs to tune). ! worker service /srv/app/worker --idx %(process_num)s ! command uses supervisord %(...)s expansions RunWisp doesn't fill in (e.g. %(ENV_x)s, %(process_num)s). Review the run line. • numprocs=4 became instances. Each instance gets a distinct RUNWISP_INSTANCE_INDEX (0-based) in place of %(process_num)s.
1 item carries a # TODO — fix it before you rely on it.
Review the TOML above, save it as runwisp.toml, then run `runwisp validate`.
! supervisord still manages these programs. Stop them there before starting RunWisp, or each one runs twice.~ means it’s imported but something changed, ! that it needs you before you
rely on it, ✓ that it mapped cleanly, and - that your runwisp.toml already
had it. Every [program] in the file gets a row either way — including the ones
that produced no TOML at all, which is the case worth seeing.
Save it and run runwisp validate. Then stop the programs in supervisord —
see nothing has retired supervisord yet
— and runwisp daemon brings them up under RunWisp, where you can watch them
from the dashboard.
If you already have a runwisp.toml
Section titled “If you already have a runwisp.toml”-o writes one flat file, which is what you want when you’re starting fresh.
When you already have a runwisp.toml of your own, use --write instead:
runwisp import supervisord /etc/supervisor/supervisord.conf --write --dry-runrunwisp import supervisord /etc/supervisor/supervisord.conf --write--dry-run first is worth the extra command: you get the same summary plus the
exact files a real run would create or change, and it changes none of them.
The imported services land in a machine-owned runwisp.d/imported.toml and your
root config is wired to load it, so nothing you hand-wrote is touched. They show
up as staged — imported, not native yet — and run exactly like any other
service in the meantime. When you’re happy with one:
runwisp promote web # move it into your runwisp.tomlrunwisp promote --all # or move all of themThe block moves verbatim, comments and # TODO notes included, and the service
keeps running while it happens. Re-running the import later skips programs you’ve
already promoted rather than tripping over the duplicate name. The
cron recipe has
the full picture of the two-tier layout, which works identically here.
Nothing has retired supervisord yet
Section titled “Nothing has retired supervisord yet”An import copies your programs into runwisp.toml. It doesn’t stop
supervisord, and it doesn’t edit its config — that’s yours. So if you start the
daemon while supervisord is still running the same programs, you get two of
every process, each with its own supervisor determined to restart it.
That’s not a subtle problem for services the way it is for cron jobs: two
gunicorn instances fight over a port, two workers double-consume a queue, and
whichever one loses the race gets restarted by its supervisor, forever. Worth
doing first, then:
sudo supervisorctl stop web worker # just the ones you importedsudo supervisorctl stop all # or everythingThen take them out of supervisord’s config for good, so a supervisorctl reload or a host reboot doesn’t quietly bring the second copy back:
sudo cp /etc/supervisor/supervisord.conf ~/supervisord.conf.backupsudo $EDITOR /etc/supervisor/supervisord.conf # or the conf.d/ filesudo supervisorctl reread && sudo supervisorctl updateOnce nothing is left in there, sudo systemctl disable --now supervisor
retires the daemon itself. Migrating a few programs at a time works fine too —
just never leave one enabled in both places.
How the pieces map
Section titled “How the pieces map”A [program] is a long-running, auto-restarted process — which is exactly a
RunWisp service. Most keys carry straight over:
| supervisord | runwisp.toml | Notes |
|---|---|---|
[program:web] |
[services.web] |
One program → one service. |
command |
run |
%(program_name)s is expanded. |
directory |
working_dir |
|
user |
user |
Needs the daemon running as root to switch users. |
umask |
umask |
|
numprocs |
instances |
Each gets a RUNWISP_INSTANCE_INDEX (0-based). |
startsecs |
healthy_after |
Uptime that marks an instance healthy. |
startretries |
restart_attempts |
Fast failures tolerated before FATAL. |
stopsignal |
stop_signal |
INT → SIGINT. |
stopwaitsecs |
graceful_stop |
Grace window before SIGKILL. |
exitcodes |
exit_codes |
Which codes count as success. |
priority |
priority |
Boot start order. |
autostart |
autostart |
Whether instances start at boot. |
environment |
[services.NAME.env] |
Quoted values are parsed correctly. |
[group:site] programs=web,… |
group = "site" on each |
RunWisp has no program groups; members are tagged. |
[include] files=conf.d/*.conf |
(followed and merged) | Resolved relative to the config file. |
What needs a human
Section titled “What needs a human”The importer flags everything it can’t map cleanly with a # TODO in the TOML
and a note on that program’s row. A key it doesn’t recognise at all is dropped —
but the row says which keys, by name, so you can decide whether any of them
mattered. The ones to know about:
autorestart. RunWisp services are always-on — they restart whenever they exit.autorestart=true(and supervisord’s default,unexpected) map to a service directly.autorestart=falseis different: that’s a run-once process, so it’s imported as a task withrun_on_startandrestart = "never"instead of a service. If you relied onunexpectedto not restart on a clean exit, setexit_codesso RunWisp knows which codes are success.%(...)sexpansions.%(program_name)sis filled in. Anything else —%(process_num)s,%(ENV_x)s,%(host_node_name)s— is left in place with a TODO. Fornumprocs, the per-instance index isRUNWISP_INSTANCE_INDEXin the environment; swap it into your command.- Log files.
stdout_logfile/stderr_logfileare dropped — RunWisp captures both streams for every run automatically, no paths to manage. Tune size and retention withlog_max_sizeandkeep_runsinstead. - Daemon sections.
[supervisord],[supervisorctl],[unix_http_server],[inet_http_server], and[rpcinterface]are supervisord’s own plumbing and have no RunWisp equivalent — RunWisp’s daemon is configured in[daemon]and served over HTTP out of the box. They’re skipped, with a note at the bottom of the summary. [eventlistener]/[fcgi-program]. Not supported — RunWisp has no event listener bus or FastCGI process manager. Nothing is generated for them, but they still get a!row: they’re not “nothing to do”, they’re “go do this another way”.
What you get that supervisord didn’t
Section titled “What you get that supervisord didn’t”- Logs you don’t have to
tail. Per-run stdout/stderr, streamed live to the Web UI and TUI and indexed for search — not a flat file you rotate by hand. - Restart visibility. Every restart is recorded with timing, so a flapping
instance is obvious instead of buried in
supervisord.log. Healthy uptime (healthy_after) resets the backoff; too many fast failures mark it FATAL. - Failure alerts. Notify on failure through Slack, Telegram, email, or a webhook.
- Start ordering.
prioritycontrols boot order, anddepends_ongates a service on another becoming healthy first — both honored at startup. - Trigger / stop from anywhere. Start and stop services from the dashboard,
TUI, or REST API; stops are graceful, honoring your
stop_signalandgraceful_stop.
With supervisord out of the picture, runwisp.toml is the single place your
processes are defined.